Get Counts of all Operating Systems in a Forest Per Domain
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