#Get-DomainPasswordReport.ps1 #v1.2 September 23, 2008 # added paged search to return more than 1000 users # fixed problem with nested functions # Jeffery Hicks # jhicks@sapien.com # http://blog.sapien.com #--------------------------------------------------------------------------------------- #sample output # Name : Roy Biv # DN : CN=Roy Biv,OU=Executive,OU=Employees,DC=bigcompany,DC=local # Description : Company president # Email : roy@bigcompany.com # AccountCreated : 2/24/2008 11:41:44 AM # AccountModified : 6/13/2008 9:08:41 PM # LastLogon : 6/22/2008 8:56:21 AM # PasswordLastChanged : 5/12/2008 11:55:00 PM # PasswordAge : 58 # PasswordExpired : False # PasswordNeverExpires : True # PasswordChangeAllowed : True # BadPasswordTime : 0 #Usage examples # Set-Alias gdpr c:\scripts\Get-DomainPasswordReport.ps1 # gdpr | where {$_.PasswordNeverExpires} # gdpr | where {$_.PasswordNeverExpires} | out-file AccountReport.txt # gdpr | where {$_.PasswordNeverExpires} | Select Name,Email,LastLogon,PasswordAge # gdpr | where {$_.passwordExpired} | Export-CSV \\file9\reports\Expired.csv # gdpr | where {$_.passwordage -gt 50 -and !$_.passwordexpired -and !$_.PasswordNeverExpires} | Select name,email,passwordAge,passwordlastchanged # IMPORTANT: # This script requires a Windows 2003 or later domain. # **************************************************************** # * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED * # * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK. * # **************************************************************** #--------------------------------------------------------------------------------------- #define some functions used in the script #get the age of the password in days #a value of 0 means just set or needs to be set #the value of $LastSet will be a large integer #indicating the number seconds since 1/1/1601 #since the password was set Function Get-UTCAge { #get date time of the last password change Param([int64]$Last=0) if ($Last -eq 0) { write 0 } else { #clock starts counting from 1/1/1601. [datetime]$utc="1/1/1601" #calculate the number of days based on the int64 number $i=$Last/864000000000 #Add the number of days to 1/1/1601 #and write the result to the pipeline write ($utc.AddDays($i)) } } # end Get-UTCAge function Function Get-PwdAge { Param([int64]$LastSet=0) if ($LastSet -eq 0) { write "0" } else { #get the date the password was last changed [datetime]$ChangeDate=Get-UTCAge $LastSet #get the current date and time [datetime]$RightNow=Get-Date #write the difference in days write $RightNow.Subtract($ChangeDate).Days } } #end Get-PwdAge function #main code #define some constants New-Variable ADS_UF_ACCOUNTDISABLE 0x0002 -Option Constant New-Variable ADS_UF_PASSWD_CANT_CHANGE 0x0040 -Option Constant New-Variable ADS_UF_DONT_EXPIRE_PASSWD 0x10000 -Option Constant New-Variable ADS_UF_PASSWD_EXPIRED 0x800000 -Option Constant #define our searcher object $Searcher = New-Object DirectoryServices.DirectorySearcher #enable paged searches $searcher.pagesize=100 # find all non-disabled user objects $filter="(&(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2))" $searcher.filter=$filter #get all enabled users and for each one get information $searcher.findall() | ForEach-Object { #get password properties from useraccountcontrol field if ($_.properties.item("useraccountcontrol")[0] -band $ADS_UF_DONT_EXPIRE_PASSWD) { $pwdNeverExpires=$True } else { $pwdNeverExpires=$False } #Password expired should be calculated from a computed UAC value $user=$_.GetDirectoryEntry() $user.psbase.refreshcache("msDS-User-Account-Control-Computed") [int]$computed=$user.psbase.properties.item("msDS-User-Account-Control-Computed").value if ($computed -band $ADS_UF_PASSWD_EXPIRED) { $pwdExpired=$True } else { $pwdExpired=$False } #check if user can change their password if ($_.properties.item("useraccountcontrol")[0] -band $ADS_UF_PASSWD_CANT_CHANGE) { $pwdChangeAllowed=$False } else { $pwdChangeAllowed=$True } #create a custom object for the account and password properties $obj=New-Object PSObject #add properties to the object $obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $_.properties.item("name")[0] $obj | Add-Member -MemberType NoteProperty -Name "DN" -Value $_.properties.item("distinguishedname")[0] $obj | Add-Member -MemberType NoteProperty -Name "Description" -Value $_.properties.item("description")[0] $obj | Add-Member -MemberType NoteProperty -Name "Email" -Value $_.properties.item("mail")[0] $obj | Add-Member -MemberType NoteProperty -Name "AccountCreated" -Value $_.properties.item("whencreated")[0] $obj | Add-Member -MemberType NoteProperty -Name "AccountModified" -Value $_.properties.item("WhenChanged")[0] $obj | Add-Member -MemberType NoteProperty -Name "LastLogon" -Value (Get-UTCAge $_.properties.item("lastlogon")[0]) $obj | Add-Member -MemberType NoteProperty -Name "PasswordLastChanged" -Value (Get-UTCAge $_.properties.item("pwdlastset")[0]) $obj | Add-Member -MemberType NoteProperty -Name "PasswordAge" -Value (Get-PwdAge $_.properties.item("pwdlastset")[0]) $obj | Add-Member -MemberType NoteProperty -Name "PasswordExpired" -Value $pwdExpired $obj | Add-Member -MemberType NoteProperty -Name "PasswordNeverExpires" -Value $pwdNeverExpires $obj | Add-Member -MemberType NoteProperty -Name "PasswordChangeAllowed" -Value $pwdChangeAllowed $obj | Add-Member -MemberType NoteProperty -Name "BadPasswordTime" -Value (Get-UTCAge $_.properties.item("BadPassWordTime")[0]) #write object to the pipeline write $obj } #end foreach #end of script