PowerShell 脚本太慢

PowerShell script too slow

需要加速脚本的帮助,因为它将 运行 针对 10-20K 服务器。目前在 4K 服务器上测试,耗时将近 6 小时。尝试 运行 将其作为作业(一个父作业和 4000 个子作业,它 运行 很好并且快得多但是父作业永远停留在“运行 宁”状态。这是因为其中一个 childjobs 处于“Notstarted”状态。不知道如何解决这个问题。

############################################# #########################################

$today = Get-Date
$path = (Get-Location).Path
$path += "\"
$date = Get-Date -uformat "%Y%m%d%H%M"
$Inputfile = $path + "Computers.txt"
$outfile = $path + "Report\" + "Certificate_Report_$date.csv"
$transcript = $path + "Logs\" + "Transcript_$date.log"

Start-Transcript $transcript

$computers = gc $Inputfile

Foreach ($c in $computers){

$cert = Invoke-Command -ComputerName $c -ScriptBlock{Get-ChildItem Cert:\localmachine -Recurse} -ErrorVariable issue -ErrorAction Continue

If ($issue){
$Connection = $Error[0].FullyQualifiedErrorId
$obj1 = New-Object Psobject
$Obj1 | Add-Member -MemberType NoteProperty -Name Server -Value $c
$Obj1 | Add-Member -MemberType NoteProperty -Name Serverconnection -Value $Connection
#$report += $obj1
$obj1 | Export-Csv $outfile -NoTypeInformation -Append -force
}
Else{$Connection = "Success"}


Foreach ($cer in $cert){

if($cer.Thumbprint -ne $null){

$obj = New-Object Psobject
$Obj | Add-Member -MemberType NoteProperty -Name Server -Value $c
$Obj | Add-Member -MemberType NoteProperty -Name Serverconnection -Value $Connection
$Obj | Add-Member -MemberType NoteProperty -Name PsParentpath -Value $Cer.PsParentpath
$Obj | Add-Member -MemberType NoteProperty -Name Subject -Value $Cer.Subject
$Obj | Add-Member -MemberType NoteProperty -Name Thumbprint -Value $Cer.Thumbprint
$Obj | Add-Member -MemberType NoteProperty -Name DnsNamelist -Value $Cer.DNSNamelist
$Obj | Add-Member -MemberType NoteProperty -Name FriendlyName -Value $Cer.FriendlyName
$Obj | Add-Member -MemberType NoteProperty -Name Issuer -Value $Cer.Issuer
$Obj | Add-Member -MemberType NoteProperty -Name Valid_From -Value $Cer.NotBefore
$Obj | Add-Member -MemberType NoteProperty -Name Expiration_Date -Value $Cer.NotAfter

if ($cer.NotAfter -lt $today){
$status = "Expired"
}
Else{$status = "Valid"}

$Obj | Add-Member -MemberType NoteProperty -Name Cert_Status -Value $status
$obj | Export-Csv $outfile -NoTypeInformation -Append

}
}
}


Stop-Transcript

想到的唯一明显的优化(没有并行化远程查询)是避免 | Add-Member 并为结果对象使用 [pscustomobject] 语法:

$today = Get-Date
$date = Get-Date -uformat "%Y%m%d%H%M"
$Inputfile = (Resolve-Path "Computers.txt").Path
$outfile = (Resolve-Path "Report\Certificate_Report_$date.csv").Path
$transcript = (Resolve-Path "Logs\Transcript_$date.log").Path

Start-Transcript $transcript

$computers = gc $Inputfile

Foreach ($c in $computers) {

    $cert = Invoke-Command -ComputerName $c -ScriptBlock { Get-ChildItem Cert:\localmachine -Recurse } -ErrorVariable issue -ErrorAction Continue

    If ($issue) {
        $Connection = $Error[0].FullyQualifiedErrorId
        $obj1 = [pscustomobject]@{
            Server = $c
            Serverconnection = $Connection
        } | Export-Csv $outfile -NoTypeInformation -Append -force
    }
    Else {
        $Connection = "Success"
    }

    Foreach ($cer in $cert) {
        if ($null -ne $cer.Thumbprint) {
            [pscustomobject]@{
                Server = $c
                Serverconnection = $Connection
                PsParentpath = $Cer.PsParentpath
                Subject = $Cer.Subject
                Thumbprint = $Cer.Thumbprint
                DnsNamelist = $Cer.DNSNamelist
                FriendlyName = $Cer.FriendlyName
                Issuer = $Cer.Issuer
                Valid_From = $Cer.NotBefore
                Expiration_Date = $Cer.NotAfter
                Cert_Status = if ($cer.NotAfter -lt $today) { "Expired" } else { "Valid" }
            } | Export-Csv $outfile -NoTypeInformation -Append
        }
    }
}

Stop-Transcript

作为 ,您可能还想尝试将远程命令的并行执行完全卸载到 Invoke-Command,方法是预先传递所有计算机名称:

Invoke-Command -ComputerName $computers -ScriptBlock {Get-ChildItem Cert:\localmachine -Recurse} -ErrorAction Continue |For-EachObject {
  # Process the results here
}

如果您需要有关使用后台作业进行故障排除的帮助,请post您遇到问题的代码:)

脚本 posted 在我的问题中花费了将近 6 个小时来完成这个修改版本在 30 分钟内完成的同样的事情。感谢您对此 post 的帮助。下面是最终脚本:

$today = Get-Date
$date = Get-Date -uformat "%Y%m%d%H%M"
$Inputfile = gc (Resolve-Path "Computers.txt").Path
$outfile = (Resolve-Path "Report\").Path + "Certificate_Report_$date.csv"
$transcript = (Resolve-Path "Logs\").Path + "Transcript_$date.log"
$failed = "Couldn't retrieve Data"

$IC_ScriptBlock = {Get-ChildItem Cert:\localmachine -Recurse}
$IC_Params = @{
    ComputerName = $Inputfile
    ScriptBlock = $IC_ScriptBlock
    ErrorAction = 'SilentlyContinue'
        }
$responding = Invoke-Command @IC_Params|ForEach-Object {
if ($null -ne $_.Thumbprint) {
            [pscustomobject]@{
                Server = $_.pscomputername
                PsParentpath = $_.PsParentpath
                Subject = $_.Subject
                Thumbprint = $_.Thumbprint
                DnsNamelist = $_.DNSNamelist
                FriendlyName = $_.FriendlyName
                Issuer = $_.Issuer
                Valid_From = $_.NotBefore
                Expiration_Date = $_.NotAfter
                Cert_Status = if ($_.NotAfter -lt $today) { "Expired" } else { "Valid" }
            } | Export-Csv $outfile -NoTypeInformation -Append
        }
        }

$not_responding = $Inputfile.Where({
$_ -notin $responding.Pscomputername -and "[$_]" -notin $responding.pscomputername
}).

foreach({
[pscustomobject]@{
                Server = $_
                PsParentpath = $failed
                Subject = $failed
                Thumbprint = $failed
                DnsNamelist = $failed
                FriendlyName = $failed
                Issuer = $failed
                Valid_From = $failed
                Expiration_Date = $failed
                Cert_Status = "NA"
                } | Export-Csv $outfile -Append -NoTypeInformation
                
                })