In Windows 7 the password expiry notification is shown just for few seconds in the bottom right of the screen, five days in advance by default.
Unfortunately the notification message is not so visible and often it is hard to be noted. The consequence is the password expiration making the network services inaccessible to the user.
To solve this situation and having a more clear notification, a popup during login and sending an email to the user mailbox could help.
Display a popup at logon
Showing a popup during login could be a first solution. This target can be achieved using a script .vbs launched with GPO.
In the script here below, the warningDays variable specifies how many days notification popup is shown before the password get expired.
'======================================== ' First, get the domain policy. '======================================== Dim oDomain Dim oUser Dim maxPwdAge Dim numDays Dim warningDays warningDays = 14 Set LoginInfo = CreateObject("ADSystemInfo") Set objUser = GetObject("LDAP://" & LoginInfo.UserName & "") strDomainDN = UCase(LoginInfo.DomainDNSName) strUserDN = LoginInfo.UserName Set oDomain = GetObject("LDAP://" & strDomainDN) Set maxPwdAge = oDomain.Get("maxPwdAge") '======================================== ' Calculate the number of days that are ' held in this value. '======================================== numDays = CCur((maxPwdAge.HighPart * 2 ^ 32) + _ maxPwdAge.LowPart) / CCur(-864000000000) 'WScript.Echo "Maximum Password Age: " & numDays '======================================== ' Determine the last time that the user ' changed his or her password. '======================================== Set oUser = GetObject("LDAP://" & strUserDN) '======================================== ' Add the number of days to the last time ' the password was set. '======================================== whenPasswordExpires = DateAdd("d", numDays, oUser.PasswordLastChanged) fromDate = Date daysLeft = DateDiff("d",fromDate,whenPasswordExpires) 'WScript.Echo "Password Last Changed: " & oUser.PasswordLastChanged if (daysLeft < warningDays) and (daysLeft > -1) then Msgbox "La password scade tra " & daysLeft & " giorni" & " il " & _ whenPasswordExpires &chr(13) & chr(13) & "Premi CTRL + ALT + CANC " &_ "e seleziona l'opzione 'Cambia password'.", 0, "AVVISO SCADENZA PASSWORD" End if '======================================== ' Clean up. '======================================== Set oUser = Nothing Set maxPwdAge = Nothing Set oDomain = Nothing
To execute the script during login, create a new GPO with the tool Group Policy Management. Right click Group Policy Objects item and select New.
Type a Name for the new GPO then click OK.
Edit the GPO and select User Configuration > Policies > Windows Settings > Scripts (Logon/Logoff) option and right click Logon > Properties.
In the Scripts section, click Add and specify the .vbs file to be used through the Browse button.
Select the script then click Open.
Once the script .vbs has been set, click OK to save the configuration.
Link the new GPO to the correct OU.
When the user login into the system, the script displays the popup notification.
Sending a notification email
To make the password notification as clear as possible, in addition to the popup during the login, we could send also an email to the user mailbox.
The email is sent using a PowerShell script launched at logon. The script in the example has been taken from the www.rlmueller.net website and customized to match the network environment.
Trap {"Error: $_"; Break;} # Specify number of days. Any users whose passwords expire within # this many days after today will be processed. $intDays = 14 # Email settings. $Script:From = "myemailaddress@mydomain.com" $Script:Subject = "Password Expiration Notice" $Server = "smtp.mydomain.com" $Port = 25 $Client = New-Object System.Net.Mail.SmtpClient $Server, $Port # You may need to provide credentials. $Client.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials Function SendEmail($To, $Body) { $Message = New-Object System.Net.Mail.MailMessage ` $Script:From, $To, $Script:Subject, $Body $Client.Send($Message) } # Retrieve Domain maximum password age policy, in days. $D = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() $Domain = [ADSI]"LDAP://$D" $MPA = $Domain.maxPwdAge.Value # Convert to Int64 ticks (100-nanosecond intervals). $lngMaxPwdAge = $Domain.ConvertLargeIntegerToInt64($MPA) # Convert to days. $MaxPwdAge = -$lngMaxPwdAge/(600000000 * 1440) # Determine the password last changed date such that the password # would just now be expired. We will not process any users whose # password has already expired. $Now = Get-Date $Date1 = $Now.AddDays(-$MaxPwdAge) # Determine the password last changed date such the password # will expire $intDays in the future. $Date2 = $Now.AddDays($intDays - $MaxPwdAge) # Convert from PowerShell ticks to Active Directory ticks. $64Bit1 = $Date1.Ticks - 504911232000000000 $64Bit2 = $Date2.Ticks - 504911232000000000 $Searcher = New-Object System.DirectoryServices.DirectorySearcher $Searcher.PageSize = 200 $Searcher.SearchScope = "subtree" # Filter on user objects where the password expires between the # dates specified, the account is not disabled, password never # expires is not set, password not required is not set. # and password cannot change is not set. $Searcher.Filter = "(&(objectCategory=person)(objectClass=user)" ` + "(pwdLastSet>=" + $($64Bit1) + ")" ` + "(pwdLastSet<=" + $($64Bit2) + ")" ` + "(!userAccountControl:1.2.840.113556.1.4.803:=2)" ` + "(!userAccountControl:1.2.840.113556.1.4.803:=65536)" ` + "(!userAccountControl:1.2.840.113556.1.4.803:=32)" ` + "(!userAccountControl:1.2.840.113556.1.4.803:=48))" $Searcher.PropertiesToLoad.Add("sAMAccountName") > $Null $Searcher.PropertiesToLoad.Add("pwdLastSet") > $Null $Searcher.PropertiesToLoad.Add("mail") > $Null $Searcher.PropertiesToLoad.Add("proxyAddresses") > $Null $Searcher.SearchRoot = "LDAP://" + $Domain.distinguishedName $Results = $Searcher.FindAll() ForEach ($Result In $Results) { $Name = $Result.Properties.Item("sAMAccountName") $PLS = $Result.Properties.Item("pwdLastSet") $Mail = $Result.Properties.Item("mail") $Addresses = $Result.Properties.Item("proxyAddresses") If ($PLS.Count -eq 0) { $Date = [DateTime]0 } Else { # Interpret 64-bit integer as a date. $Date = [DateTime]$PLS.Item(0) } # Convert from .NET ticks to Active Directory Integer8 ticks. # Also, convert from UTC to local time. $PwdLastSet = $Date.AddYears(1600).ToLocalTime() # Determine when password expires. $PwdExpires = $PwdLastSet.AddDays($MaxPwdAge) # Determine email address. If ("$Mail" -eq "") { ForEach ($Address In $Addresses) { $Prefix = $Address.SubString(0, 5) If (($Prefix -ceq "SMTP:") -or ($Prefix -ceq "X400:")) { $Mail = $Address.SubString(5) Break } } } If ("$Mail" -ne "") { $Notice = "Password for user $Name must be changed by $PwdExpires" SendEmail $Mail $Notice "Email sent to $Name ($Mail), password expires $PwdExpires" } Else { "$Name has no email, but password expires $PwdExpires" "DN: $DN" } }
The script is added to the previously created GPO by editing the option User Configuration > Policies > Windows Settings > Scripts (Logon/Logoff). Right click Logon > Properties.
Select PowerShell Scripts pane and click Add. Click the Browse button and select this time the script .ps1.
Select the script .ps1 then click Open.
If you are going to use both solutions, you can set an execution scripts order setting the option “For this GPO, run scripts in the following order”. Click OK to save the configuration.
At logon, in addition to the popup the user will receive also a notification email in his/her mailbox.
Notify the password expiration with a popup, with an email or with both solutions depends on corporate policies and needs. You could set the system to show initially only the popup then the email.
Update 25/08/2014
To execute the script powershell avoiding the problem of emails generated for each user that logs on the system, run the script on daily basis using the Windows Task Scheduler removing the script from the GPO.
Create a new task in the Task Scheduler and set parameters as follow:
- Action: Start a program
- Program/Script: powershell.exe
- Add arguments (optional): -f “C:\Scripts\psw_warning_email.ps1?
Configure the execution with the following options:
- Use the following user account: a Domain Admin account
- Run whether user is logged on or not
- Run with highest privileges