Wednesday, 8 August 2012

Expiring password notifications with Powershell scripting

Let's say that your users have non-domain joined workstation, but use Outlook or Outlook Web Access to access your e-mail infrastructure. These users would never be notified that their domain password is about to expire. Once it does, their only resort would be to contact administrator and ask him to reset his or her password.

Because of this, I wrote a simple Powershell script that is scheduled to run every day and creates a list of user accounts which passwords will expire in less than 7 days and sends them an e-mail notification that they should change their password. Once a user receives this notification, he can use Outlook Web Access to change his password.

Some notes about the script:

  • The script uses System.Globalization.CultureInfo object to format the date returned for Croatian region (hr-HR). If you come from US, you can change hr-HR in the script to en-US
  • The $msg.Body property in the script contains HTML formatted text that user receives as an e-mail message body. You can modify this HTML code, for example, to include your company branding
  • You can modify the $OU property so that script only scopes your external users, users from a specific department, etc.
  • The script does not send e-mail to users whose passwords are already expired or who have "Password never expires" property set
  • You can modify how many days are left until password expiry before you start notifying users. Currently, script notifies users whose passwords expire in less than 7 days.
    You can modify the following line: $DaysToExpire.Days -lt 7
  • The policy works with default domain password policy and with Password Settings Object, a feature of Windows Server 2008 Active Directory
  • You must run the script on a domain controller or on a server with Remote Server Administration Tools installed
  • You must change the variable $smtpServer to your internal mail server (usually Exchange Hub Transport server) that can send e-mails to your users

Here is the script:

PowerGUI Script Editor
Import-Module ActiveDirectory

#System globalization
$ci = New-Object System.Globalization.CultureInfo("hr-HR")

#SMTP server name
$smtpServer = "smtp.domain.local"

#Creating SMTP server object
$smtp = new-object Net.Mail.SmtpClient($smtpServer)

#E-mail structure
Function EmailStructure($to,$expiryDate,$upn)

 #Creating a Mail object
 $msg = new-object Net.Mail.MailMessage

 $msg.IsBodyHtml = $true
 $msg.From = ""
 $msg.Subject = "Password expiration notice"
 $msg.Body = "<html><body><font face='Arial'>This is an automatically generated message from Service Provider Exchange service.<br><br><b>Please note that the password for your account $upn will expire on $expiryDate.</b><br><br>Please change your password immediately or at least before this date as you will be unable to access the service without contacting your administrator.</font></body></html>"
 return $msg

#Set the target OU that will be searched for user accounts
$OU = "OU=Users,DC=domain,DC=local"

$ADAccounts = Get-ADUser -LDAPFilter "(objectClass=user)" -searchbase $OU -properties PasswordExpired, PasswordNeverExpires, PasswordLastSet, Mail, Enabled | Where-object {$_.Enabled -eq $true -and $_.PasswordNeverExpires -eq $false}

Foreach ($ADAccount in $ADAccounts)
 $accountFGPP = Get-ADUserResultantPasswordPolicy $ADAccount

                if ($accountFGPP -ne $null) {
                   $maxPasswordAgeTimeSpan = $accountFGPP.MaxPasswordAge
                } else {
                   $maxPasswordAgeTimeSpan = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge

  #Fill in the user variables
  $samAccountName = $ADAccount.samAccountName
  $userEmailAddress = $ADAccount.Mail
  $userPrincipalName = $ADAccount.UserPrincipalName

  if ($ADAccount.PasswordExpired) {
   Write-host "The password for account $samAccountName has expired!"
  } else {
   $ExpiryDate = $ADAccount.PasswordLastSet + $maxPasswordAgeTimeSpan
   Write-host "The password for account $samAccountName expires on: $ExpiryDate"

   $TodaysDate = Get-Date
   $DaysToExpire = $ExpiryDate - $TodaysDate
   #Write-Host $DaysToExpire.Days
   if ($DaysToExpire.Days -lt 7) {
    $expiryDate = $expiryDate.ToString("d",$ci)
    #Generate e-mail structure and send message
    $msg = EmailStructure $userEmailAddress $expiryDate $userPrincipalName

    Write-Host "The expiration notification e-mail was sent to $userEmailAddress"