Deaktivieren inaktiver Benutzeraccounts unter Windows

Welcher Sysadmin hat das noch nicht miterlebt? Über die Zeit sammeln sich unbenutzte Benutzeraccounts an. Sei es weil irgendjemand schlicht und ergreifend vergessen hat Bescheid zu geben, dass jemand aus dem Unternehmen ausgeschieden ist, oder sei es weil eine Aussenstelle ja doch so weit von der Zentrale entfernt ist.
Mit dem folgenden Powershell- Script können inaktive Benutzeraccounts in allen Domains eines Windows- Forest deaktiviert werden. Zusätzlich wird eine eMail mit einer angehängten Textdatei versendet, die die ausgeführten Aktionen dokumentiert.

 
###
# rss - 20100715
###
# call as: CMD: powershell "& 'c:\admin\InactiveUsers.ps1'"
###
function fnGet_Domain_controller_list($domain) {
$domain_controller_list = $domain.DomainControllers | Select-Object Name
}

function fnGetDomainList {
$forest = [system.directoryservices.activedirectory.Forest]::GetCurrentForest()
If ( $Debug ) { write-Output "Working in Forest: " $forest.Name | out-File -filepath $LOGFILE -Append }
return $forest.Domains
}

function fnGetUsersInDomain($domain) {
$searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]"LDAP://$domain")
$searcher.filter = "(&(objectCategory=person)(objectClass=user))"
$searcher.PageSize = 100000
$users = $searcher.Findall()
return $users;
}

function sendmail($body, $attachfile) {
$filename = $attachfile
$SmtpClient = new-object system.net.mail.smtpClient
$MailMessage = New-Object system.net.mail.mailmessage
$att = new-object Net.Mail.Attachment($filename)
$SmtpClient.Host = "SMTPHOST"
$mailmessage.from = "ABSENDER@DOMAIN.RS"
$mailmessage.To.add("EMPFAENGER@DOMAIN.RS")
$mailmessage.Subject = .Inactive Users for $now UTC.
$MailMessage.IsBodyHtml = $false
$mailmessage.Body = $body
$mailmessage.Attachments.Add($att)
$smtpclient.Send($mailmessage)
}


###
# Main part
###
# Anzahl der Tage in denen der Account nicht angemeldet war
If ( $args[0] -eq "" ) {
$MeanDays = 90
}
else {
$MeanDays = $args[0]
}
# Debugmode
If ( $args[1] -eq "DEBUG" ) {
$Debug = 1
}
else {
$Debug = 0
}

$ADS_UF_DONT_EXPIRE_PASSWD = 0x010000
$ADS_UF_ACCOUNTDISABLED = 0x0002
$LOGFILE = "c:\admin\inactiveusers.txt"

# reset logfile
write-Output "Starting... Please stand by..." | out-File -filepath $LOGFILE
write-Output "The following people did not log on the last $MeanDays days:" | out-File -filepath $LOGFILE -Append

If ( $Debug ) { write-Output "Working with MeanDays of $MeanDays days" | out-File -filepath $LOGFILE -Append }

$domains = fnGetDomainList
foreach ($domain in $domains) {
$users = fnGetUsersInDomain($domain)
foreach ($user in $users) {
if ( $user.properties.mail ) {
[string]$userCN = $user.properties.cn
[string]$userAccount = $user.properties.samaccountname
[string]$userLastLogon = $user.properties.lastlogontimestamp
$userprops = [ADSI]$user.Path
$userAccountDisabled = $userprops.userAccountControl.Item(0)
[datetime]$now = [System.DateTime]::UtcNow
If ( $Debug ) { write-Output "========================" | out-File -filepath $LOGFILE -Append }
If ( $Debug ) { write-Output "Login: $domain\$userAccount" | out-File -filepath $LOGFILE -Append }
If (( $userAccountDisabled -band $ADS_UF_ACCOUNTDISABLED ) -ne 0 ) {
If ( $Debug ) { Write-Output "Account already disabled" | out-File -filepath $LOGFILE -Append }
$disabled = 1
}
else {
If ( $Debug ) { Write-Output "Account active" | out-File -filepath $LOGFILE -Append }
$disabled = 0
}
If (( $userAccountDisabled -band $ADS_UF_DONT_EXPIRE_PASSWD ) -ne 0 ) {
$pwexpire = 0
}
else {
$pwexpire = 1
}
$LastLogonHR = [System.DateTime]::FromFileTime($userLastLogon)
If ( $Debug ) { write-Output "LastLogon: $LastLogonHR" | out-File -filepath $LOGFILE -Append }
If ( $Debug ) { write-Output "Jetzt grad: $now" | out-File -filepath $LOGFILE -Append }
$jetztts = $now.ToFileTimeUtc()
[datetime]$etzadla = [System.DateTime]::FromFileTime($jetztts)
$uLastLogon = $now.AddDays(- $MeanDays)
If ( $Debug ) { Write-Output "Referenzzeit: $uLastLogon" | out-File -filepath $LOGFILE -Append }
# wann lastlogon < (jetz-mean)
$MeanDaysMS = $MeanDays * 1440 * 60 * 10000000
If ( ( [Int64]$userLastLogon -lt ([Int64]$jetztts - $MeanDaysMS ) ) -and $disabled -eq 0 -and $pwexpire -eq 1 ) {
$value = $value -bxor $ADS_UF_ACCOUNTDISABLED
$user.userAccountControl = $value
$user.SetInfo()
Write-Output "$domain\$userAccount disabled @$now - Last logon: $LastLogonHR" | out-File -filepath $LOGFILE -Append
}
}
}
}

write-Output "Completed Massa Buana!" | out-File -filepath $LOGFILE -Append

$body = "Irgendein Text oder der Inhalt einer Variable oder sonst ein Gebrabbel das keinen interessiert."
sendmail $body $LOGFILE


  Dem Script können zwei Parameter übergeben werden:
powershell "& 'c:\admin\InactiveUsers.ps1TAGE DEBUG'"
TAGE ist hierbei die Anzahl an Tagen, die vergangen sein muss bis ein Account als inaktiv angesehen wird, also die Anzahl an Tagen seit dem letzten Login. Wird hier kein Wert angegeben, so wird der Standard von 90 Tagen verwendet.
Wird für DEBUG der Wert 1 übergeben so gibt das Script zusätzliche Meldungen aus. Soll das nicht geschehen, so kann der Parameter auch einfach weggelassen werden.

Das Anpassen sämtlicher eMail- Parameter wie Absender, Empfänger, Mailserver usw. erfolgt im Script selbst.  
###
# rss - 20100715
###
# call as: CMD: powershell "& 'c:\admin\InactiveUsers.ps1'"
###
function fnGet_Domain_controller_list($domain) {
 $domain_controller_list = $domain.DomainControllers | Select-Object Name
}
 
function fnGetDomainList {
 $forest = [system.directoryservices.activedirectory.Forest]::GetCurrentForest()
 If ( $Debug ) { write-Output "Working in Forest: " $forest.Name | out-File -filepath $LOGFILE -Append }
 return $forest.Domains
}
 
function fnGetUsersInDomain($domain) {
 $searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]"LDAP://$domain")
 $searcher.filter = "(&(objectCategory=person)(objectClass=user))"
 $searcher.PageSize = 100000
 $users = $searcher.Findall()
 return $users;
}
 
function sendmail($body, $attachfile) {
 $filename = $attachfile
 $SmtpClient = new-object system.net.mail.smtpClient
 $MailMessage = New-Object system.net.mail.mailmessage
 $att = new-object Net.Mail.Attachment($filename)
 $SmtpClient.Host = "SMTPHOST"
 $mailmessage.from = "ABSENDER@DOMAIN.RS"
 $mailmessage.To.add("EMPFAENGER@DOMAIN.RS")
 $mailmessage.Subject = .Inactive Users for $now UTC.
 $MailMessage.IsBodyHtml = $false
 $mailmessage.Body = $body
 $mailmessage.Attachments.Add($att)
 $smtpclient.Send($mailmessage)
}
 
 
###
# Main part
###
# Anzahl der Tage in denen der Account nicht angemeldet war
If ( $args[0] -eq "" ) {
 $MeanDays = 90
}
else {
 $MeanDays = $args[0]
}
# Debugmode
If ( $args[1] -eq "DEBUG" ) {
 $Debug = 1
}
else {
 $Debug = 0
}
 
$ADS_UF_DONT_EXPIRE_PASSWD = 0x010000
$ADS_UF_ACCOUNTDISABLED = 0x0002
$LOGFILE = "c:\admin\inactiveusers.txt"
 
# reset logfile
write-Output "Starting... Please stand by..." | out-File -filepath $LOGFILE
write-Output "The following people did not log on the last $MeanDays days:" | out-File -filepath $LOGFILE -Append
 
If ( $Debug ) { write-Output "Working with MeanDays of $MeanDays days" | out-File -filepath $LOGFILE -Append }
 
$domains = fnGetDomainList
foreach ($domain in $domains) {
 $users = fnGetUsersInDomain($domain)
 foreach ($user in $users) {
 if ( $user.properties.mail ) {
 [string]$userCN = $user.properties.cn
 [string]$userAccount = $user.properties.samaccountname
 [string]$userLastLogon = $user.properties.lastlogontimestamp
 $userprops = [ADSI]$user.Path
 $userAccountDisabled = $userprops.userAccountControl.Item(0)
 [datetime]$now = [System.DateTime]::UtcNow
 If ( $Debug ) { write-Output "========================" | out-File -filepath $LOGFILE -Append }
 If ( $Debug ) { write-Output "Login: $domain\$userAccount" | out-File -filepath $LOGFILE -Append }
 If (( $userAccountDisabled -band $ADS_UF_ACCOUNTDISABLED ) -ne 0 ) {
 If ( $Debug ) { Write-Output "Account already disabled" | out-File -filepath $LOGFILE -Append }
 $disabled = 1
 }
 else {
 If ( $Debug ) { Write-Output "Account active" | out-File -filepath $LOGFILE -Append }
 $disabled = 0
 }
 If (( $userAccountDisabled -band $ADS_UF_DONT_EXPIRE_PASSWD ) -ne 0 ) {
 $pwexpire = 0
 }
 else {
 $pwexpire = 1
 }
 $LastLogonHR = [System.DateTime]::FromFileTime($userLastLogon)
 If ( $Debug ) { write-Output "LastLogon: $LastLogonHR" | out-File -filepath $LOGFILE -Append }
 If ( $Debug ) { write-Output "Jetzt grad: $now" | out-File -filepath $LOGFILE -Append }
 $jetztts = $now.ToFileTimeUtc()
 [datetime]$etzadla = [System.DateTime]::FromFileTime($jetztts)
 $uLastLogon = $now.AddDays(- $MeanDays)
 If ( $Debug ) { Write-Output "Referenzzeit: $uLastLogon" | out-File -filepath $LOGFILE -Append }
 # wann lastlogon < (jetz-mean)
 $MeanDaysMS = $MeanDays * 1440 * 60 * 10000000
 If ( ( [Int64]$userLastLogon -lt ([Int64]$jetztts - $MeanDaysMS ) ) -and $disabled -eq 0 -and $pwexpire -eq 1 ) {
 $value = $value -bxor $ADS_UF_ACCOUNTDISABLED
 $user.userAccountControl = $value
 $user.SetInfo()
 Write-Output "$domain\$userAccount disabled @$now - Last logon: $LastLogonHR" | out-File -filepath $LOGFILE -Append
 }
 }
 }
}
 
write-Output "Completed Massa Buana!" | out-File -filepath $LOGFILE -Append
 
$body = "Irgendein Text oder der Inhalt einer Variable oder sonst ein Gebrabbel das keinen interessiert."
sendmail $body $LOGFILE