One of the most common tasks of Active Directory cleanup is to perform a discovery of domain joined computers by their Operating System types. As of 2020, legacy versions such as Windows XP, 2003, and 2008 should be decommissioned from the environment as soon as possible. Here are some codes to gather this information:

# checkActiveDirectoryForUnsupportedWindows.ps1

# Define current unsupported OS build number as this may change over time
$unsupportedBuild=9200
$daysLimit=30
$csvExportFile="C:\UnsupportedWindowsList_$env:USERDNSDOMAIN.csv"

# Get a detailed report of machines and OS versions
$limitTimestamp=(get-date).AddDays(-$daysLimit).ToFileTime()
$enabledWindows=Get-ADComputer -Filter "OperatingSystem -like 'Windows*' -and Enabled -eq 'True' -and LastLogon -ge $limitTimestamp" -Properties OperatingSystemVersion,OperatingSystem,LastLogon
$detailedList=$enabledWindows|select-object @{Name='Name';e={$_.DNSHostName}},
    @{name='versionNumber';e={[int]([regex]::match($_.OperatingSystemVersion,"\((\d+)\)").groups[1].Value) }},
    OperatingSystem, `
    @{name='lastLogon';e={[DateTime]::FromFileTime($_.LastLogon)}},
    @{name='container';e={$splitName=$_.DistinguishedName -split ',';$splitName[1..$splitName.count] -join ','}}
$unsupportedList=$detailedList|?{$_.versionNumber -le $unsupportedBuild}|sort -Property versionNumber
$summary=$unsupportedList|group OperatingSystem|select Name,Count|sort -Property Name

write-host "Summary of unsupported machines in $env:USERDNSDOMAIN`:`r`n$(($summary|out-string).trim())"
$unsupportedList|Export-Csv -Path $csvExportFile -NoTypeInformation
# Previous version

function convertWindowsBuildToName($number){
$osName=switch ($number){
19043{'Windows 10 (codenamed "21H1")'}
19041{'Windows 10 (2004) (codenamed "20H1")'}
18363{'Windows 10 (1909)'}
18362{'Windows 10 (1903)'}
17763{'Windows 10 (1809)'}
17134{'Windows 10 (1803)'}
16299{'Windows 10 (1709)'}
15063{'Windows 10 (1703)'}
14393{'Windows 10 (1607)'}
10586{'Windows 10 (1511)'}
10240{'Windows 10'}
9600{'Windows 8.1'}
9200{'Windows 8'}
7601{'Windows 7 SP1'}
7600{'Windows 7'}
6003{'Windows Vista SP3'}
6002{'Windows Vista SP2'}
6001{'Windows Vista SP1'}
6000{'Windows Vista'}
3790{'Windows XP3'}
2600{'Windows XP2'}
2195{'Windows XP'}
default{'Unknown'}
}
return $osName
}


$osVersions=Get-ADComputer -Filter "OperatingSystem -like 'Windows*'" -Properties OperatingSystemVersion | group OperatingSystemVersion | select Name,Count
$sortedOsVersions=$osVersions|Sort-Object @{e={ [int](.{[void]($_.Name -match "\((\d+)\)");return $matches[1]}) }}
$sortedOsVersions|%{$_.Name = .{            
             $number=[int](.{[void]($_.Name -match "\((\d+)\)");return $matches[1]});
             convertWindowsBuildToName $number
             }
             }
$sortedOsVersions

<#
Name              Count
----              -----
Windows 7 SP1      6549
Windows 8.1         156
Windows XP3          53
Windows XP           86
Windows 10 (1909)  4647
Windows XP2          32
Windows 8            17
Windows 7            10
Windows Vista SP2     6
Windows 10 (1809)   856
Windows 10 (1703)    87
Windows 10 (1607)    64
Windows 10            4
Windows 10 (1903)  9967
Windows 10 (1511)   745
Windows 10 (1803)   593
Windows 10 (1709)   731
#>
# Scan for all Windows machines in Active Directory

Get-ADComputer -Filter "OperatingSystem -like 'Windows*'" -Properties OperatingSystem | group operatingsystem | select Name,Count | sort name

PS C:\Windows\system32> Get-ADComputer -Filter "OperatingSystem -like 'Windows*'" -Properties OperatingSystem | group operatingsystem | sort name

Count Name Group
----- ---- -----
270 Windows 10 Enterprise
1 Windows 10 Enterprise ...
18 Windows 10 Enterprise N
110 Windows 10 Pro
1 Windows 10 Pro Education
45 Windows 7 Enterprise
49 Windows 7 Professional
1 Windows 7 Ultimate
1 Windows 8 Pro
168 Windows 8.1 Enterprise
13 Windows 8.1 Pro
1 Windows Embedded Standard
2 Windows Server 2003
1 Windows Server 2008 R2...
5 Windows Server 2008 R2...
6 Windows Server 2008 R2...
8 Windows Server 2012 Da...
75 Windows Server 2012 R2...
15 Windows Server 2012 R2...
9 Windows Server 2016 Da...
16 Windows Server 2016 St...
13 Windows Server 2019 Da...
23 Windows XP Professional
# Narrow down to systems requiring removal such as Windows 2003 & XP
$win2003Servers=(Get-ADComputer -Filter "OperatingSystem -like 'Windows Server 2003'" -Properties OperatingSystem).DNSHostname
$winXPMachines=(Get-ADComputer -Filter "OperatingSystem -like 'Windows XP*'" -Properties OperatingSystem).DNSHostname

$win2003Servers|write-host
$winXPMachines|write-host

A quick sample output:

PS C:\Windows\system32> Get-ADComputer -Filter "OperatingSystem -like 'Windows*'" -Properties OperatingSystem | group operatingsystem | select Name,Count |sort name

Name Count
---- -----
Windows 10 Pro 2
Windows 7 Enterprise 1
Windows 7 Professional 3
Windows Server 2003 20
Windows Server 2008 R2 Enterprise 8
Windows Server 2008 R2 Standard 35
Windows Server 2012 R2 Datacenter 6
Windows Server 2012 R2 Standard 143
Windows Server 2012 Standard 9
Windows Server 2016 Datacenter 2
Windows Server 2016 Standard 12
Windows Server 2019 Datacenter 24
Windows Server 2019 Standard 19
Windows ServerĀ® 2008 Standard 9
# Export a the machine list as a CSV file

$windowsMachines=Get-ADComputer -Filter "OperatingSystem -like 'Windows*'" -Properties OperatingSystem | group operatingsystem| select -ExpandProperty group|select OperatingSystem,DNSHostName,DistinguishedName |sort -Property OperatingSystem

$windowsMachines|Export-Csv -path c:\WindowsNodes.csv -NoTypeInformation

Discovery of Computer Resources

# Purpose: this script uses a list of computer names to discover information about each of them (IPs, Machine Type, CPU, Memory, and Disks)

$computerNames=@(
'SHERVER01',
'SHERVER03',
'SHERVER03'
)

$GLOBAL:computerObjects=@()

$scriptName=$MyInvocation.MyCommand.Path
$scriptPath=Split-Path -Path $scriptName

function systemsDiscovery{
#for ($i=0;$i -le $computers.length;$i++){
foreach($name in $computerNames){
"Scanning $name ..."
$computerObject=New-Object PSObject
$ips="Unknown";
$machineType="Unknown";
$cpu="Unknown";
$cpuLoad=0;
$memory="Unknown";
$volumes="Unknown";
try{
$ips=[System.Net.Dns]::GetHostAddresses($name)|?{$_.AddressFamily -eq "InterNetwork"}|%{$_.IPAddressToString;}
$machineModel=(Get-WmiObject -Class Win32_ComputerSystem -ComputerName $name -ea stop).Model
$cpu = Get-WmiObject -computername $name win32_processor -ea stop |select Name,NumberOfCores
$cpuLoad = Get-WmiObject -computername $name win32_processor -ea stop| Measure-Object -property LoadPercentage -Average | Select @{Name="CurrentLoad";Expression={"{0:N2} %" -f ($_.Average)}}
$memory = gwmi -Class win32_operatingsystem -computername $name -ea stop| select @{Name="Memory";Expression={"{0:N2} GB" -f ($_.TotalVisibleMemorySize / 1048576)}},@{Name = "MemoryUsage"; Expression = {"{0:N2} %" -f ((($_.TotalVisibleMemorySize - $_.FreePhysicalMemory)*100)/ $_.TotalVisibleMemorySize) }}
#$volumes = (gwmi -Class win32_volume -computername $name -ea stop| ?{$_.DriveLetter -ne $isnull}|Select-object DriveLetter,Capacity,@{Name = "PercentFree"; Expression = {"{0:N2}" -f (($_.FreeSpace / $_.Capacity)*100) } } | Out-String ).TrimEnd()
$volumes = (gwmi -Class win32_volume -computername $name -Filter "DriveType!=5" -ea stop| ?{$_.DriveLetter -ne $isnull}|Select-object DriveLetter,@{Name="Capacity";Expression={"{0:N2} GiB" -f ($_.Capacity/1073741824)}},@{Name = "PercentFree"; Expression = {"{0:N2} %" -f (($_.FreeSpace / $_.Capacity)*100) } } | Out-String ).Trim()
}
catch{
$machineModel="Unknown";
$machineType="Unknown";
if(!($ips)){$ips="Unknown";}
if(!($cpu)){$cpu="Unknown";}
if(!($memory)){$memory="Unknown";}
if(!($volumes)){$volumes="Unknown";}
Continue;
}
finally{
switch -wildcard ($machineModel){
"*Virtual*" {$machineType="VMWare Virtual Machine";}
"*HVM*" {$machineType="AWS Virtual Machine";}
"Unknown" {$machineType="Physical Machine";}
}
$computerObject | Add-Member -MemberType NoteProperty -Name "Name" -Value $name
$computerObject | Add-Member -MemberType NoteProperty -Name "ips" -Value $ips
$computerObject | Add-Member -MemberType NoteProperty -Name "machineType" -Value $machineType
$computerObject | Add-Member -MemberType NoteProperty -Name "cpu" -Value $cpu,$cpuLoad
$computerObject | Add-Member -MemberType NoteProperty -Name "memory" -Value $memory
$computerObject | Add-Member -MemberType NoteProperty -Name "volumes" -Value $volumes
$computerObject
$GLOBAL:computerObjects+=,$computerObject
}
}
}

systemsDiscovery;

"Exporting results to a CSV file in the current path of $scriptPath`..."
$computerObjects|Export-Csv -Path "$scriptPath`\systemsDiscovery.csv" -NoTypeInformation