The first step is to install the CleanupMonster module
Install-Module CleanupMonster -Force -Verbose
Then, it's a matter of running a single function, Invoke-ADSIDHistoryCleanup. Below is an example of how you can schedule a slow, controlled cleanup over time. Please notice the use of specific SIDHistoryDomains, RemoveLimits, and other settings.
# Prepare splat
$invokeADSIDHistoryCleanupSplat = @{
Verbose = $true
WhatIf = $true
IncludeSIDHistoryDomain = @(
# 'S-1-5-21-3661168273-3802070955-2987026695'
'S-1-5-21-853615985-2870445339-3163598659'
)
RemoveLimitSID = 1
RemoveLimitObject = 2
SafetyADLimit = 1
ShowHTML = $true
Online = $true
LogPath = "$PSScriptRoot\ProcessedSIDHistory.log"
ReportPath = "$PSScriptRoot\ProcessedSIDHistory.html"
DataStorePath = "$PSScriptRoot\ProcessedSIDHistory.xml"
}
# Run the script
$Output = Invoke-ADSIDHistoryCleanup @invokeADSIDHistoryCleanupSplat
$Output | Format-Table -AutoSize
# Lets send an email
$EmailBody = $Output.EmailBody
# Send email with Microsoft Graph and using Mailozaurr module
Connect-MgGraph -Scopes 'Mail.Send' -NoWelcome
Send-EmailMessage -To 'przemyslaw.klys@test.pl' -From 'przemyslaw.klys@test.pl' -MgGraphRequest -Subject "Automated SID Cleanup Report" -Body $EmailBody -Priority Low -Verbose
Once you run the script, an HTML report is generated, and you can also send an email with pre-prepared content. Remember that the function has multiple other parameters that help delete only what you want. Please DO NOT RUN the script without first testing it out on TEST ENVIRONMENT and understanding what happens and how it affects the environment! This function is DANGEROUS! Here's a help file for the function that cleans up SID History on a global level.
NAME
Invoke-ADSIDHistoryCleanup
SYNOPSIS
Cleans up SID history entries in Active Directory based on various filtering criteria.
SYNTAX
Invoke-ADSIDHistoryCleanup [[-Forest] <String>] [[-IncludeDomains] <String[]>] [[-ExcludeDomains] <String[]>] [[-IncludeOrganizationalUnit]
<String[]>] [[-ExcludeOrganizationalUnit] <String[]>] [[-IncludeSIDHistoryDomain] <String[]>] [[-ExcludeSIDHistoryDomain] <String[]>]
[[-RemoveLimitSID] <Nullable`1>] [[-RemoveLimitObject] <Nullable`1>] [[-IncludeType] <String[]>] [[-ExcludeType] <String[]>] [[-ReportPath] <String>]
[[-DataStorePath] <String>] [-ReportOnly] [[-LogPath] <String>] [[-LogMaximum] <Int32>] [-LogShowTime] [[-LogTimeFormat] <String>] [-Suppress]
[-ShowHTML] [-Online] [-DisabledOnly] [[-SafetyADLimit] <Nullable`1>] [-DontWriteToEventLog] [-WhatIf] [-Confirm] [<CommonParameters>]
DESCRIPTION
This function identifies and removes SID history entries from AD objects based on specified filters.
It can target internal domains (same forest), external domains (trusted), or unknown domains.
The function allows for detailed reporting before making any changes.
PARAMETERS
-Forest <String>
The name of the forest to process. If not specified, uses the current forest.
-IncludeDomains <String[]>
An array of domain names to include in the cleanup process.
-ExcludeDomains <String[]>
An array of domain names to exclude from the cleanup process.
-IncludeOrganizationalUnit <String[]>
An array of organizational units to include in the cleanup process.
-ExcludeOrganizationalUnit <String[]>
An array of organizational units to exclude from the cleanup process.
-IncludeSIDHistoryDomain <String[]>
An array of domain SIDs to include when cleaning up SID history.
-ExcludeSIDHistoryDomain <String[]>
An array of domain SIDs to exclude when cleaning up SID history.
-RemoveLimitSID <Nullable`1>
Limits the total number of SID history entries to remove.
-RemoveLimitObject <Nullable`1>
Limits the total number of objects to process for SID history removal. Defaults to 1 to prevent accidental mass deletions.
-IncludeType <String[]>
Specifies which types of SID history to include: 'Internal', 'External', or 'Unknown'.
Defaults to all three types if not specified.
-ExcludeType <String[]>
Specifies which types of SID history to exclude: 'Internal', 'External', or 'Unknown'.
-ReportPath <String>
The path where the HTML report should be saved. Used with the -Report parameter.
-DataStorePath <String>
Path to the XML file used to store processed SID history entries.
-ReportOnly [<SwitchParameter>]
If specified, only generates a report without making any changes.
-LogPath <String>
The path to the log file to write.
-LogMaximum <Int32>
The maximum number of log files to keep.
-LogShowTime [<SwitchParameter>]
If specified, includes the time in the log entries.
-LogTimeFormat <String>
The format to use for the time in the log entries.
-Suppress [<SwitchParameter>]
Suppresses the output of the function and only returns the summary information.
-ShowHTML [<SwitchParameter>]
If specified, shows the HTML report in the default browser.
-Online [<SwitchParameter>]
If specified, uses online resources in HTML report (CSS/JS is loaded from CDN). Otherwise local resources are used (bigger HTML file).
-DisabledOnly [<SwitchParameter>]
Only processes objects that are disabled.
-SafetyADLimit <Nullable`1>
Stops processing if the number of objects with SID history in AD is less than the specified limit.
-DontWriteToEventLog [<SwitchParameter>]
-WhatIf [<SwitchParameter>]
Shows what would happen if the function runs. The SID history entries aren't actually removed.
-Confirm [<SwitchParameter>]
<CommonParameters>
This cmdlet supports the common parameters: Verbose, Debug,
ErrorAction, ErrorVariable, WarningAction, WarningVariable,
OutBuffer, PipelineVariable, and OutVariable. For more information, see
about_CommonParameters (https:/go.microsoft.com/fwlink/?LinkID=113216).
-------------------------- EXAMPLE 1 --------------------------
PS C:\>Invoke-ADSIDHistoryCleanup -Forest "contoso.com" -IncludeType "External" -ReportOnly -ReportPath "C:\Temp\SIDHistoryReport.html" -WhatIf
Generates a report of external SID history entries in the contoso.com forest without making any changes.
-------------------------- EXAMPLE 2 --------------------------
PS C:\>Invoke-ADSIDHistoryCleanup -IncludeDomains "domain1.local" -IncludeType "Internal" -RemoveLimitSID 2 -WhatIf
Removes up to 2 internal SID history entries from objects in domain1.local.
-------------------------- EXAMPLE 3 --------------------------
PS C:\>Invoke-ADSIDHistoryCleanup -ExcludeSIDHistoryDomain "S-1-5-21-1234567890-1234567890-1234567890" -WhatIf -RemoveLimitObject 2
Shows what SID history entries would be removed while excluding entries from the specified domain SID. Limits the number of objects to process to 2.
-------------------------- EXAMPLE 4 --------------------------
PS C:\># Prepare splat
$invokeADSIDHistoryCleanupSplat = @{
Verbose = $true
WhatIf = $true
IncludeSIDHistoryDomain = @(
'S-1-5-21-3661168273-3802070955-2987026695'
'S-1-5-21-853615985-2870445339-3163598659'
)
IncludeType = 'External'
RemoveLimitSID = 1
RemoveLimitObject = 2
SafetyADLimit = 1
ShowHTML = $true
Online = $true
DisabledOnly = $true
#ReportOnly = $true
LogPath = "C:\Temp\ProcessedSIDHistory.log"
ReportPath = "$PSScriptRoot\ProcessedSIDHistory.html"
DataStorePath = "$PSScriptRoot\ProcessedSIDHistory.xml"
}
# Run the script
$Output = Invoke-ADSIDHistoryCleanup @invokeADSIDHistoryCleanupSplat
$Output | Format-Table -AutoSize
# Lets send an email
$EmailBody = $Output.EmailBody
Connect-MgGraph -Scopes 'Mail.Send' -NoWelcome
Send-EmailMessage -To 'przemyslaw.klys@test.pl' -From 'przemyslaw.klys@test.pl' -MgGraphRequest -Subject "Automated SID Cleanup Report" -Body
$EmailBody -Priority Low -Verbose
REMARKS
To see the examples, type: "get-help Invoke-ADSIDHistoryCleanup -examples".
For more information, type: "get-help Invoke-ADSIDHistoryCleanup -detailed".
For technical information, type: "get-help Invoke-ADSIDHistoryCleanup -full".