# Create array of a strings (strongly-typed) to hold currently linked/enabled GPO IDs
[string[]]$appliedgpoids = @();
# Create array of a strings (strongly-typed) to hold all GPO IDs
[string[]]$allgpoids = @();
# Create array of a strings (strongly-typed) to hold GPO IDs not in-use
[string[]]$notinusegpoids = @();
# Define regex to find GUIDs of GPOs (the text between curly braces)
$gpoidregex = "[^{\}]+(?=})";
# Define regex to find gpLink status (the text between ';' and ']')
$gpolinkoptionsregex = "[^{;]+(?=\])";
# Get Distinguished Names
$root = [ADSI]"LDAP://RootDSE"
$domainName = ([System.DirectoryServices.ActiveDirectory.Domain]::GetComputerDomain()).Name
$domaindn = "DC=" + ($domainName -replace "\.", ",DC=")
$sitesdn = "cn=sites," + ($root.configurationnamingcontext).tostring();
#----------------[ Get all site-linked GPOs ]----------------
# Get AD site
$sites = get-adobject -ldapfilter "(objectclass=site)" -searchbase $sitesdn -properties gplink, gpoptions;
# Loop through each site found
foreach ($site in $sites) {
# Get GPOs, by GUID, linked to the site
$sitegpos = select-string -pattern $gpoidregex -input $site.gplink -allmatches;
$sitegposlinkoptions = select-string -pattern $gpolinkoptionsregex -input $site.gplink -allmatches;
# If linked GPOs are found
if ($sitegpos -ne $null) {
# Counter to track current item index
$foreachcount = 0;
# Loop through each linked GPO ID found and add to global in-use list
foreach ($sitegpo in $sitegpos.matches) {
# Check that the GPO link isn't disabled
if ((($sitegposlinkoptions.matches)[$foreachcount].value -eq 0) -or (($sitegposlinkoptions.matches)[$foreachcount].value -eq 2)) {
$appliedgpoids += $sitegpo.value;
}
# Increment counter
$foreachcount++;
}
}
}
#----------------[ Get all domain-linked GPOs ]----------------
# Get domain info
$domain = get-adobject $domaindn -properties gplink, gpoptions;
# Get GPOs, by GUID, linked to the domain
$domaingpos = select-string -pattern $gpoidregex -input $domain.gplink -allmatches;
$domaingposlinkoptions = select-string -pattern $gpolinkoptionsregex -input $domain.gplink -allmatches;
# If linked GPOs are found
if ($domaingpos -ne $null) {
# Counter to track current item index
$foreachcount = 0;
# Loop through each linked GPO ID found and add to global in-use list
foreach ($domaingpo in $domaingpos.matches) {
# Check that the GPO link isn't disabled
if ((($domaingposlinkoptions.matches)[$foreachcount].value -eq 0) -or (($domaingposlinkoptions.matches)[$foreachcount].value -eq 2)) {
$appliedgpoids += $domaingpo.value;
}
# Increment counter
$foreachcount++;
}
}
#----------------[ Get all OU-linked GPOs ]----------------
# Get all OUs
$ous = get-adorganizationalunit -filter * -properties gplink, gpoptions;
# Loop through each OU found
foreach ($ou in $ous) {
# Get GPOs, by GUID, linked to the OU
$ougpos = select-string -pattern $gpoidregex -input $ou.gplink -allmatches;
$ougposlinkoptions = select-string -pattern $gpolinkoptionsregex -input $ou.gplink -allmatches;
if ($ougpos -ne $null) {
# Counter to track current item index
$foreachcount = 0;
# Loop through each linked GPO ID found and add to global in-use list
foreach ($ougpo in $ougpos.matches) {
# Check that the GPO link isn't disabled
if ((($ougposlinkoptions.matches)[$foreachcount].value -eq 0) -or (($ougposlinkoptions.matches)[$foreachcount].value -eq 2)) {
$appliedgpoids += $ougpo.value;
}
# Increment counter
$foreachcount++;
}
}
}
#----------------[ The rest ]----------------
# Remove duplicates from in-use GPO list
$appliedgpoids = $appliedgpoids | select -unique;
# Extract all GPOs
$rawgpos = get-gpo -domain $domainName -all;
# Loop through each GPO found
foreach ($rawgpo in $rawgpos) {
# Add ID for each GPO into array of strings (for later comparison)
$allgpoids += $rawgpo.id;
# Check Security Filtering (at least one setting with "GPOApply")
$gpopermissions = get-gppermissions -all -guid $rawgpo.id.guid -DomainName $domainName | where {$_.permission -eq "gpoapply"};
# If a GPOApply permission is not found, add to master list of not in-use GPO IDs
if ($gpopermissions -eq $null) {
$notinusegpoids += $rawgpo.id.guid;
}
}
# Calculate list of GPO IDs not in-use (all GPOs minus actively linked GPOs)
$notinusegpoids += compare-object -referenceobject $allgpoids -differenceobject $appliedgpoids -passthru | where {$_.sideindicator -eq "<="};
# Find disabled GPOs and add to master list of not in-use GPO IDs
$disabledgpos = $rawgpos | where {$_.gpostatus -eq "allsettingsdisabled"};
# Loop through each disabled GPO found and add to master list of not in-use GPO IDs
foreach ($disabledgpo in $disabledgpos) {
$notinusegpoids += $disabledgpo.id.guid;
}
# Remove duplicates from not in-use GPO IDs
$notinusegpoids = $notinusegpoids | select -unique;
# Get GPO info for each GPO ID not in use (display name, etc)
$notinusegpos = $rawgpos | where {$_.id -in $notinusegpoids};
# Display GPOs not in-use, by Display Name
$notinusegpos | sort displayname | select displayname;