我正在寻找一种方法来在脚本中调用重新启动后从停止的地方继续执行 Powershell 脚本。例如,我正在通过 Powershell 自动化构建 DC,在将 PC 重命名为 TESTDC01 后,需要重新启动,但在重新启动后,继续执行脚本继续 dcpromo 等。
这可能吗?
干杯!
我正在寻找一种方法来在脚本中调用重新启动后从停止的地方继续执行 Powershell 脚本。例如,我正在通过 Powershell 自动化构建 DC,在将 PC 重命名为 TESTDC01 后,需要重新启动,但在重新启动后,继续执行脚本继续 dcpromo 等。
这可能吗?
干杯!
在 TechNet 上有一篇来自 Hey, Scripting Guy 系列的精彩文章,其中介绍了与您所描述的情况非常相似的情况:重命名计算机并在重新启动后恢复脚本。神奇之处在于使用版本 3 中的新工作流程:
workflow Rename-And-Reboot {
param ([string]$Name)
Rename-Computer -NewName $Name -Force -Passthru
Restart-Computer -Wait
Do-MoreStuff
}
一旦声明了工作流(您没有将其分配给变量),您就可以像调用常规 cmdlet 一样调用它。真正的魔力在于-Wait
Restart-Computer cmdlet 上的参数。
Rename-And-Reboot PowerShellWorkflows
来源:https ://devblogs.microsoft.com/scripting/powershell-workflows-restarting-the-computer/
如果 PowerShell v3 或更高版本不是可用的选择,您可以将现有脚本分成多个较小的脚本,并拥有一个在启动时运行的主脚本,检查某处保存的状态(文件、注册表等),然后开始执行新脚本在适当的地方继续。就像是:
$state = Get-MyCoolPersistedState
switch ($state) {
"Stage1" { . \Path\To\Stage1.ps1 ; break }
"Stage2" { . \Path\To\Stage2.ps1 ; break }
"Stage3" { . \Path\To\Stage3.ps1 ; break }
default { "Uh, something unexpected happened" }
}
请务必记住在您浏览较小的脚本时适当地设置您的状态。
上面的答案是对的,但它只适用于远程执行powershell脚本。根据windows web portal,在本地机器重新启动后,让本地运行的脚本从停止的地方恢复的方法是这样的:
workflow Resume_Workflow
{
.....
Rename-Computer -NewName some_name -Force -Passthru
Restart-Computer -Wait
# Do some stuff
.....
}
# Create the scheduled job properties
$options = New-ScheduledJobOption -RunElevated -ContinueIfGoingOnBattery -StartIfOnBattery
$secpasswd = ConvertTo-SecureString "Aa123456!" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential ("WELCOME\Administrator", $secpasswd)
$AtStartup = New-JobTrigger -AtStartup
# Register the scheduled job
Register-ScheduledJob -Name Resume_Workflow_Job -Trigger $AtStartup -ScriptBlock ({[System.Management.Automation.Remoting.PSSessionConfigurationData]::IsServerManager = $true; Import-Module PSWorkflow; Resume-Job -Name new_resume_workflow_job -Wait}) -ScheduledJobOption $options
# Execute the workflow as a new job
Resume_Workflow -AsJob -JobName new_resume_workflow_job
请注意,[System.Management.Automation.Remoting.PSSessionConfigurationData]::IsServerManager
仅当工作流操作旨在在重新启动后在本地执行时,才应将标志设置为 true。
查看带有工作流的 PS 3.0。我还没有和他们合作过,但他们应该会从重启中恢复。
如果这对任何人都有帮助,我所做的是重新启动服务器然后循环直到\\server\c$
离线。接下来,我循环While (-not(Test-path "\\$server\c$"))
确认服务器再次联机并继续我的脚本。
此代码正在运行,但绝对可以改进。它会生成正在重新启动的服务器的 CSV 日志。它也应该在 PowerShell v2 和更新版本中工作。
Param([Parameter(Mandatory=$true)][string]$server)
$ErrorActionPreference = "SilentlyContinue"
Try{
$LastReboot = Get-EventLog -ComputerName $server -LogName system | Where-Object {$_.EventID -eq '6005'} | Select -ExpandProperty TimeGenerated | select -first 1
(Invoke-WmiMethod -ComputerName $server -Path "Win32_Service.Name='HealthService'" -Name PauseService).ReturnValue | Out-Null
Restart-Computer -ComputerName $server -Force
#New loop with counter, exit script if server did not reboot.
$max = 20;$i = 0
DO{
IF($i -gt $max){
$hash = @{
"Server" = $server
"Status" = "FailedToReboot!"
"LastRebootTime" = "$LastReboot"
"CurrentRebootTime" = "FailedToReboot!"
}
$newRow = New-Object PsObject -Property $hash
$rnd = Get-Random -Minimum 5 -Maximum 40
Start-Sleep -Seconds $rnd
Export-Csv D:\RebootResults.csv -InputObject $newrow -Append -Force
"Failed to reboot $server"
exit}#exit script and log failed to reboot.
$i++
"Wait for server to reboot"
Start-Sleep -Seconds 15
}#end DO
While (Test-path "\\$server\c$")
$max = 20;$i = 0
DO{
IF($i -gt $max){
$hash = @{
"Server" = $server
"Status" = "FailedToComeOnline!"
"LastRebootTime" = "$LastReboot"
"CurrentRebootTime" = "FailedToReboot!"
}
$newRow = New-Object PsObject -Property $hash
$rnd = Get-Random -Minimum 5 -Maximum 40
Start-Sleep -Seconds $rnd
Export-Csv D:\RebootResults.csv -InputObject $newrow -Append -Force
"$server did not come online"
exit}#exit script and log failed to come online.
$i++
"Wait for [$server] to come online"
Start-Sleep -Seconds 15
}#end DO
While (-not(Test-path "\\$server\c$"))
$CurrentReboot = Get-EventLog -ComputerName $server -LogName system | Where-Object {$_.EventID -eq '6005'} | Select -ExpandProperty TimeGenerated | select -first 1
$hash = @{
"Server" = $server
"Status" = "RebootSuccessful"
"LastRebootTime" = $LastReboot
"CurrentRebootTime" = "$CurrentReboot"
}
$newRow = New-Object PsObject -Property $hash
$rnd = Get-Random -Minimum 5 -Maximum 40
Start-Sleep -Seconds $rnd
Export-Csv D:\RebootResults.csv -InputObject $newrow -Append -Force
}#End Try.
Catch{
$errMsg = $_.Exception
"Failed with $errMsg"
}
远程执行:
Rename-Computer -ComputerName $computer -NewName "TESTDC01" -DomainCredential $domain\$username -Force -Restart
然后继续你的脚本 8)
需要重新启动我的本地计算机并在之后继续执行脚本。尝试了@adaml 的解决方案,但我无法获得计划的作业(在重新启动后运行)来找到应该恢复的暂停工作流作业。因此,它一直处于暂停状态。
无论凭据或提升,Get-Job 都没有返回工作。另一个奇怪的事情是,如果我通过在 Powershell ISE 中标记代码来运行工作流程并使用 F8 运行该部分,则该作业永远不会被暂停......必须使用 F5 运行整个脚本或从其他地方调用它。
为了让工作恢复正常,我必须注册一个计划任务而不是计划任务:
workflow test-restart {
Write-Output "Before reboot" | Out-File C:/Log/t.txt -Append
Restart-Computer -Wait
Write-Output "$Now2 After reboot" | Out-File C:/Log/t.txt -Append
}
$PSPath = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
$Args = '-NonInteractive -WindowStyle Hidden -NoLogo -NoProfile -NoExit -Command "& {Import-Module PSWorkflow ; Get-Job | Resume-Job}"'
$Action = New-ScheduledTaskAction -Execute $PSPath -Argument $Args
$Option = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -WakeToRun
$Trigger = New-JobTrigger -AtStartUp -RandomDelay (New-TimeSpan -Minutes 5)
Register-ScheduledTask -TaskName ResumeJob -Action $Action -Trigger $Trigger -Settings $Option -RunLevel Highest
test-restart -AsJob
大声思考,为了在 AWS/您的云提供商中这样做的人的利益......
我对 AWS 实例也有同样的问题,需要重命名为客户端标准服务器名称、+域加入、+cert 安装、+Tentacle 安装。我将通过 Terraform 将服务器启动脚本放入实例的 user_data 字段中。
我将在实例的 PS 中设置 EC2 实例“重命名”标签,在设置的每个部分之后,因此当服务器在重命名后重新启动时,脚本将查找 IF “重命名 = 完成”标签值,然后跳过这部分实例启动的时间。DomainJoined、TentacleInstalled 标签等的逻辑相同。
读取标签的代码如下:
$instanceId = (Invoke-RestMethod -Method Get -Uri http://169.254.169.254/latest/meta-data/instance-id)
$instance = ((Get-EC2Instance -region $region -Instance $instanceId).RunningInstance)
$myInstance = $instance | Where-Object { $_.InstanceId -eq $instanceId }
$Renamed = ($myInstance.Tags | Where-Object { $_.Key -eq "Renamed" }).Value