When a password is about to expire, Windows systems are able to notify about the expiry. They are also able to ask the user to change his password when the current one expires. This may not be true for non-Windows systems and the management of password policies may become complicated in companies having heterogeneous operating systems (Windows and non-Windows Operating Systems). In similar situations, companies can use third-party tools that allow the notification and the password change. However, this may become costly in terms of expenses and management of the tools.

An alternative solution to manage password change and expiry notifications in similar situations is to use:

  • A password change portal: It can be a TMG/UAG Password Change portal, a developed one or a third party one
  • A script that checks Active Directory on daily basis to identify user accounts that are about to expire and notify the end users by e-mail

In this Wiki article, we are covering the mail expiry notification by sharing a Powershell script that can be used for this purpose.

How Password Policies are applied in AD DS 2008 and higher

Starting from Windows Server 2008 Domain Functional Level, it became possible to apply multiple password policies in an Active Directory domain by using PSO objects and the following logic:

  • If no PSO object is applied on a user then the settings in the Default Domain Password Policy will take effect
  • If a single PSO object is applied on a user then the settings inside the object will take effect
  • If multiple PSO objects are applied on a user then the settings inside the PSO object with the lowest precedence will take effect

How to identify which Password Policy is applied on a user

With PowerShell, it is easy to identify the Password Policy that is applied to a user. This could be done by using the following cmdlets:

  • Get-ADDefaultDomainPasswordPolicy: This cmdlet allows getting the Password Policy settings applied in the Default Domain Policy
  • Get-ADUserResultantPasswordPolicy: This cmdlet allows getting the Resultant Password Policy that is taking effect via the applied PSO object with the lowest precedence. If the output is $null then no PSO object is applied on the user

So, to identify the Password Policy that is getting applied to a user, you need first to use Get-ADUserResultantPasswordPolicy. If the output is not $null then you will have the Password Policy that is applied. If the output is $null then the Password Policy of the Default Domain Policy is getting applied and you can get the settings by using Get-ADDefaultDomainPasswordPolicy.

Script to notify Active Directory users about the expiry of their passwords

Below is a script that is able to identify Active Directory accounts that are about to expire and sends a mail notification to the end users. It optionally allows sending the applied Password Policy settings which make easier for users to choose a new password that meets the company requirements.

You will need to update the following parameters:

  • $verbose: Set it to $true if you would like to send the Password Policy settings to the end users. If you do not want to use this optional feature, you can set the value to $false.
  • $notificationstartday: Set the value of the default interval to start notifying the users about the expiry (This is the delta between the current date and the expiry date)
  • $sendermailaddress: Set the e-mail address that will be used to send mail notifications. It can be, as an example, your Service Desk e-mail address
  • $SMTPserver: Set the DNS name or the IP address of your SMTP server
  • $DN: Set the Distinguished Name of the start of a search for AD user accounts. You can update it to be your Domain Distinguished Name if you would like to scan all the users in your Active Directory domain
##############Variables#################            
$verbose = $true            
$notificationstartday = 14            
$sendermailaddress = "no-reply@contoso.com"            
$SMTPserver = "mail.contoso.com"            
$DN = "DC=contoso,DC=com"            
########################################            
            
##############Function##################            
function PreparePasswordPolicyMail ($ComplexityEnabled,$MaxPasswordAge,$MinPasswordAge,$MinPasswordLength,$PasswordHistoryCount)            
{            
    $verbosemailBody = "Below is a summary of the applied Password Policy settings:`r`n`r`n"            
    $verbosemailBody += "Complexity Enabled = " + $ComplexityEnabled + "`r`n`r`n"            
    $verbosemailBody += "Maximum Password Age = " + $MaxPasswordAge + "`r`n`r`n"            
    $verbosemailBody += "Minimum Password Age = " + $MinPasswordAge + "`r`n`r`n"            
    $verbosemailBody += "Minimum Password Length = " + $MinPasswordLength + "`r`n`r`n"            
    $verbosemailBody += "Remembered Password History = " + $PasswordHistoryCount + "`r`n`r`n"            
    return $verbosemailBody            
}            
            
function SendMail ($SMTPserver,$sendermailaddress,$usermailaddress,$mailBody)            
{            
    $smtpServer = $SMTPserver            
    $msg = new-object Net.Mail.MailMessage            
    $smtp = new-object Net.Mail.SmtpClient($smtpServer)            
    $msg.From = $sendermailaddress            
    $msg.To.Add($usermailaddress)            
    $msg.Subject = "Your password is about to expire"            
    $msg.Body = $mailBody            
    $smtp.Send($msg)            
}            
########################################            
            
##############Main######################            
$domainPolicy = Get-ADDefaultDomainPasswordPolicy            
$passwordexpirydefaultdomainpolicy = $domainPolicy.MaxPasswordAge.Days -ne 0            
            
if($passwordexpirydefaultdomainpolicy)            
{            
    $defaultdomainpolicyMaxPasswordAge = $domainPolicy.MaxPasswordAge.Days            
    if($verbose)            
    {            
        $defaultdomainpolicyverbosemailBody = PreparePasswordPolicyMail $PSOpolicy.ComplexityEnabled $PSOpolicy.MaxPasswordAge.Days $PSOpolicy.MinPasswordAge.Days $PSOpolicy.MinPasswordLength $PSOpolicy.PasswordHistoryCount            
    }            
}            
            
foreach ($user in (Get-ADUser -SearchBase $DN -Filter * -properties mail))            
{            
    $samaccountname = $user.samaccountname            
    $PSO= Get-ADUserResultantPasswordPolicy -Identity $samaccountname            
    if ($PSO -ne $null)            
    {                         
        $PSOpolicy = Get-ADUserResultantPasswordPolicy -Identity $samaccountname            
        $PSOMaxPasswordAge = $PSOpolicy.MaxPasswordAge.days            
        $pwdlastset = [datetime]::FromFileTime((Get-ADUser -LDAPFilter "(&(samaccountname=$samaccountname))" -properties pwdLastSet).pwdLastSet)            
        $expirydate = ($pwdlastset).AddDays($PSOMaxPasswordAge)            
        $delta = ($expirydate - (Get-Date)).Days            
        $comparionresults = (($expirydate - (Get-Date)).Days -le $notificationstartday) -AND ($delta -ge 1)            
        if ($comparionresults)            
        {            
            $mailBody = "Dear " + $user.GivenName + ",`r`n`r`n"            
            $mailBody += "Your password will expire after " + $delta + " days. You will need to change your password to keep using it.`r`n`r`n"            
            if ($verbose)            
            {            
                $mailBody += PreparePasswordPolicyMail $PSOpolicy.ComplexityEnabled $PSOpolicy.MaxPasswordAge.Days $PSOpolicy.MinPasswordAge.Days $PSOpolicy.MinPasswordLength $PSOpolicy.PasswordHistoryCount            
            }            
            $mailBody += "`r`n`r`nYour IT Department"            
            $usermailaddress = $user.mail            
            SendMail $SMTPserver $sendermailaddress $usermailaddress $mailBody            
        }            
    }            
    else            
    {            
        if($passwordexpirydefaultdomainpolicy)            
        {            
            $pwdlastset = [datetime]::FromFileTime((Get-ADUser -LDAPFilter "(&(samaccountname=$samaccountname))" -properties pwdLastSet).pwdLastSet)            
            $expirydate = ($pwdlastset).AddDays($defaultdomainpolicyMaxPasswordAge)            
            $delta = ($expirydate - (Get-Date)).Days            
            $comparionresults = (($expirydate - (Get-Date)).Days -le $notificationstartday) -AND ($delta -ge 1)            
            if ($comparionresults)            
            {            
                $mailBody = "Dear " + $user.GivenName + ",`r`n`r`n"            
                $delta = ($expirydate - (Get-Date)).Days            
                $mailBody += "Your password will expire after " + $delta + " days. You will need to change your password to keep using it.`r`n`r`n"            
                if ($verbose)            
                {            
                    $mailBody += $defaultdomainpolicyverbosemailBody            
                }            
                $mailBody += "`r`n`r`nYour IT Department"            
                $usermailaddress = $user.mail            
                SendMail $SMTPserver $sendermailaddress $usermailaddress $mailBody            
            }            
            
        }            
    }            
}


Below are screen captures we took from the output the screen:
  • When the verbose mode is enabled:

  • When the verbose mode is disabled

How is the script working

The script will query every AD user starting from the Domain / ContainerOrganizational Unit you specify in the $DN parameter, identifies if a password policy is applied or not and then checks if the user password will expire in less or equal to the number of days defined in $notificationstartday. If this condition is true then a mail notification will be sent to the end user (The applied Password Policy settings will be sent as part of the mail if a $verbose parameter is set to $true).

The script identifies if a user password will expire soon or not by doing a comparison in which it uses the current date, the value of pwdlastset user attribute and the value of $notificationstartday parameter value.