【问题标题】:Unable to parse variable into the ForEach ($Server in $Servers) loop?无法将变量解析到 ForEach($Servers 中的 $Server)循环中?
【发布时间】:2020-09-07 11:14:06
【问题描述】:

我正在尝试更正以下脚本以使用 Qwinsta 查询服务器登录会话。

此服务器的目标是显示当前登录的用户。

脚本主要部分:

$queryResults = (qwinsta /server:$ServerName | Select-Object -Skip 1 | ForEach-Object { (($_.trim() -replace "\s+", ",")) } |从 csv 转换 -ErrorAction 停止)

工作正常:

PS C:\WINDOWS\system32> $queryResults

services      0     Disc  
--------      -     ----  
console       1     Conn  
Administrator 3     Disc  
rdp-tcp       65536 Listen

脚本:

$HtmlHead = @"
<style>
    body {
        font-family: Arial;
    }
    table {
        width: 100%;
        border-collapse: collapse;
        border: 1px solid;
    }
    th {
        background-color: green;
        border: 1px solid;
        padding: 1px;
    }
    td {
        border: 1px solid;
        padding: 1px;
    }
</style>
"@

$Today = Get-Date -Format 'F'
$SessionList = "`n`nRDP Session List - " + $Today + "`n`n"
$CurrentSN = 0

# Get a list of servers from Active Directory
write-progress -activity "Getting list of servers from Active Directory" -status "... please wait ..."

$Servers = Get-ADComputer -Filter { Enabled -eq $True -and OperatingSystem -like "*Server*" } -SearchBase "OU=Servers,DC=Company,DC=com," | Where-Object { Test-Connection $_.Name -Count 1 -Quiet } | Select-Object -ExpandProperty Name

$NumberOfServers = $Servers.Count

# Iterate through the retrieved list to check RDP sessions on each machine
ForEach ($Server in $Servers) {

    $ServerName = $Server.Name
    Write-Host "Processing $($Server.Name) ..." -ForegroundColor Yellow
    Write-progress -activity "Checking RDP Sessions" -status "Querying $ServerName" -percentcomplete (($CurrentSN / $NumberOfServers) * 100)

    # Run qwinsta and grab the output
    try {
        $queryResults = (qwinsta /server:$ServerName | Select-Object -Skip 1 | ForEach-Object { (($_.trim() -replace "\s+", ",")) } | convertfrom-csv -ErrorAction Stop)

        # get session info from the instance
        ForEach ($QueryResult in $QueryResults) {

            $RDPUser = $($QueryResult.substring(19, 22)).trim()
            $SessionType = $($QueryResult.substring(1, 18)).trim()
            $SessionID = $($QueryResult.substring(41, 5)).trim()
            $ReturnedCurrentState = $($QueryResult.substring(48, 8)).trim()

            $RDPUser = $QueryResult.USERNAME
            $SessionType = $QueryResult.SESSIONNAME
            $SessionID = $QueryResult.ID
            $ReturnedCurrentState = $QueryResult.State

            If ($ReturnedCurrentState -eq $null) { $CurrentState = "Disconnected" } Else { $CurrentState = "Active" }

            # filter out the irrelevant information
            If (($RDPUser -ne $null) -and ($SessionType -ne "console") -and ($SessionType -ne "services") -and ($SessionType -ne "rdp-tcp") -and ($RDPUser -ne "65536")) {
                $SessionList = $SessionList + "`n" + $ServerName + " logged in by " + $RDPUser + " on " + $SessionType + ", session id $SessionID $CurrentState"
            }
        }

    }
    catch {
        $SessionList = $SessionList + "`n Unable to query " + $ServerName
        write-host "Unable to query $ServerName!" -foregroundcolor Red
    }

    $CurrentSN++
}

# Send the output the screen.
$SessionList + "`n`n"

$sendMailArgs = @{
    From       = "$env:USERNAME@$env:userdnsdomain"
    To         = 'SOC@domain.com'
    SmtpServer = 'SMTP.domain.com'
    Priority   = 'High'
    BodyAsHtml = $true
    Body       = ($SessionList | ConvertTo-Html -Head $HtmlHead) -join "`r`n"
    Subject    = "$($SessionList.Count) Logged On users from $($NumberOfServers) online servers as at $($Today)"
}

Send-MailMessage @sendMailArgs

问题是脚本循环总是进入:

    write-host "Unable to query $ServerName!" -foregroundcolor Red

邮件结果总是这样:

*
424

这是什么意思?

【问题讨论】:

  • 已按要求更新。
  • 添加 exception handling 以查看异常错误消息是什么。
  • 是的,我已经包含了 Try/Catch
  • 当然可以,但是 catch 块对异常没有任何作用。请参阅existing answer,了解如何查看异常的实际情况。

标签: powershell


【解决方案1】:

Substring() 方法很可能会引发异常,因为 startIndex 参数值 41 和 48 看起来很大。如果 $QueryResult 字符串最终变短,您将得到该异常并立即转到 catch 语句。

我建议将查询服务器和构建会话列表的错误处理分开。

更新: 以下是如何在脚本示例中分离错误处理。基本上,您需要了解理论上可以使用您提供的输入参数引发异常并涵盖这些情况的方法或 cmdlet。

ForEach ($Server in $Servers) {

    $ServerName = $Server.Name
    Write-Host "Processing $ServerName ..." -ForegroundColor Yellow
    Write-progress -activity "Checking RDP Sessions" -status "Querying $ServerName" -percentcomplete (($CurrentSN / $NumberOfServers) * 100)

    # Run qwinsta and grab the output
    try {
        $queryResults = (qwinsta /server:$ServerName | Select-Object -Skip 1 | ForEach-Object { (($_.trim() -replace "\s+", ",")) } | convertfrom-csv )
    }
    catch {
        # Error handling for failed server connection here
        $SessionList += "`n Unable to query " + $ServerName
        write-host "Unable to query $ServerName!" -foregroundcolor Red
        continue # Skip to next $Server
    }
    # get session info from the instance
    ForEach ($QueryResult in $QueryResults) {
        try {
            # Do something about this
            $RDPUser = $($QueryResult.substring(19, 22)).trim()
            $SessionType = $($QueryResult.substring(1, 18)).trim()
            $SessionID = $($QueryResult.substring(41, 5)).trim()
            $ReturnedCurrentState = $($QueryResult.substring(48, 8)).trim()
    
            $RDPUser = $QueryResult.USERNAME
            $SessionType = $QueryResult.SESSIONNAME
            $SessionID = $QueryResult.ID
            $ReturnedCurrentState = $QueryResult.State
        }
        catch {
            # Insert your error handling here
            # Write-Host "Failed to process query result from $ServerName!" -foregroundcolor Red
             
            Write-Host $Error[0].Exception # If you want to get the exception message. A shorter way to do it below
            # Write-Host $_
        }

        If ($null -eq $ReturnedCurrentState) { $CurrentState = "Disconnected" } Else { $CurrentState = "Active" }

        # filter out the irrelevant information
        If (($null -ne $RDPUser) -and ($SessionType -ne "console") -and ($SessionType -ne "services") -and ($SessionType -ne "rdp-tcp") -and ($RDPUser -ne "65536")) {
            $SessionList = $SessionList + "`n" + $ServerName + " logged in by " + $RDPUser + " on " + $SessionType + ", session id $SessionID $CurrentState"
        }
    }
    $CurrentSN++
} 

我真的很关心你为$RDPUser$SessionType 等变量赋值的代码块。我会给你一个怀疑的好处,我不知道你的环境,但是逻辑看起来很奇怪。如果$QueryResult 是一个具有多个属性的对象,为什么要使用子字符串?该对象没有此方法。如果它只是一个在某些字符位置具有值的长字符串,为什么要尝试获取它的属性值,例如$QueryResult.ID - 它没有任何值。

【讨论】:

  • 感谢您的建议,您能告诉我如何在嵌套的多个 Try/Catch 上执行此操作吗?
  • 当然,我已经用一个例子更新了我的原始答案。
  • 太好了,非常感谢您对思维过程的详细解释和分享,这对我这个问题很有帮助。
猜你喜欢
  • 2015-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-01
相关资源
最近更新 更多