Email notification about password expiration not sent

Hello guys, can you please advise me on script which suppose to send email notification to users which password will expire in 14,7,3 days

Script is partialy working properly, it will export users with the information about password expiration date,however it will not send notofication email to users, which account will expire in for example 14 days

Also when I tried only send-mailmessage part of the script with defined variables, it worked fine as well and test user received an email(so SMTP is configured fine). So I suppose script will drop the user’s information before it will go to “email” part of the script

Import-Module ActiveDirectory

# Create warning dates for future password expiration
$FourteenDayWarnDate = (get-date).adddays(14).ToLongDateString()
$SevenDayWarnDate = (get-date).adddays(7).ToLongDateString()
$ThreeDayWarnDate = (get-date).adddays(3).ToLongDateString()

#Email Variables
#Email Variables
$MailSender = "helpdesk@company.com"
$Subject = 'FYI - Your account password will expire soon'
$EmailStub1 = 'I am a bot and performed this action automatically. I am here to inform you that the password for'
$EmailStub2 = 'will expire in'
$EmailStub3 = 'days on'
$EmailStub4 = '. Please contact the helpdesk if you need assistance changing your password. DO NOT REPLY TO THIS EMAIL.'
$SMTPServer = 'smtp.somecompany.com'

# Provide list of required AD OUs (child OUs are included)
$OU = 
'OU=TEST,DC=company,DC=com'


# Find accounts that are enabled and have expiring passwords
$users = $OU | ForEach-Object {
    Get-ADUser -SearchBase $PSItem -Filter {
        Enabled -eq $True -and PasswordNeverExpires -eq $False -and PasswordLastSet -gt 0
    } -Properties "Name", "EmailAddress", "msDS-UserPasswordExpiryTimeComputed" | Select-Object -Property "Name", "EmailAddress", @{Name = "PasswordExpiry"; Expression = { [datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed").toshortdatestring()}}
    } |  Export-Csv -Path c:\scripts\out2.csv -Encoding UTF8 -NoTypeInformation

# email part of the script
#check password expiration date and send email on match
foreach ($user in $users) {
     if ($user.PasswordExpiry -eq $FourteenDayWarnDate) {
         $days = 14
         $EmailBody = $EmailStub1, $user.name, $EmailStub2, $days, $EmailStub3, $SevenDayWarnDate, $EmailStub4 -join ' '
         Send-MailMessage -To $user.EmailAddress -From $MailSender -SmtpServer $SMTPServer -Subject $Subject -Body $EmailBody
     }
     elseif ($user.PasswordExpiry -eq $SevenDayWarnDate) {
         $days = 7
         $EmailBody = $EmailStub1, $user.name, $EmailStub2, $days, $EmailStub3, $ThreeDayWarnDate, $EmailStub4 -join ' '
         Send-MailMessage -To $user.EmailAddress -From $MailSender -SmtpServer $SMTPServer -Subject $Subject `
         -Body $EmailBody
     }
     elseif ($user.PasswordExpiry -eq $ThreeDayWarnDate) {
         $days = 3
         $EmailBody = $EmailStub1, $user.name, $EmailStub2, $days, $EmailStub3, $OneDayWarnDate, $EmailStub4 -join ' '
         Send-MailMessage -To $user.EmailAddress -From $MailSender -SmtpServer $SMTPServer -Subject $Subject -Body $EmailBody
     } 
    else {}
 } 

Michal,
Welcome to the forum. :wave:t4:

You create a variable $FourteenDayWarnDate for comparison with the format .ToLongDateString(). But then you create a property with the format .toshortdatestring() to compare it against your variable. I wouldn’t expect this to work.

I’d recommend to NOT use formatted strings for comparison. Instead you should use proper DateTime variable types to compare. With the property .Date of a DateTime type you can remove the time information from it to make it easily comparable. :wink:

(Get-Date).Date

Hi Olaf,

you were right, ToLongDateString() and .toshortdatestring() variables were part of the problem. I also removed | export to csv after get-user command and mail notifications started to work.

Thanks for your help.

Just another tip, use a here string for the body and you should use the -BodyAsHtml switch so that you can bold and use basic html formatting in the message. You can get fancy with images and other things, but it will make formatting much simpler:

$expDays = 13
$acct = 'domain\jsmith'
$expDate = (Get-Date).AddDays(13)

$EmailBody = @"
<p>I am a bot and performed this action automatically. I am here to inform you that the password for <b>$acct</b> will expire on <b>$expDate</b> in:</p>

<h3>$expDays days</h3>

<p>Please contact the helpdesk if you need assistance changing your password. DO NOT REPLY TO THIS EMAIL.</p>
"@

$sendMailParams = @{
    To         = $user.EmailAddress
    From       = $MailSender 
    SmtpServer = $SMTPServer
    Subject    = $Subject
    Body       = $EmailBody
    BodyAsHtml = $true
}  

Send-MailMessage @sendMailParams
1 Like

Hi Rob, thanks for the hint with HTML formatting, I re-compiled the script, it is working in Powershell ISE console but I am getting an error, when I want to run it in powershell as a script-see error bellow:

Send-MailMessage : Cannot validate argument on parameter ‘Body’. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At C:\scripts\Pass_expire_notification.ps1:43 char:19

  • Send-MailMessage @sendMailParams -Encoding UTF8 -Priority High
Import-Module ActiveDirectory

# Provide list of required AD OUs (child OUs are included)
$OU = 
'OU=TEST,DC=company,DC=local'

# Create warning dates for future password expiration
$FourteenDayWarnDate = (get-date).adddays(177).TolongDateString()
$SevenDayWarnDate = (get-date).adddays(7).TolongDateString()
$ThreeDayWarnDate = (get-date).adddays(3).TolongDateString()

#Email Variables
$MailSender = "sender@company.com"
$SMTPServer = 'smtp.company.com'
$sendMailParams = @{
    To         = $user.EmailAddress
    From       = $MailSender 
    SmtpServer = $SMTPServer
    Subject    = 'Your password will expire soon'
    Body       = $EmailBody
    BodyAsHtml = $true
}  
### Email Body ###
    

  # Find accounts that are enabled and have expiring passwords
$users = $OU | ForEach {
    Get-ADUser -SearchBase $PSItem -Filter {
        Enabled -eq $True -and PasswordNeverExpires -eq $False -and PasswordLastSet -gt 0
    } -Properties "Name", "EmailAddress", "msDS-UserPasswordExpiryTimeComputed" | Select-Object -Property "Name", "EmailAddress", @{Name = "PasswordExpiry"; Expression = { [datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed").tolongdatestring()}}
    } 

#check password expiration date and send email on match

ForEach ($user in $Users) {
     if ($user.PasswordExpiry -eq $FourteenDayWarnDate) {
         $days = 14
         $EmailBody = @"
         <p>Hello,</p> <p>this is automatic generated email, which inform you, that you password will expire in </p> <h3>$days days</h3>
         <p>If you need assistance, please contact <a href="mailto:helpdesk@company.com">helpdesk.</a>. Please DO not reply to this eamil.</p>
"@

 Send-MailMessage @sendMailParams -Encoding UTF8 -Priority High 
     }

     elseif ($user.PasswordExpiry -eq $SevenDayWarnDate) {
         $days = 7
         $EmailBody = @"
         <p>Hello,</p> <p>this is automatic generated email, which inform you, that you password will expire in </p> <h3>$days days</h3>
         <p>If you need assistance, please contact <a href="mailto:helpdesk@company.com">helpdesk.</a>. Please DO not reply to this eamil.</p>
"@

 Send-MailMessage @sendMailParams -Encoding UTF8 -Priority High
          
     }
     elseif ($user.PasswordExpiry -eq $ThreeDayWarnDate) {
         $days = 3
         $EmailBody = @"
         <p>Hello,</p> <p>this is automatic generated email, which inform you, that you password will expire in </p> <h3>$days days</h3>
         <p>If you need assistance, please contact <a href="mailto:helpdesk@company.com">helpdesk.</a>. Please DO not reply to this eamil.</p>
"@

 Send-MailMessage @sendMailParams -Encoding UTF8 -Priority High 
     } 
    else {}
 }
  

Powershell is going to read from top to bottom, so you are referencing a variable before it’s created (e.g. null) and that is the error. Would recommend changing your approach a bit. When working with dates, you should compare the dates rather than convert them to strings. Don’t have AD handy to test, but assuming msDS-UserPasswordExpiryTimeComputed is a future date of expiration, you can calculate the days until expiration from today (Get-Date) until that expiration date. The email appeared to be the same for each expiration, so you can just set the intervals and see if the days calculated from the dates matches, then send the email.

Import-Module ActiveDirectory

# Provide list of required AD OUs (child OUs are included)
$OUs = 'OU=TEST,DC=company,DC=local'

#Email Variables
$MailSender = "sender@company.com"
$SMTPServer = 'smtp.company.com'

### Email Body ###
    

  # Find accounts that are enabled and have expiring passwords
$users = foreach ($ou in $OUs) {
    $adParams = @{
        SearchBase = $ou
        Filter = {Enabled -eq $True -and PasswordNeverExpires -eq $False -and PasswordLastSet -gt 0 } 
        Properties = "Name", "EmailAddress", "msDS-UserPasswordExpiryTimeComputed", "PasswordLastSet"
    }

    Get-ADUser @adParams | 
    Select-Object -Property "Name", 
                            "EmailAddress", 
                            @{Name = "PasswordExpiry"; Expression = { [datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")}},
                            @{Name='DaysUntilExpiration';Expression={ New-TimeSpan -Start (Get-Date) -End ([datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")) | Select-Object -ExpandProperty Days}}
} 

#check password expiration date and send email on match

ForEach ($user in $Users) {

$EmailBody = @"
    <p>Hello,</p> <p>this is automatic generated email, which inform you, that you password will expire in </p> <h3>$days days</h3>
    <p>If you need assistance, please contact <a href="mailto:helpdesk@company.com">helpdesk.</a>. Please DO not reply to this eamil.</p>
"@
    #if the interval contains the expiration days
    if (14,7,3 -contains $user.DaysUntilExpiration) {

        $sendMailParams = @{
            To         = $user.EmailAddress
            From       = $MailSender 
            SmtpServer = $SMTPServer
            Subject    = 'Your password will expire soon'
            Body       = $EmailBody
            BodyAsHtml = $true
            Encoding   = 'UTF8'
            Priority   = 'High'
        }  

        Send-MailMessage @sendMailParams
    }
 }