3

I'm writing a PowerShell-Script that reads all shares from all AD-Servers and outputs them into a csv-file. At the same time the script is saving all occuring errors and outputs them into an error-log. The script will be run as a weekly task. When I run the script, all goes well until it gets to a server that has frozen. In that case, the script will just run forever because it gets no answer from the server.

Now I need to add some sort of timeout that skips a server after it doesn't recieve an answer for a specific amount of time. How would I do that with my existing code?

My Code:


$computers = (Get-Content C:\PowerShell\Shares\serverlist.txt).ForEach({
    if(-not [string]::IsNullOrWhiteSpace($_))
    {
        "$_.domain.com"
    }
})

$remoteCode = {
    Get-SmbShare | Where-Object Path | Get-Acl |
    Select-Object -Property "PSChildName", "Path", "Group", "AccessToString"
}

$results = Invoke-Command -ComputerName $computers -ScriptBlock $remoteCode 2>&1
$errors, $good = $results.Where({$_ -is [System.Management.Automation.ErrorRecord]}, 'Split')

$good | Sort-Object PSComputerName | Select-Object "PSComputerName", "PSChildName", "Path", "Group", @{ Name = "AccessToString"; Expression = { $_.AccessToString -replace("268435456", "FullControl") -replace("-1610612736", "ReadAndExecute")}} | export-csv -path C:\PowerShell\Shares\shares.csv -NoTypeInformation -delimiter ";"

$errors.Exception.Message | Set-Content $error_logfile -Encoding Unicode
zynory
  • 43
  • 3
  • 3
    Think you can configure it when creating a PSSession. An alternative would be to use jobs. If the job isn't finished, terminate it after certain amount of time. – Abraham Zinala Mar 03 '22 at 13:37

1 Answers1

3

NOTE: This answer is pretty much useless, in an ideal world, -OperationTimeout would do what it's name implies, however, as the helpful comment from mklement0 states:

Unfortunately, the OperationTimeout session option doesn't do what its name suggests: see GitHub issue #15696. Implementing an actual operation-duration timeout is the subject of GitHub proposal #5434, which suggest adding a -Timeout parameter to Invoke-Command.

If you feel this would be a great implementation for future versions of PowerShell, consider up-voting his proposal!


You could use PSSessionOption with a Operation Timeout and Open Timeout below the default values (3 minutes):

See the Parameter section of New-PSSessionOption documentation:

-OpenTimeout
Determines how long the client computer waits for the session connection to be established. When the interval expires, the command to establish the connection fails.

-OperationTimeout
Determines the maximum time WinRM waits for positive connection tests from a live connection before initiating a connection time-out.

$timeOut = 30000 # => 30 seconds
$psso = New-PSSessionOption -OpenTimeout $timeOut -OperationTimeout $timeOut

$session = (Get-Content C:\PowerShell\Shares\serverlist.txt).ForEach({
    if(-not [string]::IsNullOrWhiteSpace($_)) {
        try {
            New-PSSession -ComputerName "$_.domain.com" -SessionOption $psso
        }
        catch {
            Write-Warning $_.Exception.Message
        }
    }
})

Then the rest of the script is the same, except for the use of -Session instead -ComputerName for Invoke-Command:

$results = Invoke-Command -Session $session -ScriptBlock $remoteCode 2>&1

And lastly, after you're done with the remote connections, you would need to remove the PSSessions:

Remove-PSSession $session
Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
  • 3
    Unfortunately, the `OperationTimeout ` session option doesn't do what its name suggests: see [GitHub issue #15696](https://github.com/PowerShell/PowerShell/issues/15696). Implementing an actual operation-duration timeout is the subject of [GitHub proposal #5434](https://github.com/PowerShell/PowerShell/issues/5434), which suggest adding a `-Timeout` parameter to `Invoke-Command`. – mklement0 Mar 03 '22 at 14:39
  • 2
    @mklement0 well I wasn't aware of this, thanks for pointing it out to me. This is very unfortunate though, this answer is useless in this case then – Santiago Squarzon Mar 03 '22 at 15:06
  • 2
    @AbrahamZinala good catch, thanks for pointing it out. Editing my answer. – Santiago Squarzon Mar 03 '22 at 15:07
  • @mklement0 according to [Jordan Borean's comment](https://github.com/PowerShell/PowerShell/issues/15696#issuecomment-873220777) this should work if the client is unresponsive or am I misunderstanding ? – Santiago Squarzon Mar 03 '22 at 15:13
  • 2
    I'm hazy on the details, but it sounds like such timeouts are _ignored_ internally by PowerShell and receiving from the remote session is simply retried. You can verify that the overall operation doesn't time out with the following commands (targets the local machine with "loopback remoting" and therefore requires it to be set up for remoting and requires running _with elevation_): – mklement0 Mar 03 '22 at 15:40
  • 2
    `$computerName = '.'; $timeoutSecs = 2; $pso = New-PSSessionOption -OperationTimeout ($timeoutSecs*1000); $session = New-PSSession -cn $computerName -SessionOption $pso; "Invoking with $timeoutSecs-second OperationTimeout..."; Invoke-Command -Session $session { $sleepSecs = $using:timeoutSecs + 2; Start-Sleep $sleepSecs; "Remote operation completed after $sleepSecs secs." }` – mklement0 Mar 03 '22 at 15:40