Created a PowerShell module, it has a function and exposes a cmdlet for that function. the built-in PowerShell 5.1 and pwsh.exe 7.3.1 (Installed using MSI installer) can detect and run the cmdlet without problem.
now I need that cmdlet to "run whether the user is logged on or not" in Windows task scheduler.
the problem arises when I try to run my PowerShell module's cmdlet as NT AUTHORITY\SYSTEM
.
Which I need to do because in task scheduler, that appears to be the only way to get scheduled task "run whether the user is logged on or not". (I don't want to manually enter username or password of any Windows user account)
Ideally, I'd rather use built in administrators security group but as you can see then i won't be able to run the task if the user is not logged on.
so I'm really stuck here not sure what to do. I assume this is one of the edge cases I'm encountering.
I need to find a way so that when PowerShell is running as SYSTEM, it will still be able to detect my module's cmdlet.
I know my cmdlet isn't detected when PowerShell is running as SYSTEM because I tested it with PsExec64.
I put my PowerShell module in here (that's where they get installed by default from online galleries):
C:\Users\<UserName>\OneDrive\Documents\PowerShell\Modules\ModuleFolder
This is the entire block of the PowerShell script I use to create my task.
$action = New-ScheduledTaskAction -Execute "pwsh.exe" -Argument "-command 'myCmdLet -parameter1 $variable1"
# First thing I tried
$TaskPrincipal = New-ScheduledTaskPrincipal -GroupId "BUILTIN\Administrators" -RunLevel Highest
# Second thing I tried
$TaskPrincipal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount -RunLevel Highest
$trigger = New-ScheduledTaskTrigger -AtStartup
Register-ScheduledTask -Action $action -Trigger $trigger -Principal $TaskPrincipal -TaskName "Name" -Description "Description"
$TaskSettings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -Compatibility Win8 -StartWhenAvailable
Set-ScheduledTask -TaskName "Name" -Settings $TaskSettings
UPDATE:
I found a way:
$TaskPrincipal = New-ScheduledTaskPrincipal -LogonType S4U -UserId $env:USERNAME -RunLevel Highest
which runs the task as administrator (since the module cmdlet won't even work without administrator privileges) and also checks these 2 boxes which I really wanted to do. however, still looking for a way to make my module's cmdlet known to PowerShell when it runs as SYSTEM, it will provide 1 benefit, AFAIK, which is to remove the dependency on a specific user account existing on the computer.
To summarize:
Your problem was that the module you want your scheduled task to use was installed in the scope of the current user, which a task running as a different user, such as
NT AUTORITY\SYSTEM
, would not see.The simples solution is to install the module for all users, via the
-Scope AllUsers
argument passed toInstall-Module
(requires elevation).However, there is a solution even if that is not possible or desired, namely to pass the full module path to an explicit
Import-Module
call performed in the context of the task, as shown below.Since your task needs to run with elevation, as you state, running in the context of the current user is only an option if that user is an administrator; as you have found, you can set that up as follows (requires elevation):
Running a task as user
NT AUTORITY\SYSTEM
invariably runs:-LogonType Service
.C:\Windows\System32
as the working director by default<hostname>$
reflected in$env:USERNAME
, where<hostname>
is the name of the local machine, and a$HOME
/$env:USERPROFILE
folder ofC:\Windows\system32\config\systemprofile
.The following self-contained example:
NT AUTHORITY\SYSTEM
~/_test.psm1
and~/_test.txt
, and the temporary task is named_Test
)Sample output:
The last 4 lines are the module function's (
Get-Foo
's) output, proving that the module was successfully imported in the context of the task.