This script is designed for PRTG and will allow you to go through all your RDS hosts and result back the total amount of sessions and active sessions.
You have various options as server name source, see the parameter section on top of the script.
This was also posted here: https://kb.paessler.com/en/topic/83151-total-user-count-rds-windows-2016
Please note that I grabbed the original script and re-wrote it completely, adjust some issues I encountered and tried to make it as variable as possible.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | param( $ServerNameSource, #Numeric script config parameter: 1 = Parameter ServerNames list / 2 = Parameter InputFile / 3 = LDAP query with name-prefix / 4 = LDAP query with filter on OU / 5 = LDAP query with name-prefix and OU filter $ServerNames, #depending on ServerNameSource (1) - a simple, coma separated list of server names e.g. @("server1", "server2", "server3") $ServerNamesInputFile, #depending on ServerNameSource (2) - path and name of a file that holds the server names, one servername per line - e.g.: "C:\servernames.txt" $ServerNamesLDAPnameFilter, #depending on ServerNameSource (3 and/or 5) - name search string - can hold wildcards (*) e.g.: "RDS*" $ServerNamesLDAPOUFilter, #depending on ServerNameSource (4 and/or 5) - e.g.: "OU=RDS hosts,DC=domain,DC=local" $SubstractSessionsPerHost = 2, #substract this amount of sessions per host - due to e.g. two RDS listeners (would make 2 sessions) or additional e.g. 1x ICA listener (would make 3 sessions) etc... $UseWMI = $false #use WMI - if not set it will use QWINSTA by default: enable with: $true ) #function to convert a string to proper XML and write it as output/screen Function WriteXmlToScreen ([xml]$xml) #just to make it clean XML code... { $StringWriter = New-Object System.IO.StringWriter; $XmlWriter = New-Object System.Xml.XmlTextWriter $StringWriter; $XmlWriter.Formatting = "indented"; $xml.WriteTo($XmlWriter); $XmlWriter.Flush(); $StringWriter.Flush(); Write-Output $StringWriter.ToString(); } $ServerNameList; If ($ServerNameSource -gt 0 -and $ServerNameSource -lt 6) { Switch ($ServerNameSource) { 1 { $ServerNameList = $ServerNames; } 2 { $ServerNameList = Get-Content $ServerNamesInputFile; } 3 { $ServerNameList = (Get-ADComputer -LDAPFilter "(name=$ServerNamesLDAPnameFilter)" | Select Name).Name; } 4 { $ServerNameList = (Get-ADComputer -Filter "*" -SearchBase $ServerNamesLDAPOUFilter | Select Name).Name; } 5 { $ServerNameList = (Get-ADComputer -LDAPFilter "(name=$ServerNamesLDAPnameFilter)" -SearchBase $ServerNamesLDAPOUFilter | Select Name).Name; } } } Else { Write-Output "Missing Parameter or wrong value for: ServerNameSource"; Exit; } #if the array holds entries If ($ServerNameList.Count -gt 0) { $SessionsTotal = 0; $SessionsActive = 0; $ServersNotReached = 0; #go through the list ForEach ($ServerName in $ServerNameList) { #try to connect - on error jump to catch Try { $FoundTotal = 0; $FoundActive = 0; If ($UseWMI -eq $false) { #less control in theory - due to reformatting and searching through the results #single execution of the query / command $QWinstaResults = qwinsta /server $ServerName | ForEach-Object { $_.Trim() -replace "\s+","," } | ConvertFrom-Csv -Header "SessionName","UserName","ID","State","Type","Device"; #we add manual headers here, since this otherwise can cause issues with the filter later on $FoundTotal = $QWinstaResults.Count-1; #if we use QWINSTA we need to substract one more session from the results $FoundActive = ($QWinstaResults | ? { $_.State -eq "Active" }).Count; } else { #more control - but might run slower #in theory you can invoke all server-names at once, but then you need to run through the table and you lose some control over what was reached and what wasn't - first run might always be slowest depending on amount of servers $FoundTotal = (Get-WmiObject -Query "SELECT TotalSessions FROM Win32_TerminalService" -ComputerName $ServerName | Select TotalSessions).TotalSessions; $FoundActive = (Get-WmiObject -Query "SELECT ActiveSessions FROM Win32_PerfFormattedData_LocalSessionManager_TerminalServices" -ComputerName $ServerName | Select ActiveSessions).ActiveSessions; } #SessionsTotal might need a substract depending on the parameter input $FoundTotal = $FoundTotal - $SubstractSessionsPerHost; #we need to avoid that the substractions is negative, to avoid false substraction If ($FoundTotal -le 0){ $FoundTotal = 0; } #we now add the results to the current counters $SessionsTotal += $FoundTotal; $SessionsActive += $FoundActive; } Catch { $ServersNotReached += 1; } Finally { #nothing to do here } } #Lets put together the results $PRTGstring="<prtg> <result> <channel>Total Servers checked</channel> <value>" + $ServerNameList.Count + "</value> </result> <result> <channel>Total Servers responded</channel> <value>" + ($ServerNameList.Count - $ServersNotReached) + "</value> </result> <result> <channel>Total Servers not reached</channel> <value>$ServersNotReached</value> </result> <result> <channel>Total Sessions found</channel> <value>$SessionsTotal</value> </result> <result> <channel>Total Sessions active</channel> <value>$SessionsActive</value> </result> </prtg>" #we call the function WriteXmlToScreen to transform the $PRTGstring to XML and output it WriteXmlToScreen "$PRTGstring" } Else { Write-Output "Parameter Error or List did not contain any names / no server names found" Exit; } |