Active Directory Expired Accounts beheren met PowerShell

Een medewerker gaat over zes weken uit dienst en dan moet natuurlijk het account op slot. Je kunt zes weken wachten en dan netjes het account sluiten, maar het liefst stel je direct een einddatum in zodat je er niet meer naar om hoeft te kijken. Daarom is er in Active Directory de mogelijkheid om bij een gebruikers account een verloopdatum in te stellen. Maar wat te doen met de expired accounts die na verloop van tijd ontstaan? Tijd om e.e.a. te automatiseren met behulp van PowerShell.

Het voordeel van Powershell is dat men, zonder handmatige interventie, razendsnel alle gebruiker accounts in Active Directory kan controleren op de aanwezigheid van een verloopdatum. Dit zou je natuurlijk kunnen exporteren naar een CSV-bestand of mailen naar de beheerder die actie moet ondernemen, maar beter is het als het script gelijk een aantal zaken voor je regelt. Hieronder bespreek ik een aantal stukjes code die in de meeste Active Directory omgevingen gemakkelijk te gebruiken zijn:

  • Zoeken naar expired accounts
  • Expired accounts bewerken
    • Uitschakelen
    • Beschrijving bewerken
    • Verbergen op Exchange adreslijsten
    • Verplaatsen naar een andere OU
  • Resultaten mailen

 

Zoeken naar expired accounts

De eerste stap is het ophalen van alle gebruikers accounts met behulp van het Get-ADUser cmdlet:

Try {
    Get-ADUser -SearchScope Subtree -SearchBase $UsersOU -Filter {ObjectClass -eq 'User'} -properties AccountExpirationDate,Description,Mail | Where-Object{$_.AccountExpirationDate -ne $null}
} Catch {
    Write-Warning -LogMessage "Het ophalen van user accounts is mislukt:  $($_.Exception.Message)"
    $SendThatAlert = $true
    $MailBody = $MailBody + "Het ophalen van user accounts is mislukt:  $($_.Exception.Message)"
}

In bovenstaande stuk code zoeken we in een specifieke OU (de variabele $UsersOU bevat de distinghuised name van die OU) naar gebruiker accounts. Door de SeachScope in te stellen op Subtree worden ook onderliggende Organization Units doorzocht op accounts. Bij de resultaten nemen we naast de standaard gegevens ook de attributen voor de expire datum (AccountExpirationDate), de beschrijving (Description) en het e-mailadres (Mail) mee.

Naast AccountExpirationDate is er ook een AccountExpires attribuut voor elk account, maar deze waarde is binair en dus praktisch onleesbaar. Hoewel deze goed te converteren is, is het gebruik van de AccountExpirationDate veel makkelijker. Niet alle accounts zijn natuurlijk expired accounts dus het resultaat pipen we naar een Where-Object die accounts selecteert met een expire datum.

Doormiddel van een foreach loop worden de gevonden accounts gecontroleerd op de expire datum. Deze worden vergeleken met de huidige datum (Get-Date) om te kijken of deze al verlopen is. De waarde van AccountExpirationDate is overigens altijd opgeslagen in UTC formaat, dus voor een nog exactere vergelijking moet er nog een conversie worden gedaan naar de werkelijke lokale tijd.

Foreach ($UserAccount in $UserAccountsThatExpire) {
    If ($UserAccount.AccountExpirationDate -le (Get-Date)) {
        Write-Warning "Het account $($UserAccount.Name) is expired."
        $SendThatAlert = $true
        $MailBody = $MailBody + "`r`nAccount `'$($UserAccount.Name)`' is expired."
        # Start hier met het bewerken van het account
    }
}

Als men via ‘Active Directory Users and Computers’ MMC Snap-in instelt dat het account verloopt op 31 december dan zal het object in PowerShell een verloopdatum teruggeven van 1 januari om 00:00:00. Als je bijvoorbeeld compliant moet zijn als het om licenties gaat is het dus interessant om het script voor middernacht te draaien en dan te controleren of het account morgen verlopen is, om het vervolgens gelijk te disablen:

Foreach ($UserAccount in $UserAccountsThatExpire) {
    If ($UserAccount.AccountExpirationDate -le ((Get-Date).AddDays(1))) {
        Write-Warning "Het account $($UserAccount.Name) is morgen verlopen."
        $SendThatAlert = $true
        $MailBody = $MailBody + "`r`nAccount `'$($UserAccount.Name)`' is morgen expired."
        
        # Acties uitvoeren op de (bijna) verlopen accounts
    }
}

Expired accounts bewerken

Het meeste logische om met een account te doen als deze verlopen is, is disablen. Aangezien dit ook duidelijker zichtbaar is in de GUI geeft dat meer overzicht in de status van accounts. Verder is het te adviseren om bijvoorbeeld de beschrijving aan te passen zodat duidelijk is dat het account d.m.v. een script disabled is. Verder is een handige optie om gebruikers te verbergen op de Exchange adreslijsten zodat de adreslijst niet vervuild wordt met accounts die niet meer actief zijn. De laatste stap die we hier bespreken is het verplaatsten van het account naar een andere OU waarin alle disabled accounts worden verzameld.

 

Account disablen

Het disablen van het account zou kunnen met het Set-ADUser cmdlet, waarbij je de Enabled attribuut aanpast. Er is ook een specifieke cmdlet beschikbaar: Disable-ADAccount.

Write-Host "Disable account van $($UserAccount.Name)"
Try {
    Disable-ADAccount $UserAccount.SAMAccountName
} Catch {
    Write-Warning "Disablen account is mislukt: $($_.Exception.Message)"
    $MailBody = $MailBody + "`r`nDisablen account is mislukt."
}

Account voorzien van beschrijving

Het is altijd handig dat je op een later moment nog makkelijk kan terug vinden waarom een account disabled is. Vandaar dat we een beschrijving toevoegen dat het account gescript disabled is inclusief datum en tijd. Omdat je een bestaande beschrijving natuurlijk niet wil verwijderen, wordt er gecontroleerd of er al een beschrijving is en als dat zo is wordt deze weer achter de nieuwe beschrijving geplakt.

Write-Host "Voeg beschrijving toe."
Try {
    If ($UserAccount.Description) {
        Set-ADUser $UserAccount.SAMAccountName -Description "Account gescript disabled op $(Get-Date) - $($UserAccount.Description)"
    } else {
        Set-ADUser $UserAccount.SAMAccountName -Description "Account gescript disabled op $(Get-Date)"
    }
} Catch {
    Write-Warning "Toevoegen van beschrijving is mislukt: $($_.Exception.Message)"
    $MailBody = $MailBody + "`r`nToevoegen van beschrijving is mislukt: $($_.Exception.Message)"
}

Verberg gebruiker in de Exchange adreslijst

Om te voorkomen dat mensen gaan mailen dat collega X of Y niet meer in dienst is, maar nog wel in hun adreslijst in Outlook staat is het goed om dat gelijk mee te nemen. Dit kan uiteraard met de Exchange Management Shell (EMC) of door alle benodigde Exchange PSSnapins te laden in PowerShell en een Remote PowerShell sessie op te zetten. Het desbetreffende attribuut is echter ook onderdeel van het Active Directory account en kan dus ook met Set-ADUser cmdlet aangepast worden:

If ($UserAccount.Mail) {
    Write-Host "Verberg gebruiker op Exchange adreslijsten."
    Try {
        Set-ADUser $UserAccount.SAMAccountName -Replace @{msExchHideFromAddressLists="TRUE"}
    } Catch {
        Write-Warning "Verbergen van gebruiker in Exchange adreslijst is mislukt: $($_.Exception.Message)"
        $MailBody = $MailBody + "`r`nVerbergen van gebruiker in Exchange adreslijst is mislukt: $($_.Exception.Message)"
    }
}

Verplaats account naar een ander OU

De laatste stap is het verplaatsen van het account naar een specifieke OU. Het voordeel daarvan is dat het nog overzichtelijker wordt in Active Directory. Als vervolg zou je op basis van een ander script accounts in deze OU op een geven moment automatisch kunnen verwijderen.

Write-Host "Verplaats user account naar $DisabledAccountsOU."
Try {
    Get-Aduser $UserAccount.SAMAccountName | Move-ADObject -targetpath $DisabledAccountsOU
} Catch {
    Write-Warning "Verplaatsen van account is mislukt: $($_.Exception.Message)"
    $MailBody = $MailBody + "`r`nVerplaatsen van account is mislukt: $($_.Exception.Message)"
}

Resultaat mailen

Een script als deze is handmatig te draaien. Ons advies is om dit script als scheduled task te laten draaien. Het nadeel is dan natuurlijk dat je de output in de console nooit te zien krijgt. Dit kan opgelost worden door te loggen naar een tekst bestand of het resultaat te exporteren naar een CSV-bestand. Nog handiger is om een mail te versturen met behulp van het Send-MailMessage cmdlet. Dit valt toch een stuk meer op en er hoeven niet handmatig nog logfiles uitgelezen te worden. Om dit mogelijk te maken moet er wel een receive connector in Exchange aanwezig zijn, die anoniem e-mail kan ontvangen vanaf de server waarop het betreffende script draait.

In de, in dit blog gebruikte, code wordt het mailtje alleen verstuurd als er iets fout gaat in het script of als er een verlopen account wordt gevonden d.m.v. de boolean $SendThatAlert. De interessante informatie wordt steeds verwerkt in $MailBody en deze variabele is uiteindelijk de body van het e-mailbericht. Met deze cmdlet is het ook mogelijk om een log- of CSV-bestand als bijlage mee te sturen met de –Attachments parameter.

If ($SendThatAlert) {
    $MailTo = "Helpdesk@domain.local"
    $MailFrom = "Script@domain.local"
    $MailSubject = "Script: verlopen accounts zijn disabled"
    $MailSMTP = "smtp.domain.local"
    Send-MailMessage -to $MailTo -from $MailFrom -subject $MailSubject -SmtpServer $MailSMTP -body $MailBody

    Write-Host "Een bericht is verzonden naar $Mailto"
}