CleanupMonster
is a PowerShell module to that helps you clean up Active Directory.
It has multiple functionalities currently & planned:
- Cleanup stale Computer objects from Active Directory
- Cleanup SID History from Active Directory
- Cleanup stale User objects from Active Directory
- Cleanup stale Group objects from Active Directory
- Cleanup GMSA/MSA objects from Active Directory
There are 2 blog posts that explain how to use the module:
- Mastering Active Directory Hygiene: Automating Stale Computer Cleanup with CleanupMonster
- Mastering Active Directory Hygiene: Automating SID History Cleanup with CleanupMonster
The solution is really thought through and has many options to customize it to your needs. It's a complete solution for cleaning up Active Directory. Please make sure to run this module with proper permissions or you may get wrong results. By default Active Directory domain allows a standard user to read LastLogonDate and LastPasswordSet attributes. If you have changed those settings you may need to run the module with elevated permissions even for reporting needs.
If you find this project helpful, please consider supporting its development. Your sponsorship will help the maintainers dedicate more time to maintenance and new feature development for everyone.
It takes a lot of time and effort to create and maintain this project. By becoming a sponsor, you can help ensure that it stays free and accessible to everyone who needs it.
To become a sponsor, you can choose from the following options:
Your sponsorship is completely optional and not required for using this project. We want this project to remain open-source and available for anyone to use for free, regardless of whether they choose to sponsor it or not.
If you work for a company that uses our .NET libraries or PowerShell Modules, please consider asking your manager or marketing team if your company would be interested in supporting this project. Your company's support can help us continue to maintain and improve this project for the benefit of everyone.
Thank you for considering supporting this project!
Install-Module -Name CleanupMonster -Force -Verbose
The first thing you should do is to run the module in a report only mode. It will show you how many computers are there to disable and delete.
$Output = Invoke-ADComputersCleanup -WhatIf -ReportOnly -Disable -Delete -ShowHTML
$Output
Keep in mind it works with default values such as 180 days for LastLogonDate and LastPasswordSet. You can change those values by using parameters.
This is a sample script that you can use to run the module interactively. It's good idea to run it interactively first to clean your AD and then run it in a scheduled task.
# this is a fresh run and it will try to disable computers according to it's defaults
$Output = Invoke-ADComputersCleanup -Disable -WhatIfDisable -ShowHTML
$Output
When you run cleanup the module will deliver HTML report on every run. It will show you:
- Devices in Current Run (Actioned)
- Devices in Previous Runs (History)
- Devices on Pending List (Pending deletion)
- All Devices (All) remaining
Another example with log settings and custom report path
# this is a fresh run and it will try to delete computers according to it's defaults
$Output = Invoke-ADComputersCleanup -Delete -WhatIfDelete -ShowHTML -LogPath $PSScriptRoot\Logs\DeleteComputers_$((Get-Date).ToString('yyyy-MM-dd_HH_mm_ss')).log -ReportPath $PSScriptRoot\Reports\DeleteComputers_$((Get-Date).ToString('yyyy-MM-dd_HH_mm_ss')).html
$Output
This is a sample script that you can use to run the module in a scheduled task. It's a good idea to run it as a scheduled task as it will log all the actions and you can easily review them. It's very advanced with many options and you can easily customize it to your needs.
# Run the script
$Configuration = @{
Disable = $true
DisableNoServicePrincipalName = $null
DisableIsEnabled = $true
DisableLastLogonDateMoreThan = 90
DisablePasswordLastSetMoreThan = 90
DisableExcludeSystems = @(
# 'Windows Server*'
)
DisableIncludeSystems = @()
DisableLimit = 2 # 0 means unlimited, ignored for reports
DisableModifyDescription = $false
DisableModifyAdminDescription = $true
Delete = $true
DeleteIsEnabled = $false
DeleteNoServicePrincipalName = $null
DeleteLastLogonDateMoreThan = 180
DeletePasswordLastSetMoreThan = 180
DeleteListProcessedMoreThan = 90 # 90 days since computer was added to list
DeleteExcludeSystems = @(
# 'Windows Server*'
)
DeleteIncludeSystems = @(
)
DeleteLimit = 2 # 0 means unlimited, ignored for reports
Exclusions = @(
'*OU=Domain Controllers*'
'*OU=Servers,OU=Production*'
'EVOMONSTER$'
'EVOMONSTER.AD.EVOTEC.XYZ'
)
Filter = '*'
WhatIfDisable = $true
WhatIfDelete = $true
LogPath = "$PSScriptRoot\Logs\DeleteComputers_$((Get-Date).ToString('yyyy-MM-dd_HH_mm_ss')).log"
DataStorePath = "$PSScriptRoot\DeleteComputers_ListProcessed.xml"
ReportPath = "$PSScriptRoot\Reports\DeleteComputers_$((Get-Date).ToString('yyyy-MM-dd_HH_mm_ss')).html"
ShowHTML = $true
}
# Run one time as admin: Write-Event -ID 10 -LogName 'Application' -EntryType Information -Category 0 -Message 'Initialize' -Source 'CleanupComputers'
$Output = Invoke-ADComputersCleanup @Configuration
$Output
This is a sample script that you can use to run the module in a scheduled task. It's a good idea to run it as a scheduled task as it will log all the actions and you can easily review them. It's very advanced with many options and you can easily customize it to your needs.
Thi example shows how to use AzureAD, Intune and Jamf to clean up computers in Active Directory where computer also needs needs to be non-existant in AzureAD, Intune and Jamf or have last seen date matches in AzureAD, Intune and Jamf.
This example also moves computers to different OU's as part of the disable process.
# connect to graph for Azure AD, Intune (requires GraphEssentials module)
Connect-MgGraph -Scopes Device.Read.All, DeviceManagementManagedDevices.Read.All, Directory.ReadWrite.All, DeviceManagementConfiguration.Read.All
# connect to jamf (requires PowerJamf module)
Connect-Jamf -Organization 'aaa' -UserName 'aaa' -Suppress -Force -PasswordEncrypted 'aaaaa'
$invokeADComputersCleanupSplat = @{
# safety limits (minimum amount of computers that has to be returned from each source)
SafetyADLimit = 30
SafetyAzureADLimit = 5
SafetyIntuneLimit = 3
SafetyJamfLimit = 50
# disable settings
Disable = $true
DisableLimit = 3
DisableLastLogonDateMoreThan = 90
DisablePasswordLastSetMoreThan = 90
DisableLastSeenAzureMoreThan = 90
DisableLastSyncAzureMoreThan = 90
DisableLastContactJamfMoreThan = 90
DisableLastSeenIntuneMoreThan = 90
DisableAndMove = $true
DisableMoveTargetOrganizationalUnit = @{
'ad.evotec.xyz' = 'OU=Disabled,OU=Computers,OU=Devices,OU=Production,DC=ad,DC=evotec,DC=xyz'
'ad.evotec.pl' = 'OU=Disabled,OU=Computers,OU=Devices,OU=Production,DC=ad,DC=evotec,DC=pl'
}
# delete settings
Delete = $true
DeleteLimit = 3
DeleteLastLogonDateMoreThan = 180
DeletePasswordLastSetMoreThan = 180
DeleteLastSeenAzureMoreThan = 180
DeleteLastSyncAzureMoreThan = 180
DeleteLastContactJamfMoreThan = 180
DeleteLastSeenIntuneMoreThan = 180
DeleteListProcessedMoreThan = 90 # disabled computer has to spend 90 days in list before it can be deleted
DeleteIsEnabled = $false # Computer has to be disabled to be deleted
# global exclusions
Exclusions = @(
'*OU=Domain Controllers*' # exclude Domain Controllers
)
# filter for AD search
Filter = '*'
# logs, reports and datastores
LogPath = "$PSScriptRoot\Logs\CleanupComputers_$((Get-Date).ToString('yyyy-MM-dd_HH_mm_ss')).log"
DataStorePath = "$PSScriptRoot\CleanupComputers_ListProcessed.xml"
ReportPath = "$PSScriptRoot\Reports\CleanupComputers_$((Get-Date).ToString('yyyy-MM-dd_HH_mm_ss')).html"
# WhatIf settings
#ReportOnly = $true
WhatIfDisable = $true
WhatIfDelete = $true
ShowHTML = $true
}
$Output = Invoke-ADComputersCleanup @invokeADComputersCleanupSplat
$Output
The first thing you should do is to run the module in a report only mode. It will show you how many SID History entries are there to remove.
$Output = Invoke-ADSIDHistoryCleanup -WhatIf -ReportOnly
Cleanup of specific SID History entries along with report with email
$invokeADSIDHistoryCleanupSplat = @{
Verbose = $true
WhatIf = $true
IncludeSIDHistoryDomain = @(
'S-1-5-21-3661168273-3802070955-2987026695'
'S-1-5-21-853615985-2870445339-3163598659'
)
#IncludeType = 'External'
RemoveLimitSID = 2
RemoveLimitObject = 2
SafetyADLimit = 1
ShowHTML = $true
Online = $true
DisabledOnly = $false
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 an email with the report
Connect-MgGraph -Scopes 'Mail.Send' -NoWelcome
Send-EmailMessage -To '[email protected]' -From '[email protected]' -MgGraphRequest -Subject "Automated SID Cleanup Report" -Body $EmailBody -Priority Low -Verbose