Password expiry notification at logon in Windows 7

notifypsw01

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.

notifypsw02

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.

notifypsw03

Type a Name for the new GPO then click OK.

notifypsw04

Edit the GPO and select User Configuration > Policies > Windows Settings > Scripts (Logon/Logoff) option and right click Logon > Properties.

notifypsw05

In the Scripts section, click Add and specify the .vbs file to be used through the Browse button.

notifypsw06

Select the script then click Open.

notifypsw07

Once the script .vbs has been set, click OK to save the configuration.

notifypsw08

Link the new GPO to the correct OU.

notifypsw09

When the user login into the system, the script displays the popup notification.

notifypsw10

 

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.

notifypsw11

Select the script .ps1 then click Open.

notifypsw12

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.

notifypsw13

At logon, in addition to the popup the user will receive also a notification email in his/her mailbox.

notifypsw14

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

 

password expiry notification windows 7 1