Skip to content

EvotecIT/CleanupMonster

Repository files navigation

CleanupMonster - PowerShell Module

Discord

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:

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.

Support This Project

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!

Installation

Install-Module -Name CleanupMonster -Force -Verbose

Introduction ✨

CleanupMonster is built for teams that want Active Directory cleanup to feel deliberate, staged, and reviewable instead of risky and opaque. Every cleanup path starts with discovery, can generate HTML output for review, and can be wrapped in limits, pending lists, and WhatIf controls before anything destructive happens.

Whether you are retiring stale computers, pruning unused MSA / gMSA objects, or removing legacy SID history after migrations, the module is designed to help you choose the right cleanup track for the problem in front of you.

Warning

Run the module with the permissions required to read the attributes you rely on. In a default AD configuration that often includes LastLogonDate and PasswordLastSet, but hardened environments may require elevated rights even for reporting-only runs.

Overview 🧭

CleanupMonster is easiest to understand as three end-to-end cleanup paths. They all start with discovery and reporting, but each one is optimized for a different cleanup problem.

Tip

If you are new to the module, start with -ReportOnly and -WhatIf, keep limits low, and validate the generated HTML output before enabling destructive actions.

flowchart LR
    classDef computer fill:#D9F2E6,stroke:#1F7A4D,stroke-width:2px,color:#123524;
    classDef service fill:#FFF0CC,stroke:#B7791F,stroke-width:2px,color:#5B3A00;
    classDef sid fill:#DCEBFF,stroke:#2B6CB0,stroke-width:2px,color:#153E75;
    classDef action fill:#F7FAFC,stroke:#4A5568,stroke-width:1px,color:#1A202C;

    A["CleanupMonster"] --> B["Computer cleanup"]
    A --> C["Service-account cleanup"]
    A --> D["SID history cleanup"]

    B --> B1["Disable"]
    B --> B2["Move"]
    B --> B3["DisableAndMove"]
    B --> B4["Delete after pending age"]

    C --> C1["Disable"]
    C --> C2["Delete"]
    C --> C3["Explicit selectors and low limits"]

    D --> D1["Discover SID history"]
    D --> D2["Filter by OU / object type / SID domain"]
    D --> D3["Remove with small limits"]

    class B,B1,B2,B3,B4 computer;
    class C,C1,C2,C3 service;
    class D,D1,D2,D3 sid;
    class A action;
Loading
Area Cmdlet Best for Typical actions Recommended mindset
Computer cleanup Invoke-ADComputersCleanup Stale computer lifecycle management in AD, optionally cross-checked against Azure AD, Intune, and Jamf Disable, move, disable-and-move, delete Stage changes across multiple runs
Service-account cleanup Invoke-ADServiceAccountsCleanup Reviewing and cleaning stale MSA / gMSA objects with stronger destructive-run guardrails Disable, delete Start narrow and require explicit selectors
SID history cleanup Invoke-ADSIDHistoryCleanup Removing legacy SID history entries with detailed before/after reporting Report, selective remove Start with discovery and tiny limits

Quick Start 🚀

Recommended first commands for each path:

# Computer cleanup preview
Invoke-ADComputersCleanup -Disable -ReportOnly -WhatIf -ShowHTML

# Service-account cleanup preview
Invoke-ADServiceAccountsCleanup -Disable -ReportOnly -WhatIf -IncludeAccounts 'gmsa-*'

# SID history cleanup preview
Invoke-ADSIDHistoryCleanup -ReportOnly -WhatIf

Cleanup Paths 🧰

Computer Cleanup 🖥️

Computer cleanup is the richest workflow in the module. It is built for staged remediation of stale computer objects rather than one immediate destructive pass.

Use it when you want to:

  • find stale AD computer objects
  • optionally require Azure AD, Intune, or Jamf inactivity before actioning AD
  • disable first and delete later
  • move devices to a quarantine OU before final deletion
  • keep a pending list as a safety buffer across multiple runs

Typical lifecycle:

flowchart LR
    classDef stage fill:#D9F2E6,stroke:#1F7A4D,stroke-width:2px,color:#123524;
    classDef gate fill:#FFF7E6,stroke:#B7791F,stroke-width:2px,color:#5B3A00;
    classDef outcome fill:#EDF2F7,stroke:#4A5568,stroke-width:1px,color:#1A202C;

    A["Discover stale computers"] --> B{"Matches disable filters?"}
    B -- "No" --> Z["Remain untouched"]
    B -- "Yes" --> C["Disable"]
    C --> D{"DisableAndMove enabled?"}
    D -- "Yes" --> E["Move to quarantine OU"]
    D -- "No" --> F["Add to pending list"]
    E --> F
    F --> G{"Later matches move or delete gates?"}
    G -- "Move path" --> H["Move-only follow-up"]
    G -- "Delete path" --> I["Delete after pending-age threshold"]

    class A,C,E,F,H,I stage;
    class B,D,G gate;
    class Z outcome;
Loading

What matters most:

  • -WhatIf now flows to move actions as well as disable/delete actions
  • -Move is a first-class path, not just a side effect of disable
  • -DisableAndMove behaves like a disable workflow during discovery and then performs the move during execution
  • pending-list timing in the datastore lets you separate disable, move, and delete into different operational windows

Good starter profiles:

  • pilot: -ReportOnly -WhatIf -Disable
  • staged remediation: -Disable now, then -DeleteListProcessedMoreThan later
  • quarantine-first: -DisableAndMove -DisableMoveTargetOrganizationalUnit ...
  • cloud-aware cleanup: add Azure AD / Intune / Jamf inactivity thresholds and safety limits

Relevant examples:

  • Examples/DeleteComputers.ps1
  • Examples/DeleteComputersWithMoveAndEmail.ps1
  • Examples/DeleteComputersWithO365.ps1
  • Examples/DeleteComputersWithO365andJAMF.ps1

Service-Account Cleanup 🔐

Service-account cleanup is intentionally narrower and safer than computer cleanup. It is designed for MSA and gMSA hygiene where you want low limits and explicit destructive intent.

Use it when you want to:

  • review stale managed service accounts
  • limit scope with age filters or IncludeAccounts
  • keep disable/delete limits very small during rollout
  • avoid disabling and deleting the same account in the same run

Typical lifecycle:

flowchart LR
    classDef stage fill:#FFF0CC,stroke:#B7791F,stroke-width:2px,color:#5B3A00;
    classDef gate fill:#FFF7E6,stroke:#B7791F,stroke-width:2px,color:#5B3A00;
    classDef outcome fill:#EDF2F7,stroke:#4A5568,stroke-width:1px,color:#1A202C;

    A["Discover service accounts"] --> B{"Explicit selectors or age filters?"}
    B -- "No destructive criteria" --> Z["Allow preview only"]
    B -- "Yes" --> C["Find disable candidates"]
    C --> D["Disable with low limits"]
    D --> E["Find delete candidates"]
    E --> F{"Already scheduled for disable?"}
    F -- "Yes" --> G["Skip delete in this run"]
    F -- "No" --> H["Delete with low limits"]

    class A,C,D,E,G,H stage;
    class B,F gate;
    class Z outcome;
Loading

What matters most:

  • destructive disable/delete runs require explicit selection criteria
  • IncludeAccounts counts as an explicit selector
  • preview scenarios with -ReportOnly, -WhatIf, -WhatIfDisable, or -WhatIfDelete are allowed even without destructive selectors
  • accounts scheduled for disable in a run are skipped from delete in that same run

Good starter profiles:

  • preview by name pattern: -Disable -ReportOnly -WhatIf -IncludeAccounts 'gmsa-*'
  • safer scheduled run: explicit include/exclude patterns plus low DisableLimit and DeleteLimit
  • age-based hygiene: combine LastLogonDateMoreThan, PasswordLastSetMoreThan, and WhenCreatedMoreThan

Relevant examples:

  • Examples/CleanupServiceAccounts.ps1

SID History Cleanup 🧬

SID history cleanup is for post-migration and trust-hygiene work. It is optimized for discovering targeted SID history entries, filtering them carefully, and removing them with very small limits at first.

Use it when you want to:

  • discover which objects still carry SID history
  • narrow scope by source SID domain, OU, object type, or trust type
  • clean up legacy migration artifacts with strong before/after reporting
  • remove only a few SIDs or a few objects per run until the results are proven

Typical lifecycle:

flowchart LR
    classDef stage fill:#DCEBFF,stroke:#2B6CB0,stroke-width:2px,color:#153E75;
    classDef gate fill:#EAF4FF,stroke:#2B6CB0,stroke-width:2px,color:#153E75;
    classDef outcome fill:#EDF2F7,stroke:#4A5568,stroke-width:1px,color:#1A202C;

    A["Discover objects with SID history"] --> B["Filter by OU / object type / SID domain / trust type"]
    B --> C{"ReportOnly or WhatIf?"}
    C -- "Yes" --> Z["Review targeted objects and counts"]
    C -- "No" --> D["Remove targeted SID values"]
    D --> E["Refresh object after each SID removal"]
    E --> F{"Reached object or SID limit?"}
    F -- "No" --> D
    F -- "Yes" --> G["Stop and report current state"]

    class A,B,D,E,G stage;
    class C,F gate;
    class Z outcome;
Loading

What matters most:

  • you can filter by object type, OU, SID source domain, and trust type
  • RemoveLimitSID and RemoveLimitObject let you keep the blast radius intentionally small
  • each SID removal is tracked with before/after state so the report remains reviewable even for multi-SID objects

Good starter profiles:

  • full discovery: -ReportOnly -WhatIf
  • migration cleanup by domain SID: use IncludeSIDHistoryDomain
  • targeted validation run: set both RemoveLimitSID and RemoveLimitObject to 1 or 2

Relevant examples:

  • Examples/DeleteSIDHistory.ps1

Operational Guards 🛡️

CleanupMonster is designed to be used with multiple layers of safety. You do not need every guard in every environment, but you should choose a guard set deliberately.

Universal guards

These are good defaults almost everywhere:

  • -ReportOnly for first-pass validation
  • top-level -WhatIf for broad previews
  • action-specific -WhatIfDisable, -WhatIfMove, and -WhatIfDelete when you want mixed behavior
  • low action limits during rollout, then widen later
  • HTML reporting plus log files for every scheduled run

Environment guards

These help ensure your data sources are healthy before any action happens:

  • SafetyADLimit to stop if AD returns fewer objects than expected
  • SafetyAzureADLimit, SafetyIntuneLimit, and SafetyJamfLimit when cloud/device-source validation matters
  • TargetServers when you want deterministic domain-controller selection

Workflow guards

These reduce risk in destructive cleanup patterns:

  • pending-list aging via DeleteListProcessedMoreThan or MoveListProcessedMoreThan
  • OU quarantine with -Move or -DisableAndMove
  • exclusions for known-sensitive OUs, names, or systems
  • service-account explicit selectors before destructive runs
  • protected-from-accidental-deletion checks for move/delete operations

Recommended guard posture by environment

Environment maturity Suggested guard set
First lab / pilot -ReportOnly, top-level -WhatIf, low limits, HTML report, log file
Early production rollout Explicit exclusions, low limits, AD safety limit, pending-list staging, action-specific WhatIf for risky steps
Mature production automation AD and cloud safety limits, staged disable/delete windows, OU quarantine, reports/logs, explicit target servers where needed

Rollout Pattern 🪜

If you are introducing CleanupMonster into an environment for the first time, this is a good default operating model:

  1. Run report-only previews and validate the HTML output with your AD owners.
  2. Enable disable-only or disable-and-move with low limits.
  3. Review pending-list behavior for several runs.
  4. Add delete or move-after-delay thresholds only after the staged output is stable.
  5. Increase limits gradually once the rules are proven.

For service accounts, start even narrower:

  1. use IncludeAccounts or very explicit age filters
  2. keep DisableLimit / DeleteLimit low
  3. review the report
  4. then automate

For SID history, start by discovering:

  1. run in report mode
  2. filter to a known source SID domain, OU, or object type
  3. set low RemoveLimitObject / RemoveLimitSID
  4. then expand when the before/after report looks right

Detailed Walkthroughs 📚

The sections below go deeper into each cleanup path with concrete commands, screenshots, and scheduled-task examples you can adapt for your own environment.

Note

The earlier sections help you pick the right cleanup track. The walkthroughs below show how to operate each track in practice once you know which one you want.

Computer Cleanup Walkthrough 🖥️

This is the full staged device-cleanup path, from first report to scheduled automation and cloud-aware validation.

Report Only

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.

ReportOnlyMode

Interactively

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)

CurrentRun

  • Devices in Previous Runs (History)

PreviousRuns

  • Devices on Pending List (Pending deletion)

PendingList

  • All Devices (All) remaining

AllRemaining

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

Protected computer objects

If a computer is protected from accidental deletion, -RemoveProtectedFromAccidentalDeletionFlag now applies only to move and delete actions. Disable-only runs will leave that protection in place.

$Output = Invoke-ADComputersCleanup `
    -Disable `
    -DisableAndMove `
    -DisableMoveTargetOrganizationalUnit 'OU=Disabled,DC=contoso,DC=com' `
    -RemoveProtectedFromAccidentalDeletionFlag `
    -WhatIfDisable `
    -ShowHTML
$Output

Scheduled task

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

Cloud-aware scheduled task

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. When RemoveProtectedFromAccidentalDeletionFlag is enabled, the flag is only cleared for the move/delete steps that require it. For long computer names, cloud matching now falls back through DNSHostName and related aliases so truncated AD names can still match Azure AD and Intune records.

# 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
    RemoveProtectedFromAccidentalDeletionFlag = $true
}

$Output = Invoke-ADComputersCleanup @invokeADComputersCleanupSplat
$Output

Service-Account Walkthrough 🔐

Service-account cleanup supports report-only reviews, disable/delete staging, and explicit safety guardrails. If an account matches both sets of criteria in the same run, it stays in the disable stage and is skipped from delete. By default DisableLimit and DeleteLimit are both 1.

Report Only

$Output = Invoke-ADServiceAccountsCleanup -Disable -Delete -DisableLastLogonDateMoreThan 90 -DeleteLastLogonDateMoreThan 180 -ReportOnly
$Output.CurrentRun

Safer scheduled run

$invokeADServiceAccountsCleanupSplat = @{
    Disable                        = $true
    DisableLastLogonDateMoreThan   = 90
    DisablePasswordLastSetMoreThan = 90
    DisableLimit                   = 2

    Delete                         = $true
    DeleteLastLogonDateMoreThan    = 180
    DeletePasswordLastSetMoreThan  = 180
    DeleteLimit                    = 1

    SafetyADLimit                  = 10
    IncludeAccounts                = @('gmsa-*', 'msa-*')
    ExcludeAccounts                = @('gmsa-keep-*')
    ReportPath                     = "$PSScriptRoot\Reports\ServiceAccounts_$((Get-Date).ToString('yyyy-MM-dd_HH_mm_ss')).html"
    WhatIfDisable                  = $true
    WhatIfDelete                   = $true
}

$Output = Invoke-ADServiceAccountsCleanup @invokeADServiceAccountsCleanupSplat
$Output

Interactive review

$Output = Invoke-ADServiceAccountsCleanup `
    -Disable `
    -Delete `
    -DisableLastLogonDateMoreThan 90 `
    -DeleteLastLogonDateMoreThan 180 `
    -WhatIfDisable `
    -WhatIfDelete `
    -ReportPath "$PSScriptRoot\Reports\ServiceAccounts.html"
$Output

SID History Walkthrough 🧬

This path is focused on reviewable, targeted SID history removal with strong filtering and very small starter limits.

Report Only

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

Targeted cleanup with reporting

Cleanup of specific SID History entries along with report output and email-friendly reporting.

$invokeADSIDHistoryCleanupSplat = @{
    Verbose                 = $true
    WhatIf                  = $true
    IncludeSIDHistoryDomain = @(
        'S-1-5-21-3661168273-3802070955-2987026695'
        'S-1-5-21-853615985-2870445339-3163598659'
    )
    # Process only specific object classes
    IncludeObjectType        = @('Computer', 'Group')
    #ExcludeObjectType       = 'User'
    #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

About

This module provides an easy way to cleanup Active Directory from dead/old objects based on various criteria. It can also disable, move or delete objects. It can utilize Azure AD, Intune and Jamf to get additional information about objects before deleting them.

Topics

Resources

Stars

Watchers

Forks

Sponsor this project

  •  

Contributors