Support for Windows XP ends on April 8, 2014. Some organizations might not be completely off Windows XP and need a good way to determine the status of those workstations. I combined some PowerShell that I wrote a while back to accomplish this:
Pinging a List of Machines in PowerShell
Get Counts of all Operating Systems in a Forest Per Domain
The purpose of this was to find all Windows XP machines in all domains in the forest and then ping each one to determine if it is online. It only looks at XP machines that have active computer accounts in AD and have changed their computer password within the last 31 days (This happens by default every 30 days). This script can also be used to look for any OS, just search the code for “Windows XP” and specify a new filter.
The script doesn’t take any arguments but you must have RSAT and Excel installed on the machine running the script:
Remote Server Administration Tools (RSAT)
- For desktop OS’s download RSAT
- For server OS’s use Add-WindowsFeature RSAT-AD-PowerShell. *Note, the script will try to install this feature if it isn’t already installed, but it only works for server OS’s.
The output of the script will display the XP count per domain but the rich data is in the Excel file that gets generated. There is a tab for each Domain in your environment:
Image may be NSFW.
Clik here to view.
Fields
- DNSHostName: DNS Name from Active Directory
- CN: CN from Active Directory
- Name: Name from Active Directory
- DistinguishedName: DN from Active Directory
- Description: Description from Active Directory
- OperationSystem: OS from Active Directory
- OperatingSystemVersion: OS Version from Active Directory
- NameFromDNS: DNS name from DNS
- IPAddressFromDNS: IP retrieved from DNS
- PingSuccessPercentage: It was pinged 4 times, this is how many were successful
Script
#Functionsfunction ImportADModule { Import-Module ActiveDirectoryif (!($?)) { #Only works for Windows Server OS with PS running as admin, download RSAT if using desktop OS Add-WindowsFeature RSAT-AD-PowerShell Import-Module ActiveDirectory } }function GetScriptDirectory {$invocation= (Get-Variable MyInvocation -Scope 1).Value Split-Path $invocation.MyCommand.Path }function GetDN {param($domain)$names=$domain.Split(".")$bFirst=$trueforeach ($namein$names) {if ($bFirst) {$dn+="DC="+$name$bFirst=$false }else { $dn+=",DC="+$name } }return$dn }function GetDNs {param($domains)$dns=@{}foreach ($domainin$domains) {$dns.Add($domain, (GetDN -domain $domain)) }return$dns }function GetStatusCodeString {param ($code)switch ($code) {$null {$ret="Ping Command Failed"}0 {$ret="Success"}11001 {$ret="Buffer Too Small"}11002 {$ret="Destination Net Unreachable"}11003 {$ret="Destination Host Unreachable"}11004 {$ret="Destination Protocol Unreachable"}11005 {$ret="Destination Port Unreachable"}11006 {$ret="No Resources"}11007 {$ret="Bad Option"}11008 {$ret="Hardware Error"}11009 {$ret="Packet Too Big"}11010 {$ret="Request Timed Out"}11011 {$ret="Bad Request"}11012 {$ret="Bad Route"}11013 {$ret="TimeToLive Expired Transit"}11014 {$ret="TimeToLive Expired Reassembly"}11015 {$ret="Parameter Problem"}11016 {$ret="Source Quench"}11017 {$ret="Option Too Big"}11018 {$ret="Bad Destination"}11032 {$ret="Negotiating IPSEC"}11050 {$ret="General Error"}default {$ret="Ping Failed"} }return$ret }function GetPingResultsFromHashTable {param($ht, $maxConcurrent, $count, $timeout)$bDone=$false$i=0$totalMachines=0$htResults=@{}$dotTime= [System.DateTime]::Nowif ($timeout-eq$null) {$timeout=120} Write-Host ("Sending Ping Command to {0} Machines"-f$ht.Count) -NoNewlineforeach ($namein$ht.GetEnumerator()) {while ((Get-Job -State Running).Count -ge$maxConcurrent) { Start-Sleep -Seconds 1if ($i-ge50) { Write-Host "*"; $i=0 }else { Write-Host "*"-NoNewline; $i++ } }$job= Test-Connection -ComputerName $name.Key.ToString() -Count $count-AsJob$job.name ="ping:{0}"-f$name.Key.ToString()if ([System.DateTime]::Now -gt$dotTime) {$dotTime= ([System.DateTime]::Now).AddSeconds(1)if ($i-ge50) { Write-Host "."; $i=0 }else { Write-Host "."-NoNewline; $i++ } } }#Start time now, exit in case of timeout$timeout= ([System.DateTime]::Now).AddSeconds($timeout)$dotTime= [System.DateTime]::Now$i=0 Write-Host Write-Host "Getting Ping Results"-NoNewlinewhile(!($bDone)) {$results= Get-Job -Name 'ping:*'$bRunning=$falseforeach ($resultin$results) {if ($result.State -ne 'Running') {if ($result.State -eq 'Failed') {#resubmit jobif ($i-ge50) { Write-Host "+"; $i=0 }else { Write-Host "+"-NoNewline; $i++ }$job= Test-Connection -ComputerName $result.Name.ToString().Split(":")[1] -Count $count-AsJob$job.name ="ping:{0}"-f$result.Name.ToString().Split(":")[1] }else { try { $htResults.Add($result.Name.ToString().Split(":")[1], (Receive-Job $result)) } catch {}$totalMachines++ }if ([System.DateTime]::Now -gt$dotTime) {$dotTime= ([System.DateTime]::Now).AddSeconds(1)if ($i-ge50) { Write-Host "."; $i=0 }else { Write-Host "."-NoNewline; $i++ } } try { Remove-Job $result } catch {} }else {$bRunning=$true } }#Check for timeout condition, clean up all jobs if trueif ([System.DateTime]::Now -gt$timeout) {$bDone=$true Write-Host "Timeout reached, removing jobs"$results= Get-Job -Name 'ping:*'foreach ($resultin$results) { Write-Host "RemoveJob:"$result.Name try { Stop-Job $result try { Remove-Job $result-Force } catch {} } catch {} } }#If the timeout hasn't been reached and jobs are still running, loop againif (!($bRunning)) { $bDone=$true } } Write-Host Write-Host ("Received Ping Results From {0} Machines"-f$totalMachines)return$htResults }function ResolveNamesFromPingResults {param($array, $maxConcurrent, $resolveNames, $timeout) try { if ($resolveNames-ne$null) { [bool]$resolveNames= [System.Convert]::ToBoolean($resolveNames) } } catch {}$htResults=@{}if ($resolveNames) {$dotTime= ([System.DateTime]::Now)if ($timeout-eq$null) {$timeout=120}$i=0$scriptBlock= {param($s) try { $ret= [System.Net.DNS]::GetHostEntry($s) } catch {}return$ret } Write-Host ("Resolving DNS Names for {0} Machines"-f$array.Count) -NoNewlineforeach ($namein$array) {while ((Get-Job -State Running).Count -ge$maxConcurrent) { Start-Sleep -Seconds 1if ($i-ge50) { Write-Host "*"; $i=0 }else { Write-Host "*"-NoNewline; $i++ } }$job= Start-Job -ScriptBlock $scriptBlock-ArgumentList $name.NameInList$job.name ="resolve:{0}"-f$name.NameInListif ([System.DateTime]::Now -gt$dotTime) {$dotTime= ([System.DateTime]::Now).AddSeconds(1)if ($i-ge50) { Write-Host "."; $i=0 }else { Write-Host "."-NoNewline; $i++ } } }#Start time now, exit in case of timeout$timeout= ([System.DateTime]::Now).AddSeconds($timeout)$dotTime= ([System.DateTime]::Now)$i=0$bDone=$false Write-Host Write-Host "Getting DNS Results"-NoNewlinewhile(!($bDone)) {$results= Get-Job -Name 'resolve:*'$bRunning=$falseforeach ($resultin$results) {if ($result.State -ne 'Running') {if ($result.State -eq 'Failed') {#resubmit jobif ($i-ge50) { Write-Host "+"; $i=0 }else { Write-Host "+"-NoNewline; $i++ }$job= Start-Job -ScriptBlock $scriptBlock-ArgumentList $result.Name.ToString().Split(":")[1]$job.name ="resolve:{0}"-f$result.Name.ToString().Split(":")[1] }else { try { $htResults.Add($result.Name.ToString().Split(":")[1], (Receive-Job $result)) } catch {continue} }if ([System.DateTime]::Now -gt$dotTime) {$dotTime= ([System.DateTime]::Now).AddSeconds(1)if ($i-ge50) { Write-Host "."; $i=0 }else { Write-Host "."-NoNewline; $i++ } } try { Remove-Job $result-Force} catch {} }else {$bRunning=$true } }#Check for timeout condition, clean up all jobs if trueif ([System.DateTime]::Now -gt$timeout) {$bDone=$true Write-Host "Timeout reached, removing jobs"$results= Get-Job -Name 'resolve:*'foreach ($resultin$results) { Write-Host "RemoveJob:"$result.Name try { Stop-Job $result try { Remove-Job $result-Force } catch {} } catch {} } }#If the timeout hasn't been reached and jobs are still running, loop againif (!($bRunning)) { $bDone=$true } } Write-Host Write-Host ("Received DNS Results From {0} Machines"-f$htResults.Count) }return$htResults }function GetFormattedPingResultsFromHashTable {param($ht)$fResults= New-Object System.Collections.ArrayList$dotTime= ([System.DateTime]::Now)$i=0 Write-Host "Formatting Ping Results"-NoNewLineforeach ($resultin$ht.GetEnumerator()) {#There are multiple pings here if we ping more than once per computer$originalAddress=$result.Key.ToString()$pingCount=0$successCount=0$status= 'Ping Job Failed'$pingedFrom= 'Ping Job Failed'$successPercentage=0 try { $pings=$result.Value.Count } catch { $pings=0 }if ($pings-gt0) {$status= GetStatusCodeString -code $result.Value[$pings-1].StatusCode$pingedFrom=$result.Value[$pings-1].PSComputerName }foreach ($pingin$result.Value) {$pingCount++if ($ping.StatusCode -eq0) { $successCount++ }#If you try to get the IPv4Address or IPv6Address it slows down this loop significantly }#Calculate percentageif ($pingCount-ne0) { $successPercentage= ($successCount/$pingCount) *100 } else { $successPercentage=0 }#Add to array$o= New-Object PSObject -Property @{ NameInList =$originalAddress PingedFrom =$pingedFrom SuccessPercentage =$successPercentage LastPingStatus =$status } [void]$fResults.Add($o)if ([System.DateTime]::Now -gt$dotTime) {$dotTime= ([System.DateTime]::Now).AddSeconds(1)if ($i-ge50) { Write-Host "."; $i=0 }else { Write-Host "."-NoNewline; $i++ } } } Write-Host Write-Host ("Formatted Ping Results for {0} Machines"-f$fResults.Count)return$fResults }function GetFormattedPingAndDNSResults {param($pingResults, $dnsResults)if ($dnsResults.Count -ne0) { Write-Host "Formatting DNS Results"-NoNewLine$dotTime= ([System.DateTime]::Now)$i=0foreach ($pingin$pingResults) {$dns=$dnsResults.Get_Item($ping.NameInList)if ($dns-ne$null) {$bFirst=$trueforeach ($ipin$dns.AddressList) {if ($bFirst){ $ipList=$ip }else { $ipList+="|"+$ip } }$fqdn=$dns.HostName }else {$ipList=$null$fqdn= 'No DNS Entry Found' }$ping| Add-Member -MemberType NoteProperty -Name NameFromDNS -value $fqdn-Force$ping| Add-Member -MemberType NoteProperty -Name IPAddressListFromDNS -value $ipList-Forceif ([System.DateTime]::Now -gt$dotTime) {$dotTime= ([System.DateTime]::Now).AddSeconds(1)if ($i-ge50) { Write-Host "."; $i=0 }else { Write-Host "."-NoNewline; $i++ } } } Write-Host Write-Host ("Formatted DNS Results for {0} Machines"-f$pingResults.Count) }return$pingResults }function GetTimeSpanStringInMinutesAndSeconds {param($startTime, $endTime)$time=$startTime.Subtract($endTime)$minutes=$time.ToString().Split(":")[1]$seconds=$time.ToString().Split(":")[2].Split(".")[0]$timeSpan="{0} Minutes and {1} Seconds"-f$minutes, $secondsreturn$timeSpan }function GetSuccessPingCount {param($results)$successCount=0foreach ($resultin$results) {if ($result.SuccessPercentage -gt0) { $successCount++ } }return$successCount }function GetDNSNamesResolvedCount {param($results)$namesResolved=0foreach ($resultin$results) {if ($result.IPAddressListFromDNS -ne$null) { $namesResolved++ } }return$namesResolved }function GetPercentageAsString {param($n1, $n2)if ($n1-ne0) { $percentage= ($n1/$n2) *100 } else { $percentage=0 }$percentage= ("{0:N0}"-f$percentage) +"%"return$percentage }function GetOSCountsPerDomain {param($dns, $enabled, $daysOld)$osCounts=@{}$cutOffDate= ((Get-Date).Adddays(-($daysOld))).ToFileTime() Write-Host "Getting Data"-NoNewline -ForegroundColor Yellow$filter="(PwdLastSet -gt {0}) -and (Enabled -eq '{1}') -and (OperatingSystem -like '{2}')"-f$cutOffDate, $enabled, 'Windows XP*'foreach ($domainin$dns.GetEnumerator()) {$domains=@{}$htComputers=@{} Write-Host "."-NoNewline -ForegroundColor Yellow$computers= Get-ADComputer -Filter$filter-SearchBase $domain.Value -Server $domain.Key -Properties CN, Description, DistinguishedName, DNSHostName, Name, OperatingSystem, OperatingSystemVersionforeach ($computerin$computers) { try { $htComputers.Add($computer.DNSHostName, $computer) } catch {}if ($computer.OperatingSystem -eq$null) { $os= 'NULL'}else { $os=$computer.OperatingSystem }if ($computer.OperatingSystemVersion -eq$null) { $osver= 'NULL'}else { $osver=$computer.OperatingSystemVersion } try { $domains.Add(($os+" - "+$osver), 1) } catch { $domains.Set_Item(($os+" - "+$osver), ($domains.Get_Item($os+" - "+$osver))+1) } }$osCounts.Add($domain.Key, $domains)#Ping Computers$results= GetPingResultsFromHashTable -ht $htComputers-maxConcurrent 100-count 4-timeout $TimeoutInSeconds90#Format ping results into an array of objects$formattedPingResults= GetFormattedPingResultsFromHashTable -ht $results#Resolve DNS Names if specified$dnsResults= ResolveNamesFromPingResults -array $formattedPingResults-maxConcurrent 5-resolveNames $true-timeout 90#Format DNS results by adding them to the ping results$formattedPingResults= GetFormattedPingAndDNSResults -pingResults $formattedPingResults-dnsResults $dnsResults#Update master list with ping data$fResults= New-Object System.Collections.ArrayListforeach($formattedPingResultin$formattedPingResults) {$o=$htComputers.Get_Item($formattedPingResult.NameInList)$o| Add-Member -MemberType NoteProperty -Name PingSuccessPercentage -Value $formattedPingResult.SuccessPercentage -Force$o| Add-Member -MemberType NoteProperty -Name NameFromDNS -Value $formattedPingResult.NameFromDNS -Force$o| Add-Member -MemberType NoteProperty -Name IPAddressFromDNS -Value $formattedPingResult.IPAddressListFromDNS -Force [void]$fResults.Add($o) }#Output data to csv$outputFile="{0}.csv"-f$domain.Key$path= Join-Path ($Global:CurrentDirectory) $outputFile$Global:CSVs.Add($domain.Key,$path)$fResults| select DNSHostName, CN, Name, DistinguishedName, Description, OperatingSystem, OperatingSystemVersion, NameFromDNS, IPAddressFromDNS, PingSuccessPercentage | sort DNSHostName | Export-Csv $path-NoTypeInformation Write-Host Write-Host "Detailed Output File: $path" } Write-Hostreturn$osCounts }function CreateExcelFile {param ($osCounts)#Create the Excel file$oExcel= New-Object -ComObject Excel.Application$outputFile="GetXPWorkstations.xlsx"$path= Join-Path ($Global:CurrentDirectory) $outputFile$oExcel.Visible =$true$oWorkbook=$oExcel.Workbooks.Add()#Create a tab for each domainforeach ($csvin$Global:CSVs.GetEnumerator()) {$oCSV=$oExcel.Workbooks.Open($csv.Value)$oCSV.ActiveSheet.Move($oWorkBook.ActiveSheet) }$oWorkbook.SaveAs($path) }function DisplayOutput {param($osCounts) Write-Hostforeach ($osCountin$osCounts.GetEnumerator()) { Write-Host $OSCount.Key -ForegroundColor Green$osCount.Value.GetEnumerator() | Sort-Object Value -Descending | Format-Table -AutoSize } }#Main$Global:CurrentDirectory= (GetScriptDirectory)$Global:CSVs=@{}#Import AD Module for PowerShellImportADModule#Get list of domains from current forest$Domains= (Get-ADForest).domains#Get hash table of domains and distinguished names from current forest$DNs= GetDNs -domains $Domains#Get OS counts per domain (specify age here)$OSCounts= GetOSCountsPerDomain -dns $DNs-enabled $true-daysOld 31#Convert output to Excel FileCreateExcelFile -osCounts $OSCounts#Display ResultsDisplayOutput -osCounts $OSCounts
Clik here to view.
