I was recently asked to get the count of all Windows XP systems in a forest. I decided to do a bit more work and make a modular script that gets all the operating systems and lists the count of each one for every domain in a forest. This version of the script only gets computers where the account is enabled and PwdLastSet has been modified in the last 30 days.

Requirements

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.

Instructions

From a domain joined machine simply open PowerShell (as an admin if you want it to automatically add the RSAT role) and run the script .\GetForestOSCounts.ps1.

Output

*Each "dot” after Getting Data represents a domain, so in large forests this may take a while to run

Getting Data………

west.contoso.com

Name Value
Windows 8 Pro – 6.2 (9200) 5,000
Windows 7 Enterprise – 6.1 (7600) 2,200
Windows 7 Professional – 6.1 (7601) 650
Windows Server 2012 R2 Datacenter – 6.3 (9600) 200
Windows Server 2008 R2 Enterprise – 6.1 (7600) 75

east.contoso.com

Name Value
Windows 8 Pro – 6.2 (9200) 10,000
Windows 7 Enterprise – 6.1 (7600) 2,800
Windows 7 Professional – 6.1 (7601) 1,500
Windows Server 2012 R2 Datacenter – 6.3 (9600) 20
Windows Server 2008 R2 Enterprise – 6.1 (7600) 5

central.contoso.com

Name Value
Windows 8 Pro – 6.2 (9200) 1,000
Windows 7 Enterprise – 6.1 (7600) 600
Windows 7 Professional – 6.1 (7601) 500
Windows Server 2012 R2 Datacenter – 6.3 (9600) 150
Windows Server 2008 R2 Enterprise – 6.1 (7600) 50

Code

#Functions function ImportADModule { Import-Module ActiveDirectory if (!($?)) { #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 GetDN { param($domain) $names = $domain.Split(".") $bFirst = $true foreach ($name in $names) { if ($bFirst) { $dn += "DC=" + $name $bFirst = $false } else { $dn += ",DC=" + $name } } return $dn } function GetDNs { param($domains) $dns = @{} foreach ($domain in $domains) { $dns.Add($domain, (GetDN -domain $domain)) } return $dns } 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}')" -f $cutOffDate, $enabled foreach ($domain in $dns.GetEnumerator()) { $i = 0 $domains = @{} Write-Host "." -NoNewline -ForegroundColor Yellow $computers = Get-ADComputer -Filter $filter -SearchBase $domain.Value -Server $domain.Key -Properties OperatingSystem, OperatingSystemVersion foreach ($computer in $computers) { 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) } Write-Host return $osCounts } function DisplayOutput { param($osCounts) Write-Host foreach ($osCount in $osCounts.GetEnumerator()) { Write-Host $OSCount.Key -ForegroundColor Green $osCount.Value.GetEnumerator() | Sort-Object Value -Descending | Format-Table -AutoSize } } #Main #Import AD Module for PowerShell ImportADModule #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 30 #Display Results DisplayOutput -osCounts $OSCounts