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.
Related
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
I am having a problem with a script for remote windows updates.
My goal: update all the windows servers, without using WSUS or SCCM, from PowerShell without having to log into all of them and start the update process.
For my script I am using powercli and PSWindowsUpdate modules.
For test purposes I am using a single host in the $hostname variable.
So here is what I have.
$Cred = Get-Credential
Import-Module PSWindowsUpdate
Invoke-Command -ComputerName $HostName {Import-Module PSWindowsUpdate} -Verbose -Credential $Cred
Invoke-Command -ComputerName $HostName {Add-WUServiceManager -ServiceID 7971f918-a847-4430-9279-4a52d1efe18d -Confirm:$false} -Verbose -Credential $Cred
Invoke-Command -ComputerName $HostName -ScriptBlock {Get-WUInstall -MicrosoftUpdate -IgnoreUserInput -AcceptAll -AutoReboot -Confirm:$FALSE -Verbose | Out-File C:\Setup\PSWindowsUpdate.log } -credential $cred
Get-Content \\$HostName\c$\Setup\PSWindowsUpdate.log
After running the script everything works from the shut down, snapshot, power on but I can't install any updates.
I am getting the error below :
WARNING: To perform some operations you must run an elevated Windows PowerShell console.
WARNING: Can't access to machine "hostName". Try use: winrm qc
As I was searching I see that I can not make any elevation in PowerShell itself and I am reading some posts about running a CMD bat to start PowerShell in elevated rights.
It is not a problem for me to copy to every windows server the update script and have a batch to run it in elevated rights but is this the only way?
I will do the batch solution to see what I can achieve but is there any other solution in the problem I am facing?
Has anyone tried that out?
Thank you very much for your time!
I am also open for any comment on my code or fixes!
If Admin right, elevated prompts are your issue, the following may help you.
PS Code to check if running as an Administrator. If not, It will relaunch as an Administrator.
If (-Not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
$Arguments = "& '" + $MyInvocation.MyCommand.Definition + "'"
Start-Process Powershell -Verb RunAs -ArgumentList $Arguments
Break
}
You will also likely need to modify ConsentPromptBehaviorAdmin at HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\ in the Registry
-
Here is a simple BAT file that will will get around the PS Execution policy and then launch the PS Script (Assuming they are named the same).
REG ADD "HKLM\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell" /T REG_SZ /V ExecutionPolicy /D Unrestricted /F
Start PowerShell.exe -Command "& '%~dpn0.ps1'"
Add the end of your PS Script you can use Set-ExecutionPolicy -ExecutionPolicy Restricted -Scope LocalMachine -Force to set the execution policy back to restricted or what you have ti set to.
I ran into this issue when trying to import modules on remote machines using Invoke-Command. There's a security function that doesn't allow you to do it. You might try running the following to see if you're more successful at importing a new module.
$myPsSession = New-PSSession -ComputerName $HostName
Import-Module -Name PSWindowsUpdate -PSSession $myPsSession
I went with the task scheduler solution as it seems easier.
I created a script named WUpdates.ps1
$Timestamp=((Get-Date).ToString('dd_MM_yyyy_HH_mm'))
Import-Module PSWindowsUpdate -Verbose
Add-WUServiceManager -ServiceID 7971f918-a847-4430-9279-4a52d1efe18d -Confirm:$false -Verbose
Get-WUInstall -MicrosoftUpdate -IgnoreUserInput -AcceptAll -AutoReboot -Confirm:$FALSE -Verbose | Format-Table -AutoSize | Out-File C:\Setup\WUpdates\PSWindowsUpdate_$Timestamp.log
and a script to create a schedule task named WinUpdateScheduler.ps1
Unregister-ScheduledTask -TaskName "WindowsUpdates" -Confirm:$False
$Action = New-ScheduledTaskAction -Execute C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -Argument '-f "C:\Setup\WUpdates\WUpdates.ps1"'
$Trigger = New-ScheduledTaskTrigger -Once -At (get-date).AddSeconds(30)
Register-ScheduledTask -Action $Action -Trigger $Trigger -RunLevel Highest -User system -TaskName "WindowsUpdates" -Description "Running Windows updates."
then in the main script I call the schedule script and have my updates installed.
Invoke-Command -ComputerName $HostName { c:\setup\WUpdates\WinUpdateScheduler.ps1 } -Credential $Cred
If anyone want the full script I can provide it.
As of the Nixphoe and Backin answers, I will check them and I will come back later to comment on them.
Thank you very much for your time.
Invoke-WUInstall -ComputerName Test-1 -Script { ipmo PSWindowsUpdate; Get-WUInstall -AcceptAll | Out-File C:\PSWindowsUpdate.log }
-Confirm:$false –Verbose
https://4sysops.com/archives/install-windows-updates-remotely-with-the-powershell/
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.")
I've been looking into Group Managed Service Accounts (gmsa) accounts and I've been using them to run scheduled tasks on Server 2012R2 and PowerShell 5.0.10586.117.
After I've been using them a while I've encountered some very weird behavior.
The problem seem to be some kind of timing issue/race condition somewhere when a task is run as a gmsa account. Some core commands might not exists/be loaded while running the script.
Example:
Script called in the task C:\temp\broken-task\test.cmd
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy "ByPass" -File "C:\temp\broken-task\test.ps1" > "C:\temp\broken-task\test.cmd.txt"
The actual script with weird behavior C:\temp\broken-task\test.ps1
<#
.SYNOPSIS
Broken task.
.DESCRIPTION
Broken task.
#>
[CmdletBinding()] Param ()
Process {
$ErrorActionPreference
Set-Variable -Name "ErrorActionPreference" -Scope "Script" -Value "Stop"
Set-Variable -Name "ErrorActionPreference" -Scope "Script" -Value "Stop"
$ErrorActionPreference
}
I then create and run a task in C:\temp\broken-task\task.ps1
$Action = New-ScheduledTaskAction -Execute "C:\Windows\System32\cmd.exe" -Argument "/C C:\temp\broken-task\test.cmd"
$Principal = New-ScheduledTaskPrincipal -UserID "my-gmsa-user$" -LogonType "Password"
New-ScheduledTask -Action $Action -Principal $Principal | Register-ScheduledTask -TaskPath "\test\" -TaskName "test123" | Out-Null
Start-ScheduledTask -TaskPath "\test\" -TaskName "test123"
After task completion the content of C:\temp\broken-task\test.cmd.txt is the following:
Continue
Set-Variable : The term 'Set-Variable' is not recognized as the name of a cmdle
t, function, script file, or operable program. Check the spelling of the name,
or if a path was included, verify that the path is correct and try again.
At C:\temp\broken-task\test.ps1:15 char:5
+ Set-Variable -Name "ErrorActionPreference" -Scope "Script" -Value ...
+ ~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Set-Variable:String) [], Comman
dNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Stop
What is happening above is that Set-Variable doesn't exist on line 2 of the process block in test.ps1, but exist on line 3 (we can see that $ErrorActionPreference has changed).
If I run either the cmd script or the ps1 as myself (a normal user) I will get the expected output with no errors:
Continue
Stop
So what's going on here? The Set-Variable command is in the Microsoft.PowerShell.Utility module. Is the module somehow loaded in between the first and second call? This also happen with other commands like Get-Item "C:\".
What might be even worse is that this does not always happen. I've seen a task work properly in one run and fail in the other.
Also note that I find it hard to create good examples and this does not always yield the same weird results (but the above example has currently failed without exception in my tests).
Is the combination of powershell, gmsa and task scheduler broken somehow?
I can't really trust the system to do as I which when it has this inconsistent behavior.
A workaround might be explicit loading of the Microsoft.PowerShell.Utility module before calling Set-Variable, but I'm not sure if this is due to timing or something else..
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