Windows scheduled task unable to write to event log at startup - windows

I cannot get a log entry in to system or application logs if the below task runs as-is, at Startup (upon reboot and inspecting the logs) it will complete with success and write nothing to the application event log. If I run the task manually it writes to the application log just fine.
Powershell 5.1 on Windows 2019 Server:
# objective: log to windows event log at startup without requiring a login
$command = '-ExecutionPolicy Bypass -NonInteractive -NoProfile -Command { Write-EventLog -LogName Application -Source "Application" -EventID 1 -Message "important system startup message" }'
$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument "-Command {$command}"
$trigger = New-ScheduledTaskTrigger -AtStartup
$settings = New-ScheduledTaskSettingsSet -Compatibility Win8 -MultipleInstances IgnoreNew -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
$taskName = "Helpful Task Name"
$description = "Task does helpful things"
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Settings $settings -Description $description -User "NT AUTHORITY\SYSTEM" -RunLevel Highest -Force
UPDATE: As #Bender The Greatest pointed out, this has to do with invoking powershell from comspec, and the work here is about syntax reconciliation with escapes and quotes being the main offenders. Since there was no ready answer from the comments below I wanted to show the method I used with fixed code that includes an escape pattern for pushing local construction-time variables into the job data
$script = "Write-EventLog -LogName Application -Source Application -EventID 1 -Message `"[$($env:COMPUTERNAME)\$($env:USERNAME)] important system startup message`""
$command = $script.replace('"','\"')
$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument "-ExecutionPolicy Bypass -NonInteractive -NoProfile -Command ""$command"""
$trigger = New-ScheduledTaskTrigger -AtStartup
$settings = New-ScheduledTaskSettingsSet -Compatibility Win8 -MultipleInstances IgnoreNew -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
$taskName = "Helpful Task Name"
$description = "Task does helpful things"
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Settings $settings -Description $description -User "NT AUTHORITY\SYSTEM" -RunLevel Highest -Force

Modify your $action definition like so:
$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument "-Command ""$command"""
You cannot provide a ScriptBlock as an argument to powershell -Command unless you are invoking the powershell binary from within PowerShell itself. Instead, use a string as I've done above by using two double quotes (") for a string argument instead of the curly-braces ({}) you wrapped around $command.
The reason external programs can't pass in a ScriptBlock is because outside of PowerShell, a ScriptBlock doesn't exist. When Task Scheduler executes this, it will parse this as a string, then PowerShell will process that parsed string. In this case, it will literally define the ScriptBlock and exit without running it because the curly braces get rendered in this case, which syntactically defines a ScriptBlock in PowerShell. You could theoretically prefix the ScriptBlock with the call-operator (&) and it should execute, but there's little point since you can simply use a string literal instead.
The ScriptBlock works inside PowerShell because inside PowerShell, it can do its own magic to ensure certain types are converted to strings as it sees fit to before execution. Instead of becoming a literal {$command} it will first render as a literal string without the brackets before the process fires off. The order of operations matters here and is why you can't use ScriptBlocks for -Command outside of PowerShell.
Keep in mind: as written your task will not work as you expect. However, once you fix the above problem, especially since you have the transcripting set up as per the question comments, this should give you the tools necessary to work through the remaining issues with your Scheduled Task definition.

Related

Create scheduled task w/ Admin access from PowerShell?

I have a script that sets up the (AWS) instance (joins domain, hardening etc).
Part of it (load GPO backups, create OUs and users) needs to run after a reboot, so I figured I create a scheduled task for it.
But I can't get it to get the access it needs!
$psexe = Join-Path $PSHOME powershell.exe
$T = New-ScheduledTaskTrigger -AtStartup
$P = New-ScheduledTaskPrincipal -UserId "NT AUTHORITY\SYSTEM" -RunLevel Highest -LogonType ServiceAccount
$S = New-ScheduledTaskSettingsSet
$A = New-ScheduledTaskAction -Execute $psexe -Argument "-NoProfile -NonInteractive -NoLogo -ExecutionPolicy Unrestricted -File C:\Windows\Temp\install_gpo.ps1"
$D = New-ScheduledTask -Action $A -Principal $P -Trigger $T -Settings $S
Register-ScheduledTask -TaskName "Install GPO" -InputObject $D -ErrorAction Ignore
The script itself is fairly simple:
Start-Transcript -path C:\log-install_gpo.txt
Import-GPO -BackupGpoName LogonBanner -TargetName LogonBanner -Path C:\Windows\Temp\GpoBackups -CreateIfNeeded
gpupdate
Stop-Transcript
Running the task (!) manually from the task scheduler GUI and looking at the log file, I see:
Username: DOMAIN\SYSTEM
RunAs User: DOMAIN\SYSTEM
[...]
Import-GPO : Access is denied. (Exception from HRESULT: 0x80070005
(E_ACCESSDENIED))
At C:\Windows\Temp\install_gpo.ps1:2 char:1
+ Import-GPO -BackupGpoName LogonBanner -TargetName LogonBanner -Path C ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Import-GPO], UnauthorizedAcce
ssException
+ FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.Gro
upPolicy.Commands.ImportGpoCommand
So it seems that the 'System' user doesn't have access to do what I want/need! Not sure why that is, I specify the highest RunLevel and Unrestricted execution policy..
Running the Import-GPO command in an elevated PS shell works just fine!
I can't run it as Administrator, because that would require a password (that doesn't work anyway, Register-ScheduledTask refuses to accept that on the command line), I can't specify both -UserID and -GroupID (...\Administrator for example) at the same time.
I've tried using the RunOnce registry key, but that seem to work even less. Also, if I understand the docs for that correctly, it is only run when a/any user logs in, which might never happen! This is (supposed to be) a "headless server" so no-one should ever login to it..
I'm at a loss to what to try next.

I need to schedule a job to run once at startup on Windows 2016 without a user logon

I'm automating builds of windows servers in AWS (via packer and terraform). I have a userdata script that changes the hostname when host is started and reboots after.
My problem is that, after the system reboots, I need a software installation to happen (because it relies on the changed name.)
I've tried something like this:
$RunOnceKey = "HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce"
set-itemproperty $RunOnceKey "NextRun" ('C:\Windows\System32\WindowsPowerShell\v1.0\Powershell.exe -executionPolicy Unrestricted -File ' + "C:\mydir\after-boot\install_mysoftware.ps1")
The problem with this is that it only runs after a user login, which is unacceptable.
So I read about RunServiceOnce, but when I try to use that in place of RunOnce, I get an error:
set-itemproperty : Cannot find path 'C:\Users\Administrator\HKLM\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce'
because it does not exist
How can I ensure my program runs once on system boot without waiting for a user login? Point/click won't work. I need a powershell solution to set this up.
Since it is windows server, you can also schedule task in Windows Task Scheduler.
EXAMPLE:
$destination = "C:\Windows\Tasks\InstallSoftware.ps1"
$action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument $destination
$trigger = New-ScheduledTaskTrigger -AtStartup
$principal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount -RunLevel Highest
Register-ScheduledTask -TaskName 'RemoveAppLogs' -TaskPath '\CustomTasks\' -Action $action -Trigger $trigger -Principal $principal -Description 'The task will run script to install software.' -ErrorAction Stop

Add-Content, Start-Transcript, and Remove-Item not working at Startup via a Task

I thought I had this issue entirely solved but not. Dug into it a bit more and learned a few more things.
Goal
Create Log File on startup to a file share using a UNC path. Uses Add-Content.
Use Start-Transcript to create a local log of the PS Script that is running.
Remove files once complete using Remove-Item.
Issue - Only some of the time. Some times everything works perfectly
Logs are not being created, and files are not being removed
If one of these things are not working, then they are all failing. I have no seen in my testing where only one of these actions is complete.
I do not have any error to show or help me identify the issue. The script is being run by a task via Task Scheduler using the SYSTEM Account
Unsure where to turn without an error or something.
This is how Task is Created
$Task_Action = New-ScheduledTaskAction -Execute 'PowerShell.exe' -Argument '-File C:\Script.ps1'
$Task_Principal = New-ScheduledTaskPrincipal -UserId SYSTEM -RunLevel Highest
$Task_Settings = New-ScheduledTaskSettingsSet -Hidden
$Task_Trigger = New-ScheduledTaskTrigger -AtStartup
Register-ScheduledTask `
-TaskName "This is Task" `
-Action $Task_Action `
-Principal $Task_Principal `
-Trigger $Task_Trigger `
-Settings $Task_Settings `
-Force
This is how transcript is created
Start-Transcript -Path "C:\Logs\Transcript.txt"
This is how log via UNC Path is added
Function WriteTo-LogFile($Text_For_Log) {
$Log_DateTime = (Get-Date).ToFileTimeUtc()
Add-Content -Path $Log_File -Value ($Log_DateTime + ",$Text_For_Log")
}
WriteTo-LogFile ("Adding Stuff to Log.")

Can I create global powershell PSWorkflowJob jobs that all users can see?

I want to write a powershell workflow that will run scripts, reboot and continue execute where it left off. Not when a user logs on, but when the computer reboots (so non-interactively).
The challenge is that it needs to run locally. Windows makes this infuriatingly difficult.
What I have done so far is create a workflow that runs as a job (PSWorkflowJob).
In the middle of the workflow it runs restart-computer with the -wait switch
Then I create a scheduled task that runs at startup which resumes that workflow
The problem is that tasks cannot run at startup unless they store credentials or run as the local system. I don't want to pass admin creds at all in this script so I run the task as the local system.
When the computer reboots that task fires, but it can't find the job because the job was created by the local admin.
workflow Resume_Workflow
{
PowerShell.exe -Noninteractive -ExecutionPolicy Bypass -File 'C:\somescript.ps1'
Restart-Computer -Wait
PowerShell.exe -Noninteractive -ExecutionPolicy Bypass -File 'C:\someotherscript.ps1'
}
$taskName = 'Continue_Workflow'
#Pass code directly to powershell.exe to avoid creating another .ps1 file
$PSCode = #'
-NoProfile -command "& powershell.exe {
Import-Module PSWorkflow;
Resume-Job -Name new_resume_workflow_job -Wait;
}"
'#
$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument $PSCode
$trigger = New-ScheduledTaskTrigger -AtStartup -RandomDelay 00:00:30
$settings = New-ScheduledTaskSettingsSet -Compatibility Win8
$principal = New-ScheduledTaskPrincipal -UserId SYSTEM `
-LogonType ServiceAccount -RunLevel Highest
$definition = New-ScheduledTask -Action $action -Principal $principal `
-Settings $settings -Description "Run $($taskName) at startup" `
-Trigger $trigger
Register-ScheduledTask -TaskName $taskName -InputObject $definition
# Execute the workflow as a PS job (PSWorkflowJob)
Resume_Workflow -AsJob -JobName new_resume_workflow_job

Register a task for another user logon

I run a powershell script as Admiministrator, and I want to register a scheduled task for another user to run at logon.
The following powershell code will register an event for the Adiministrator user:
$action = New-ScheduledTaskAction -Execute 'C:\Windows\System32\notepad.exe'
$trigger = New-ScheduledTaskTrigger -AtLogOn
Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "NotepadLaunch" -Description "Launch notepad at logon"
I see that the New-ScheduledTaskTrigger supports the -User option:
https://technet.microsoft.com/en-us/library/jj649821.aspx
User<String>
Specifies the identifier of the user for a trigger that starts a task when a user logs on.
Unfortunately the documentation is not very clear, and it doesn't seem to work as I would expect (setting the user where the action is triggered).
My question: is it possible to specify a different user when scheduling a task?
There are other suitable alternatives to set a command to be launched when another user logs on (assuming I'm using the Administrator user to set the command)?
Tested on Server 2012 and Powershell 3.0
$action = New-ScheduledTaskAction -Execute 'C:\Windows\System32\notepad.exe'
$trigger = New-ScheduledTaskTrigger -AtLogOn -User "DOMAIN\username"
Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "NotepadLaunch" -Description "Launch notepad at logon" -User "DOMAIN\username"

Resources