Created
October 13, 2016 15:57
-
-
Save mhudasch/9009c968db4decd6953a2a81a8dd8159 to your computer and use it in GitHub Desktop.
Schedule an auto-start command to a remote machine with given rights and integrated cleaup after it ran leaving possible piped results.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #requires -version 5 | |
| Set-StrictMode -Version Latest | |
| Function Add-StartupCommand { | |
| [OutputType("ScheduledCommand")] | |
| [CmdletBinding()] | |
| param( | |
| [Parameter(Mandatory=$true,Position=0)] | |
| [String]$TaskName, | |
| [Parameter(Mandatory=$true,Position=1)] | |
| [ScriptBlock]$ScriptBlock, | |
| [Parameter(Mandatory=$false,Position=2)] | |
| [object[]]$ArgumentList, | |
| [Parameter(Mandatory=$false,Position=3)] | |
| [String[]]$ComputerName = $env:COMPUTERNAME, | |
| [Parameter(Mandatory=$false,Position=4)] | |
| [pscredential]$Credential, | |
| [Parameter(Mandatory=$false,Position=5)] | |
| [pscredential]$TaskCredential, | |
| [Parameter(Mandatory=$false,Position=6)] | |
| [Switch]$Elevated) | |
| Process { | |
| $block = [scriptblock] { | |
| param($tn, $sc, $scargs, $cn, [pscredential]$cred, [pscredential]$tcred, $elev, $logsc, $llpref, $scpref, $erpref, $vpref, $wpref, $dpref, $ipref) | |
| $VerbosePreference = $vpref; | |
| $ErrorActionPreference = $erpref; | |
| $WarningPreference = $wpref; | |
| $DebugPreference = $dpref; | |
| $InformationPreference = $ipref; | |
| [System.Management.Automation.PSSerializer]::Serialize($args) | Write-Verbose; | |
| $registerBlock = [scriptblock] { | |
| param($tn, $sc, $scargs, $elev, [pscredential]$tcred, $logsc, $llpref, $scpref, $erpref, $vpref, $wpref, $dpref, $ipref) | |
| $VBoxManageLogLocationPreference = $llpref; | |
| $VBoxManageScriptLocationPreference = $scpref; | |
| $VerbosePreference = $vpref; | |
| $ErrorActionPreference = $erpref; | |
| $WarningPreference = $wpref; | |
| $DebugPreference = $dpref; | |
| $InformationPreference = $ipref; | |
| if(!(Get-Command -Name Write-LogFile -ErrorAction SilentlyContinue)) { | |
| # remap the passed logging function script | |
| New-Item -Path function: -Name 'Write-LogFile' -Value $logsc | Out-Null; | |
| } | |
| if(!(Test-Path $llpref)) { New-Item -ItemType Directory -Path $llpref -Force | Out-Null; } | |
| if(!(Test-Path $scpref)) { New-Item -ItemType Directory -Path $scpref -Force | Out-Null; } | |
| $path = Join-Path $scpref ($tn + ".cmd"); | |
| $scrPath = Join-Path $scpref ($tn + ".ps1"); | |
| $logPath = Join-Path $llpref ($tn + ".log"); | |
| $outPath = Join-Path $scpref($tn + ".out"); | |
| $errPath = Join-Path $scpref($tn + ".err"); | |
| $vrbPath = Join-Path $scpref($tn + ".vrb"); | |
| $wrnPath = Join-Path $scpref($tn + ".wrn"); | |
| $dbgPath = Join-Path $scpref($tn + ".dbg"); | |
| $infoPath = Join-Path $scpref($tn + ".info"); | |
| $result = @{ | |
| ScriptName=$tn; | |
| ScriptCommandPath=$path; | |
| ScriptPath=$scrPath; | |
| ScriptLog=$logPath; | |
| ComputerName=$env:ComputerName; | |
| Script=$null; | |
| TaskDefinition=$null; | |
| OutPath=$outPath; | |
| ErrorPath=$errPath; | |
| WarningPath=$wrnPath; | |
| VerbosePath=$vrbPath; | |
| DebugPath=$dbgPath; | |
| InformationPath=$infoPath; | |
| }; | |
| $serializeObjectDepth = 10; | |
| $TASK_LOGON_PASSWORD = 1; | |
| ("".PadRight(100,"=")) | Write-LogFile -Name $tn | Out-Null; | |
| "[$env:ComputerName] Script-Name: '$tn'." | Write-LogFile -Name $tn | Write-Verbose; | |
| "[$env:ComputerName] Script-Path: '$path'." | Write-LogFile -Name $tn | Write-Verbose; | |
| "[$env:ComputerName] Script-Log: '$logPath'." | Write-LogFile -Name $tn | Write-Verbose; | |
| "[$env:ComputerName] Script-Return: '$outPath'." | Write-LogFile -Name $tn | Write-Verbose; | |
| #region wrapper script build | |
| $scPreamble = "`$VerbosePreference = '$vpref';`r`n"; | |
| $scPreamble += "`$ErrorActionPreference = '$erpref';`r`n"; | |
| $scPreamble += "`$WarningPreference = '$wpref';`r`n"; | |
| $scPreamble += "`$DebugPreference = '$dpref';`r`n"; | |
| $scPreamble += "`$InformationPreference = '$ipref';`r`n"; | |
| $scPreamble += "`$script:ScheduledCommandName=`"$tn`";`r`n"; | |
| $scPreamble += "`$script:ScheduledCommandLogPath=`"$logPath`";`r`n"; | |
| $scPreamble += "`$script:PostponeAutoCleanup=`$false;`r`n"; | |
| $scPreamble += "`$scwarn=@(); `$scverbose=@(); `$scdebug=@(); `$scinfo=@();`r`n"; | |
| $scArgumentStatement = "-ArgumentList `$([System.Management.Automation.PSSerializer]::Deserialize(@`"`r`n$([System.Management.Automation.PSSerializer]::Serialize($scargs, 999))`r`n`"@))"; | |
| if($scargs) { | |
| $scInvokation = "`r`n`(Invoke-Command -ScriptBlock { $sc } -ErrorAction Stop -ErrorVariable scerr $scArgumentStatement 3>&1 4>&1 5>&1 6>&1) | `r`n"; | |
| } else { | |
| $scInvokation = "`r`n`(Invoke-Command -ScriptBlock { $sc } -ErrorAction Stop -ErrorVariable scerr 3>&1 4>&1 5>&1 6>&1) | `r`n"; | |
| } | |
| $scStreamfilter = "ForEach-Object { `$t = `$_.GetType().Name; `$item = `$_; switch (`$t) { `"WarningRecord`" { `$scwarn+=`$item; } `"VerboseRecord`" { `$scverbose+=`$item; } `"DebugRecord`" { `$scdebug+=`$item; } `"InformationRecord`" { `$scinfo+=`$item; } default { `$item; } } } | "; | |
| $scOutPipe = "Export-CliXml -Path `"$outPath`" -Depth $serializeObjectDepth -Force;`r`n"; | |
| $scErrPipe = "if(`$scerr) { `$scerr | Export-CliXml -Path `"$errPath`" -Depth $serializeObjectDepth -Force; }`r`n"; | |
| $scVrbPipe = "if(`$scverbose.Count -gt 0) { `$scverbose | Export-CliXml -Path `"$vrbPath`" -Depth $serializeObjectDepth -Force; }`r`n"; | |
| $scWrnPipe = "if(`$scwarn.Count -gt 0) { `$scwarn | Export-CliXml -Path `"$wrnPath`" -Depth $serializeObjectDepth -Force; }`r`n"; | |
| $scDbgPipe = "if(`$scdebug.Count -gt 0) { `$scdebug | Export-CliXml -Path `"$dbgPath`" -Depth $serializeObjectDepth -Force; }`r`n"; | |
| $scInfoPipe = "if(`$scinfo.Count -gt 0) { `$scinfo | Export-CliXml -Path `"$infoPath`" -Depth $serializeObjectDepth -Force; }`r`n"; | |
| $cleanup = "if(!`$script:PostponeAutoCleanup) { (& schtasks /Delete /F /TN $tn 2>&1) | Out-Null;`r`nRemove-Item -Path $path -Force | Out-Null;`r`nRemove-Item -Path $scrPath -Force | Out-Null; }"; | |
| $finalScript = "$scPreamble try { $scInvokation $scStreamfilter $scOutPipe $scErrPipe $scVrbPipe $scWrnPipe $scDbgPipe $scInfoPipe } catch [System.Exception] { `$(`$_.Exception | Format-List -Force | Out-String) | Out-File `"$logPath`" -Append -Encoding utf8; `$_.Exception | Export-CliXml -Path `"$errPath`" -Depth $serializeObjectDepth -Force; } $cleanup"; | |
| $result.Script = $finalScript; | |
| #endregion | |
| Set-Content -Path $scrPath -Value $finalScript -Encoding UTF8 -Force | Out-Null; | |
| $cmd = "powershell.exe -NoLogo -NonInteractive -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File $scrPath"; | |
| Set-Content -Path $path -Value $cmd -Encoding Ascii -Force | Out-Null; | |
| $taskTemplate = "<?xml version=`"1.0`" encoding=`"UTF-16`"?><Task version=`"1.2`" xmlns=`"http://schemas.microsoft.com/windows/2004/02/mit/task`"><RegistrationInfo><Author>_AUTHOR_</Author><Description>_TASKNAME_</Description></RegistrationInfo><Triggers><BootTrigger><Enabled>true</Enabled></BootTrigger></Triggers><Settings><MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy><DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries><StopIfGoingOnBatteries>true</StopIfGoingOnBatteries><AllowHardTerminate>true</AllowHardTerminate><StartWhenAvailable>true</StartWhenAvailable><RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable><IdleSettings><Duration>PT10M</Duration><WaitTimeout>PT1H</WaitTimeout><StopOnIdleEnd>true</StopOnIdleEnd><RestartOnIdle>false</RestartOnIdle></IdleSettings><AllowStartOnDemand>true</AllowStartOnDemand><Enabled>true</Enabled><Hidden>false</Hidden><RunOnlyIfIdle>false</RunOnlyIfIdle><WakeToRun>false</WakeToRun><ExecutionTimeLimit>PT72H</ExecutionTimeLimit><Priority>7</Priority></Settings><Actions Context=`"Author`"><Exec><Command>_COMMAND_</Command></Exec></Actions><Principals><Principal id=`"Author`"><UserId>_TASKUSER_</UserId><LogonType>Password</LogonType><RunLevel>_RUNLEVEL_</RunLevel></Principal></Principals></Task>"; | |
| $taskTemplate = $taskTemplate -replace "_AUTHOR_", $([Security.Principal.WindowsIdentity]::GetCurrent().Name); | |
| $taskTemplate = $taskTemplate -replace "_TASKNAME_", $tn; | |
| $taskTemplate = $taskTemplate -replace "_COMMAND_", $path; | |
| if($null -ne $tcred) { | |
| $taskCredUserName = $tcred.UserName; | |
| $taskTemplate = $taskTemplate -replace "_TASKUSER_", $taskCredUserName; | |
| } else { | |
| $taskTemplate = $taskTemplate -replace "_TASKUSER_", "SYSTEM"; | |
| } | |
| if($elev) { | |
| $taskTemplate = $taskTemplate -replace "_RUNLEVEL_", "HighestAvailable"; | |
| } else { | |
| $taskTemplate = $taskTemplate -replace "_RUNLEVEL_", "LeastPrivilege"; | |
| } | |
| $result.TaskDefinition = $taskTemplate; | |
| $scheduler = New-Object -ComObject Schedule.Service; | |
| "[$env:ComputerName] Connect to scheduler and register task." | Write-LogFile -Name $tn | Write-Verbose; | |
| for ($i=1; $i -le 3; $i++) { | |
| Try { | |
| $scheduler.Connect($env:COMPUTERNAME); | |
| "[$env:ComputerName] OK" | Write-LogFile -Name $tn | Write-Verbose; | |
| Break; | |
| } Catch { | |
| if($i -ge 3) { | |
| "[$env:ComputerName] Can't connect to Schedule service." | Write-LogFile -Name $tn | Write-Error -ErrorAction Stop | |
| } else { | |
| Start-Sleep -Seconds 1; | |
| } | |
| } | |
| } | |
| $task = $scheduler.NewTask($null); | |
| $task.XmlText = $taskTemplate; | |
| $rootFolder = $scheduler.GetFolder("\"); | |
| if($tcred) { | |
| $taskCredUserName = $tcred.UserName; | |
| $taskCredPasswordClear = $tcred.GetNetworkCredential().Password; | |
| $td = ($rootFolder.RegisterTaskDefinition($tn, $task, 6, $taskCredUserName, $taskCredPasswordClear, $TASK_LOGON_PASSWORD) | Out-String); | |
| } else { | |
| $td = ($rootFolder.RegisterTaskDefinition($tn, $task, 6, "SYSTEM", $Null, $TASK_LOGON_PASSWORD) | Out-String); | |
| } | |
| $td | Write-LogFile -Name $tn | Out-Null; | |
| "[$env:ComputerName] Task $tn added to scheduler." | Write-LogFile -Name $tn | Write-Verbose; | |
| $result = New-Object -TypeName psobject -Property $result; | |
| $result.psobject.typenames.Insert(0, "ScheduledCommand"); | |
| $result | Write-Output; | |
| }; | |
| if($env:COMPUTERNAME -eq $cn) { # locally | |
| if($null -eq $cred) { | |
| Invoke-Command -ScriptBlock $registerBlock -ArgumentList @($tn, $sc, $scargs, $elev, $tcred, $logsc, $llpref, $scpref, $erpref, $vpref, $wpref, $dpref, $ipref) | Write-Output; | |
| } else { | |
| Invoke-Command -Credential $cred -ScriptBlock $registerBlock -ArgumentList @($tn, $sc, $scargs, $elev, $tcred, $logsc, $llpref, $scpref, $erpref, $vpref, $wpref, $dpref, $ipref) | Write-Output; | |
| } | |
| } else { #remotely | |
| $list = New-Object System.Collections.ArrayList; | |
| $list.AddRange(@($cn)); $l = $false; | |
| if($list.Contains($env:COMPUTERNAME)) { $l = $true; $list.Remove($env:COMPUTERNAME); } | |
| if($l) { | |
| if($null -eq $cred) { | |
| Invoke-Command -ScriptBlock $registerBlock -HideComputerName -ArgumentList @($tn, $sc, $scargs, $elev, $tcred, $logsc, $llpref, $scpref, $erpref, $vpref, $wpref, $dpref, $ipref) | ForEach-Object { | |
| $_.psobject.typenames.Insert(0, "ScheduledCommand"); $_; } | Write-Output; | |
| } else { | |
| Invoke-Command -Credential $cred -ScriptBlock $registerBlock -HideComputerName -ArgumentList @($tn, $sc, $scargs, $elev, $tcred, $logsc, $llpref, $scpref, $erpref, $vpref, $wpref, $dpref, $ipref) | ForEach-Object { | |
| $_.psobject.typenames.Insert(0, "ScheduledCommand"); $_; } | Write-Output; | |
| } | |
| } | |
| if($null -eq $cred) { | |
| Invoke-Command -ComputerName ($list.ToArray()) -ScriptBlock $registerBlock -HideComputerName -ArgumentList @($tn, $sc, $scargs, $elev, $tcred, $logsc, $llpref, $scpref, $erpref, $vpref, $wpref, $dpref, $ipref) | ForEach-Object { | |
| $_.psobject.typenames.Insert(0, "ScheduledCommand"); $_; } | Write-Output; | |
| } else { | |
| Invoke-Command -ComputerName ($list.ToArray()) -Credential $cred -ScriptBlock $registerBlock -HideComputerName -ArgumentList @($tn, $sc, $scargs, $elev, $tcred, $logsc, $llpref, $scpref, $erpref, $vpref, $wpref, $dpref, $ipref) | ForEach-Object { | |
| $_.psobject.typenames.Insert(0, "ScheduledCommand"); $_; } | Write-Output; | |
| } | |
| } | |
| }; | |
| Invoke-Command -ScriptBlock $block -ArgumentList @($TaskName, $ScriptBlock, $ArgumentList, $ComputerName, $Credential, $TaskCredential, $Elevated, ${function:Write-LogFile}.ToString(), $VBoxManageLogLocationPreference, $VBoxManageScriptLocationPreference, $ErrorActionPreference, $VerbosePreference, $WarningPreference, $DebugPreference, $InformationPreference) | Write-Output; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment