我有一个powershell脚本工作,该作业运行服务器列表,远程连接到服务器并运行一个简单的脚本从服务器获取主机名。问题是它在某些情况下会卡住。我仔细研究了https://4sysops.com/archives/invoke-command-compensating-for-slow-responding-computers/。我遇到了一些可以指定超时的作业。
这是我完整的脚本,目前,它挂在一些主机上。
workflow Connect-windows-Servers
{
param(
[string[]]
$server_list
)
foreach -parallel -throttlelimit 50 ($server_name in $server_list){
write-output "Inside Workflow but outside inline $server_name $(Get-Date)"
InLineScript {
$output_status = "" | Select-Object -Property server_name, isError, result
$in_server_name = $using:server_name
write-output "inline = $in_server_name"
$username = "my_usersname"
$password = "my_password"
$secstr = New-Object -TypeName System.Security.SecureString
$password.ToCharArray() | foreach-object {$secstr.AppendChar($_)}
$credentials = New-Object -typename System.Management.Automation.PSCredential -argumentlist $username,$secstr
$result = Invoke-Command -ComputerName $in_server_name -ScriptBlock { [System.Net.Dns]::GetHostName() } -ErrorVariable ErrVar -Credential $credentials
write-output "Error value is $ErrVar"
if ($ErrVar)
{
$output_status.server_name = $in_server_name
$output_status.isError = $true
$output_status.result = $ErrVar
$deployment_output += New-Object -TypeName psobject -Property @{server_name = $in_server_name; isError = $true ; result = $ErrVar }
$sql_query = "insert into my_table (servername,isfailed, result) select '$in_server_name','$true','$ErrVar'"
write-output $sql_query
Invoke-Sqlcmd -ServerInstance "db_server" -Query $sql_query -Database "db_name"
}
else
{
$output_status.server_name = $in_server_name
$output_status.isError = $false
$output_status.result = $result
$deployment_output += New-Object -TypeName psobject -Property @{server_name = $in_server_name; isError = $false ; result = $result }
$sql_query = "insert into my_table (servername,isfailed, result) select '$in_server_name','$false','$result'"
write-output $sql_query
Invoke-Sqlcmd -ServerInstance "db_server" -Query $sql_query -Database "db_name"
}
}
}
}
$list = @('server1',server2)
Connect-windows-Servers -server_list $list
有什么问题
这里的问题是上面的脚本挂在一些主机上,当这种情况发生时,整个过程都卡住了。winRM没有任何错误,这使得自动化非常困难,代码还需要能够并行运行,因为有超过2000台主机。
我做了什么?
我介绍了powershell作业,然而问题是我似乎无法获得$results的值,我想知道的是成功连接的主机,获得主机名的值,对于超时的主机,我希望能够识别它们,最重要的是使用工作流或任何其他方法并行运行,请注意我是在powershell v5上。
如果下面的部分改为
$result = Invoke-Command -ComputerName $in_server_name -ScriptBlock { [System.Net.Dns]::GetHostName() } -ErrorVariable ErrVar -Credential $credentials
$result | wait-job timeout (1*60) | Remove-Job
接下来发生的是,我失去了对特定服务器状态的跟踪,因为$result不再包含主机名,并且我无法确定哪些作业超时了。
另一个可以使用我的原始代码片段的解决方案是,如果我能让winrM优雅地超时,如果有一种方法让我知道什么时候发生,这样我就可以为特定的主机记录它。
请忽略硬编码的用户名/密码,这不是好的做法。当我多次运行脚本时,这种方式比多次执行提示更容易。
我将使用New-PSSession
与Try / Catch
和一个列表来处理代码,您可以存储可能的错误。
缺点是建立连接的循环是线性的,并且可能需要时间。
$servers = 'host1', 'host2', 'host3'
$failed = [Collections.Generic.List[object]]::new()
$session = foreach($server in $servers) {
try {
New-PSSession $server -Credential $cred -ErrorAction Stop
}
catch {
$failed.Add([pscustomobject]@{
Server = $server
Exception = $_.ToString()
})
}
}
# Highly unlikely this could fail if you could establish a PSSession
if($session) {
Invoke-Command -SessionName $session -ScriptBlock { [System.Net.Dns]::GetHostName() }
Remove-PSSession $session
}
if($failed) {
# Has all records of failed connections with the corresponding error messages
$failed
}
如果你想要更快但更脏的东西,你可以使用重定向:
$servers = 'host1', 'host2', 'host3'
$result = Invoke-Command -ComputerName $servers -ScriptBlock {
[System.Net.Dns]::GetHostName()
} -Credential $credential -ErrorAction Continue 2>&1
$failed, $success = $result.where({ $_ -is [System.Management.Automation.ErrorRecord]}, 'Split')
$failed # => has the possible errors
$success # => has the remote results