Tuesday, March 20, 2012

Password Expiration Email Reminders

Below is a PowerShell script that will email password change reminders to users when their password is about to expire. It uses built in windows functionality and doesn't require any third party software. It's fully customizable with the ability to easily set email intervals.

The default email schedule will email users at 30 days, 15 days, 7 days, 5 days, 3 days, 2 days, 1 day and the day of password expiration. If the user changes their password they will stop receiving email notifications until it becomes time to change their password again.

The script will ignore disabled user accounts and accounts that have the password never expires attribute set. If the user has an associated email address in active directory it will send the reminder to that email address. If the user does not have an associated email address an email will be sent to the address specified by the $adminEmail variable. Thus allowing the admin to be proactive. The script will also email the $adminEmail if a user's account has expired. Again, so that the admin can be proactive. The $adminEmail will receive only one email summary to avoid inbox clutter. The summary will also show user's that are required to change their passwords but have not done so yet. There's usually a good reason they're not logging on. Usually these types of accounts were provision for an employee but that employee never started.

In a nutshell, if you're running the script on a domain controller you should be set. If you want to run it on a member server then execute the following PowerShell commands:

Import-Module ServerManager

Add-WindowsFeature RSAT-AD-PowerShell

You should create a scheduled task to run the script shortly after midnight each night. Before using it, be sure to set the five configuration variables in the Configurable Settings section at the top of the script.

Note: The script is just using the default domain password policy but if you're using the AD DS Fine-Grained Password Policies you should be able to modify the script fairly easily. You'll just have to invoke the Get-ADUserResultantPasswordPolicy commandlet while iterating through the user objects. Also note that all time is done with GMT and time zones are not taken into account. This shouldn't be an issue unless your enterprise spans the globe. If you're enterprise only spans a few contiguous time zones, have the script run at midnight in the time zone that's closest to GMT.

functionEmailAdmin($content)
{if([string]::IsNullOrEmpty($content)-and$alwaysSendAdminSummary){$content="There are no user's with expired passwords or users that need to change their password."}if([string]::IsNullOrEmpty($content)-ne$True){$smtp.Send($fromEmail,$adminEmail,"Password Expiration Summary",$content)}}

return"The password for account """+$user.samAccountName+""" will expire in "+$daysToExpire+" days at "+$pwdExpires+" and there is no associated email address to send a notification to"+[System.Environment]::NewLine+[System.Environment]::NewLine
}

if($user.pwdLastSet-eq0){return"The user account """+$user.samAccountName+""" is set to require a password change at next logon and the user has not yet changed it"+[System.Environment]::NewLine+[System.Environment]::NewLine}else{return"The password for user account """+$user.samAccountName+""" expired "+$daysToExpire+" days ago on "+$pwdExpires+[System.Environment]::NewLine+[System.Environment]::NewLine}
}

foreach($userin$users){#if account is enabled and password never expire flag does not exist, then process userif(($user.enabled-eq$True)-and(($user.userAccountControl-band$ADS_UF_DONT_EXPIRE_PASSWD)-eq0)){$pwdExpires=GetPasswordExpireDate$user$daysToExpire=GetDaysToExpire$pwdExpires

#if day falls on warning intervalif(IsInWarningIntervals$daysToExpire){#if mail attribute is not found in AD, add to admin emailif([string]::IsNullOrEmpty($user.mail)){$adminEmailContent+=AppendAdminEmailNoMail$user}#otherwise email userelse{EmailUser$user}}

#if days to expire is negative, password has expired. add to admin emailif($daysToExpire-lt0){$adminEmailContent+=AppendAdminEmailExpiredAccount$user}}
}

Hi Scott!Totally agree with Geraint, super script. Im having the same issue he had; im getting the admin email without users getting the alert mail. I likely broke it myself, however we dont have a AD sandbox for testing, so I couldnt kick out twenty tests to my company while I tooled with it.

The emails to the users will only go out at the specified intervals. The default is 30 days, 15 days, 7 days, 5 days, 3 days, 2 days, 1 day and the day of password expiration. This means if today is exactly 30 days prior to the user's password expiration then they will get an email. The next day they will not get one. Then when it's 15 days until expiration they will get their second email.

Does the admin email say that there is no associated email address to email the user? If there is no email address associated with the user, the admin email should include the password expiration reminders that get generated on the intervals. It should only be included in the admin email on the interval though.

This was it exactly. I had that idea that night that I was running the script with several test accounts (with expired passwords) instead of live ones. After appending our entire applicable range of password expiry days on the notification trigger date and adding a live account to the AD OU, Voila.

I do not have fine-grained password policies so I did not test this thoroughly. I think it should work though. It should use the file grained policy if one exists for the user and if not it should revert to the default domain policy.

Do me a favor, run the following power shell snippet and let me know what the output is:

This little snippet should get all AD users, try to load the password policy for each user and display the max password age associated to that password policy.

If this outputs nothing, then replacing the GetPasswordExpireDate function probably won't work as described and we'll need to dig further. If it displays a bunch of integers then replacing the GetPasswordExpireDate function should work for you.

Quick question: how do I know that indeed the user is being notified at those intervals??? What I mean is: I receive the admin email with a list of expired and who needs to change password on next login but it does not include a list of users that have been notified, what code should I add to the script to do that and where should I insert it?.I am not verse at powershell at allthanksAL

Easiest way would be to ask them. I purposely did not include this in the admin emails as I wanted to only get admin emails when something was not quite right. I didn't want to liter the admin emails with superfluous informational. If you want to modify the script, the easiest way would be to copy the AppendAdminEmailNoMail function and call that after the EmailUser function call. Add this:

You should be able to filter based on a mask of the useraccountcontrol attribute. Can you explain your situation in a little more detail? Do you know which ones should be valid and which ones shouldn't?