Public/Get-StaleADObjects.ps1

function Get-StaleADObjects {
    <#
    .SYNOPSIS
        Finds stale (inactive) user and computer accounts in Active Directory.
 
    .DESCRIPTION
        Identifies user and computer accounts that have not logged in within the
        specified threshold. Returns objects sorted by last logon date.
 
    .PARAMETER DaysInactive
        Number of days since last logon to consider an account stale. Defaults to 90.
 
    .PARAMETER ObjectType
        Filter by object type: User, Computer, or Both. Defaults to Both.
 
    .PARAMETER SearchBase
        OU to scope the search. Defaults to entire domain.
 
    .PARAMETER ExcludeOU
        One or more OU distinguished names to exclude from results (e.g., service account OUs).
 
    .EXAMPLE
        Get-StaleADObjects -DaysInactive 60
 
    .EXAMPLE
        Get-StaleADObjects -ObjectType User -ExcludeOU "OU=Service Accounts,DC=contoso,DC=com"
    #>

    [CmdletBinding()]
    param(
        [int]$DaysInactive = 90,

        [ValidateSet('User', 'Computer', 'Both')]
        [string]$ObjectType = 'Both',

        [string]$SearchBase,

        [string[]]$ExcludeOU
    )

    $threshold = (Get-Date).AddDays(-$DaysInactive)
    $results = [System.Collections.Generic.List[PSCustomObject]]::new()

    $commonProps = @('LastLogonDate', 'PasswordLastSet', 'WhenCreated', 'Enabled', 'Description', 'DistinguishedName')

    # Stale Users
    if ($ObjectType -in @('User', 'Both')) {
        $userParams = @{
            Filter     = "LastLogonDate -lt '$threshold' -or -not LastLogonDate -like '*'"
            Properties = $commonProps + @('Title', 'Department', 'Manager', 'EmailAddress')
        }
        if ($SearchBase) { $userParams['SearchBase'] = $SearchBase }

        Get-ADUser @userParams | Where-Object { $_.Enabled -eq $true } | ForEach-Object {
            $excluded = $false
            if ($ExcludeOU) {
                foreach ($ou in $ExcludeOU) {
                    if ($_.DistinguishedName -like "*$ou") { $excluded = $true; break }
                }
            }
            if (-not $excluded) {
                $results.Add([PSCustomObject]@{
                    ObjectClass     = 'user'
                    Name            = $_.Name
                    SAMAccountName  = $_.SAMAccountName
                    LastLogon       = $_.LastLogonDate
                    PasswordLastSet = $_.PasswordLastSet
                    Created         = $_.WhenCreated
                    Department      = $_.Department
                    Description     = $_.Description
                    DN              = $_.DistinguishedName
                    DaysStale       = if ($_.LastLogonDate) { [math]::Round(((Get-Date) - $_.LastLogonDate).TotalDays) } else { 'Never' }
                })
            }
        }
    }

    # Stale Computers
    if ($ObjectType -in @('Computer', 'Both')) {
        $compParams = @{
            Filter     = "LastLogonDate -lt '$threshold' -or -not LastLogonDate -like '*'"
            Properties = $commonProps + @('OperatingSystem', 'OperatingSystemVersion')
        }
        if ($SearchBase) { $compParams['SearchBase'] = $SearchBase }

        Get-ADComputer @compParams | Where-Object { $_.Enabled -eq $true } | ForEach-Object {
            $excluded = $false
            if ($ExcludeOU) {
                foreach ($ou in $ExcludeOU) {
                    if ($_.DistinguishedName -like "*$ou") { $excluded = $true; break }
                }
            }
            if (-not $excluded) {
                $results.Add([PSCustomObject]@{
                    ObjectClass     = 'computer'
                    Name            = $_.Name
                    SAMAccountName  = $_.SAMAccountName
                    LastLogon       = $_.LastLogonDate
                    PasswordLastSet = $_.PasswordLastSet
                    Created         = $_.WhenCreated
                    Department      = $_.OperatingSystem
                    Description     = $_.Description
                    DN              = $_.DistinguishedName
                    DaysStale       = if ($_.LastLogonDate) { [math]::Round(((Get-Date) - $_.LastLogonDate).TotalDays) } else { 'Never' }
                })
            }
        }
    }

    $results | Sort-Object LastLogon
}