vonPryz provided the crucial pointer:
On Windows, PowerShell offers disconnected remote sessions that allow you to reconnect and collect output later, from any client session, even after a logoff or reboot - assuming that the disconnected session on the remote computer hasn't timed out.
See the conceptual about_Remote_Disconnected_Sessions help topic.
The following sample script demonstrates the approach:
Save it to a *.ps1
file and adapt the $computerName
and $sessionName
variable values.
The script assumes that the current user identity can be used as-is to remote into the target computer; if that is not the case, add a -Credential
argument to the Invoke-Command
and Get-PSSession
calls.
Invoke the script and, when prompted, choose when to connect to the disconnected remote session that was created - including after a logoff / reboot, in which case the script is automatically reinvoked in order to connect to the disconnected session and retrieve its output.
See the source-code comments for details, particularly with respect to the idle timeout.
One aspect not covered below is output buffering: a disconnected session that runs for a long time without having its output retrieved can potentially accumulate a lot of output. By default, if the output buffer fills up, execution is suspended. The OutputBufferingMode
session option controls the behavior - see the New-PSSessionOption
cmdlet.
The gist of the solution is:
An Invoke-Command
call with the -InDisconnectedSession
switch that starts an operation on a remote computer in an instantly disconnected session. That is, the call returns as soon as the operation was started without returning any results from the operation yet (it returns information about the disconnected session instead).
A later Receive-PSSession
call - which may happen after a reboot - implicitly connects to the disconnected session and retrieves the results of the operation.
$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