Powershell调用计算机到远程我的本地计算机重新启动后幸存下来?
Posted
技术标签:
【中文标题】Powershell调用计算机到远程我的本地计算机重新启动后幸存下来?【英文标题】:Powershell invoke-computer to a remote that survives a reboot of my local computer? 【发布时间】:2021-12-16 10:31:41 【问题描述】:有没有办法“调用命令”到远程计算机,这样我就可以重新启动计算机并且作业仍将运行,并且我可以随时检查输出日志?
PS> invoke-command -Computer Remote1 -File "ScriptThatRunsFor7days.ps1"
PS> restart-computer
PS> # Hey where's my job on remote computer? Can i see it running and connect to
# its output after rebooting my computer?
【问题讨论】:
我已删除您问题的第二部分,以防止因“过于宽泛”或“可能引出基于意见的答案”而关闭。否则这是一个很好的问题,我将在今天晚些时候尝试回答,但我也有兴趣看看是否有人比我使用的繁琐方法有更优雅的答案。 试试*-PSSession
cmdlet; Disconnect-PSSession 听起来可以。
我正在做一些测试,但必须运行。我不确定是否可以跨会话或重新启动恢复断开的会话。我知道断开连接的会话会超时,但在创建会话时可以配置。
@BendertheGreatest 我不相信它可以。我认为workflows
是专门为此而设计的。
@SantiagoSquarzon 我没有对工作流程做过任何事情,但怀疑它们在这里可能有用。但是,虽然工作流可以在会话和重新启动后继续存在,但“活动”对象是否也会在恢复执行时保持可操作状态?
【参考方案1】:
只注册一个在远程计算机上运行脚本的计划任务不是更容易吗? 对于日志记录,只需使用脚本顶部的 cmdlet Start-Transcript。 不久前我编写了一个脚本,以便在远程计算机上轻松注册计划任务。也许你可以试试看它是否适合你?
[CmdletBinding()]
param(
[parameter(Mandatory=$true)]
[string]
$PSFilePath,
[parameter(Mandatory=$true)]
[string]
$TaskName,
[parameter(Mandatory=$true)]
[string]
$ComputerName
)
$VerbosePreference="Continue"
New-Variable -Name ScriptDestinationFolder -Value "Windows\PowershellScripts" -Option Constant -Scope Script
New-Variable -Name ScriptSourcePath -Value $PSFilePath -Option Constant -Scope Script
Write-Verbose "Script sourcepath: $ScriptSourcePath"
New-Variable -Name PSTaskName -Value $TaskName -Option Constant -Scope Script
Write-Verbose "TaskName: $TaskName"
$File = Split-Path $ScriptSourcePath -leaf
Write-Verbose "Filename: $File"
New-Variable -Name PSFileName -Value $File -Option Constant -Scope Script
Write-Verbose "PSFileName: $PSFileName"
$ExecutionTime = New-TimeSpan -Hours 8
Write-Verbose "Execution time: $ExecutionTime hours"
Invoke-Command -ComputerName $ComputerName -ScriptBlock
$VerbosePreference="Continue"
#Removing old Scheduled Task
Write-Verbose "Unregistering old scheduled task.."
Stop-ScheduledTask -TaskName $Using:PSTaskName -ErrorAction SilentlyContinue
Unregister-ScheduledTask -TaskName $Using:PSTaskName -Confirm:$false -ErrorAction SilentlyContinue
#Creating destination directory for Powershell script
$PSFolderPath = "C:" , $Using:ScriptDestinationFolder -join "\"
Write-Verbose "Creating folder for script file on client: $PSFolderPath"
New-Item -Path $PSFolderPath -ItemType Directory -Force
#Scheduled Task definitions
$Trigger = New-ScheduledTaskTrigger -Daily -At "8am"
$PSFilePath = "C:", $Using:ScriptDestinationFolder , $Using:PSFileName -join "\"
Write-Verbose "Setting path for script file to destination folder on client: $PSFilePath"
$Action = New-ScheduledTaskAction -Execute PowerShell -Argument "-File $PSFilePath"
$Principal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType S4U
$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -DontStopOnIdleEnd -ExecutionTimeLimit $Using:ExecutionTime -StartWhenAvailable
$Task = Register-ScheduledTask -TaskName $Using:PSTaskName -Principal $Principal -Action $Action -Settings $Settings -Trigger $Trigger
$Task = Get-ScheduledTask -TaskName $Using:PSTaskName
$Task.Triggers[0].EndBoundary = [DateTime]::Now.AddDays(90).ToString("yyyyMMdd'T'HH:mm:ssZ")
Write-Verbose "Trigger expiration date set to: $Task.Triggers[0].EndBoundary"
$Task.Settings.DeleteExpiredTaskAfter = 'P1D'
Write-Verbose "Scheduled task will be deleted after $Task.Settings.DeleteExpiredTaskAfter after expiry."
$Task | Set-ScheduledTask -ErrorAction SilentlyContinue
#End Invoke-Command
#Copy script file from source to the computer
$ScriptDestination = "\" , $ComputerName , "C$", $ScriptDestinationFolder -join "\"
Write-Verbose "Script destination is set to: $ScriptDestination"
Write-Verbose "Copying script file: `"$ScriptSourcePath`" to `"$ScriptDestination`""
Copy-Item -Path $ScriptSourcePath -Destination $ScriptDestination -Force
用法:
Create-ScheduledTask-Test.ps1 -ComputerName MyRemoteComputer -PSFilePath "ScriptToRun.ps1" -TaskName DoSomeWork
【讨论】:
【参考方案2】:有预定工作的东西。我正在使用 pssession 将脚本复制到远程计算机。
$s = new-pssession remote1
copy-item script.ps1 c:\users\admin\documents -tosession $s
invoke-command $s Register-ScheduledJob test script.ps1 -Runnow
然后,只有当它开始运行时,它才会在远程计算机上自动显示为常规作业:
invoke-command remote1 get-job | receive-job -keep
【讨论】:
【参考方案3】:vonPryz 提供了关键指针:
在 Windows 上,PowerShell 提供断开的远程会话,允许您稍后从任何客户端会话重新连接并收集输出,即使在注销或重新启动 - 假设远程计算机上断开的会话没有超时。
请参阅概念性 about_Remote_Disconnected_Sessions 帮助主题。
以下示例脚本演示了该方法:
将其保存到*.ps1
文件并调整$computerName
和$sessionName
变量值。
脚本假定当前用户身份可以按原样使用远程进入目标计算机;如果不是这种情况,请将-Credential
参数添加到Invoke-Command
和Get-PSSession
调用。
调用脚本,并在出现提示时选择何时连接到已创建的断开连接的远程会话 - 包括在注销/重新启动后,在这种情况下,脚本会自动重新调用以连接到断开连接的会话并检索其输出。
请参阅源代码 cmets 了解详细信息,尤其是关于空闲超时的信息。
下面未的一个方面是输出缓冲:断开连接的会话长时间运行而没有检索到其输出,可能会积累大量输出。默认情况下,如果输出缓冲区已满,则暂停执行。 OutputBufferingMode
会话选项控制行为 - 请参阅 New-PSSessionOption
cmdlet。
解决方案的要点是:
Invoke-Command
调用与-InDisconnectedSession
开关 启动远程计算机上的操作在即时断开会话。也就是说,调用会在操作开始后立即返回但不会从操作返回任何结果(它会返回有关已断开会话的信息)。
稍后的 Receive-PSSession
调用(可能在重新启动后发生)隐式连接到断开的会话并检索操作结果。
$ErrorActionPreference = 'Stop'
# ADAPT THESE VALUES AS NEEDED
$computer = '???' # The remote target computer's name.
$sessionName = 'ReconnectMe' # A session name of your choice.
# See if the target session already exists.
$havePreviousSession = Get-PSSession -ComputerName $computer -Name $sessionName
if (-not $havePreviousSession)
# Create a disconnected session with a distinct custom name
# with a command that runs an output loop indefinitely.
# The command returns instantly and outputs a session-information object
# for the disconnected session.
# Note that [int]::MaxValue is used to get the maximum idle timeout,
# but the effective value is capped by the value of the remote machine's
# MaxIdleTimeoutMs WSMan configuration item, which defaults to 12 hours.
Write-Verbose -vb "Creating a disconnected session named $sessionName on computer $computer..."
$disconnectedSession =
Invoke-Command -ComputerName $computer -SessionName $sessionName -InDisconnectedSession -SessionOption @ IdleTimeout=[int]::MaxValue while ($true) Write-Host -NoNewLine .; Start-Sleep 1
# Prompt the user for when to connect and retrieve the output
# from the disconnected session.
do
$response = Read-Host @"
---
Disconnected session $sessionName created on computer $computer.
You can connect to it and retrieve its output from any session on this machine,
even after a reboot.
* If you choose to log off or reboot now, this script re-runs automatically
when you log back in, in order to connect to the remote session and collect its output.
* To see open sessions on the target computer on demand, run the following
(append | Remove-PSSession to remove them):
Get-PSSession -ComputerName $computer
---
Do you want to (L)og off, (R)eboot, (C)onnect right now, or (Q)uit (submit with ENTER)? [l/r/c/q]
"@
while (($response = $response.Trim()) -notin 'l', 'r', 'c', 'q')
$autoRelaunchCmd =
Set-ItemProperty registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce 'ReconnectDemo' "$((Get-Command powershell.exe).Path) -noexit -command `"Set-Location '$PWD'; . '$PSCommandPath'`""
switch ($response)
'q' Write-Verbose -vb 'Aborted.'; exit 2
'c' break # resume below
'l'
Write-Verbose -vb "Logging off..."
& $autoRelaunchCmd
logoff.exe
exit
'r'
Write-Verbose -vb "Rebooting..."
& $autoRelaunchCmd
Restart-Computer
exit
# Getting here means that a remote disconnection session was previously created.
# Reconnect and retrieve its output.
# Implicitly reconnect to the session by name,
# and receive a job object representing the remotely running command.
# Note: Despite what the docs say, -OutTarget Job seems to be the default.
# Use -Output Host to directly output the results of the running command.
Write-Verbose -vb "Connecting to previously created session $sessionName on computer $computer and receiving its output..."
$job = Receive-PSSession -ComputerName $computer -Name $sessionName -OutTarget Job
# Get the output from the job, timing out after a few seconds.
$job | Wait-Job -Timeout 3
$job | Remove-Job -Force # Forcefully terminate the job with the indefinitely running command.
# Remove the session.
Write-Host
Write-Verbose -Verbose "Removing remote session..."
Get-PSSession -ComputerName $computer -Name $sessionName | Remove-PSSession
【讨论】:
【参考方案4】:function remote_nohup
param(
[string]$Machine,
[string]$Cmd
)
$job_tstamp = $(get-date -f MMdd_HHmm_ss)
$job_name = "$job_tstamp"
$job_dir_start = (Get-Location).Path
$job_dir_sched = "$env:userprofile/Documents/jobs"
$job_file = "$job_dir_sched/$job_name.run.ps1"
$job_log = "$job_dir_sched/$job_name.log"
$job_computer = $Machine
$job_cmd = $Cmd
# Create Job File
$job_ps1 = @(
"`$ErrorActionPreference = `"Stop`""
""
"Start-Transcript -path $job_log -append"
""
"try "
" write-host 'job_begin:($job_name)'"
" "
" set-location $job_dir_start -EA 0"
" "
" write-host 'job_cmd:($job_cmd)'"
" $job_cmd | out-host"
""
" write-host 'job_end:($job_name)'"
""
"catch "
" `$msg = `$_"
" write-host `$msg"
" write-error `$msg"
""
"finally "
" Stop-Transcript"
""
""
"Exit-PSSession"
)
try
New-Item -ItemType Directory -Force -EA:0 -Path $job_dir_sched | out-null
copy-Item $remote_profile_ps1 $job_profile
write-host "Creating File: $job_file"
$f1 = open_w $job_file -fatal
foreach ($line in $job_ps1)
$f1.WriteLine($line)
finally
$f1.close()
# Create Jobs Dir
write-host "Creating remote job Directory"
Invoke-Command -Computer $job_computer -ScriptBlock
New-Item -ItemType Directory -Force -EA:0 -Path $using:job_dir_sched | out-null
# Copy Job File
write-host "copy-Item -recurse -ToSession `$s2 $job_file $job_file"
$s2 = New-PSSession -Computer $job_computer
copy-Item -ToSession $s2 $job_file $job_file
Receive-PSSession -Session $s2
Remove-PSSession -Session $s2
# Create Persistent Background Job
write-host "Submitting job to remote scheduler"
Invoke-Command -Computer $job_computer -ScriptBlock
Register-ScheduledJob -RunNow -Name $using:job_name -File $using:job_file
Exit-PSSession
# NOTE: Log file from run is placed on
# remote computer under jobs dir
function open_w
param([string]$path, [switch]$fatal)
try
write-host "path: $path"
$pathfix = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path)
$handle = [System.IO.StreamWriter]::new($pathfix, $false) #false:over-write, true:append
catch
if ($fatal)
write-host -ForegroundColor Red "EXCEPTION: " + $PSItem.ToString()
exit 1
return $null
return $handle
【讨论】:
与您之前的自我回答一样,没有适当的框架和解释,不清楚您的回答如何解决您自己的问题。以上是关于Powershell调用计算机到远程我的本地计算机重新启动后幸存下来?的主要内容,如果未能解决你的问题,请参考以下文章