PSPasswordExpiryNotifications – PowerShell Module

Following the PowerShell Module provides a different approach to scheduling password notifications for expiring Active Directory user accounts. While most of the scripts require knowledge on HTML… this one is just one config file and a bit of tingling around with texts. Whether this is good or bad it's up to you to decide. I do plan to add an option to use an external HTML template if there will be requests for that.

Note worthy features
Easily create emails that will be sent to users just before they expire
Define periods when the emails arrive (10 days, 3 days, 1 day, you choose…)
Define alerts for users Manager
Define alerts for administrators with extensive summary
Setup Task Scheduler with daily alerts
Useful links
Code is published on GitHub
Issues should be reported on GitHub
Code is published as a module on PowerShellGallery
Required prerequisites

Before you are able to use this script you need to do a few manual steps. Since this script is published as a module… it's quite easy to set this up. Just execute commands below and use the below configuration file

Install-Module PSPasswordExpiryNotifications
Install-Module PSWriteColor

Update-Module # if you use already some of modules I've created

Below you can find a basic example for password notifications. It may seem intimidating at first but when you take a look at how each section is built you will quickly understand the concept. If you want to get the full config file just go to the bottom of this page but it's highly advised to read thru options to understand what they do in chunks.

EmailParameters Section
EmailParameters – fillout your Email Server information. It supports relays, standard servers, whatever.
$EmailParameters = @{
    EmailFrom = "monitoring@domain.pl"
    EmailTo = "przemyslaw.klys@domain.pl" # your default email field (IMPORTANT)
    EmailCC = "" 
    EmailBCC = ""
    EmailReplyTo = "helpdesk@domain.pl" # email to use when users press Reply
    EmailServer = ""
    EmailServerPassword = ""
    EmailServerPort = "587"
    EmailServerLogin = ""
    EmailServerEnableSSL = 1
    EmailEncoding = "Unicode"
    EmailSubject = "[Password Expiring] Your password will expire on <<DateExpiry>> (<<TimeToExpire>> days)"
    EmailPriority = "Low" # Normal, High
}

There are couple of important elements here (all are useful but you should pay attention to those):

  • EmailTo – while generally this field is not used to send emails to users you will want to set this up to your email for testing phase. When you set one of the options under configuration this field is utilized instead of users emails. So instead of users… you get their expiry notifications.
  • EmailReplyTo – this field allows users to respond to emails. When they press ‘Reply' in their email client the field will be different then the one you sent mail from. You can send email from no-reply@evotec.pl and at same time when user presses respond it will fill different email.
  • EmailSubject – as the field says it's the subject users will see. You can use <<DateExpiry>> and <<TimeToExpire>> variables which will be prefilled with proper data.
FormattingParameters Section
FormattingParameters section is having all the bells and whistles on how the emails sent to users / managers will look like.
$FormattingParameters = @{
    CompanyBrandingTemplate = 'TemplateDefault'
    CompanyBranding = @{
        Logo = "https://evotec.xyz/wp-content/uploads/2015/05/Logo-evotec-012.png"
        Width = "200"
        Height = ""
        Link = "https://evotec.xyz"
    }

    FontFamily = "Calibri Light"
    FontSize = "9pt"

    FontHeadingFamily = "Calibri Light"
    FontHeadingSize = "12pt"

    FontTableHeadingFamily = "Calibri Light"
    FontTableHeadingSize = "9pt"

    FontTableDataFamily = "Calibri Light"
    FontTableDataSize = "9pt"

    Colors = @{
        Red = "reset it"
        Blue = "please contact", "CTRL+ALT+DEL"
        Green = "+48 22 600 20 20"
    }
    Styles = @{
        B = "To change your password", "<<DisplayName>>", "Change a password" # BOLD
        I = "password" # Italian
        U = "Help Desk" # Underline
    }
    Links = @{
        ClickHere = @{
            Link = "https://password.evotec.pl"
            Text = "Click Here"
            Color = "Blue"

        }
        ClickingHere = @{ Link = "https://passwordreset.microsoftonline.com/"
            Text = "clicking here"
            Color = "Red"
        }
        VisitingPortal = @{ Link = "https://evotec.xyz"
            Text = "visiting Service Desk Portal"
            Color = "Red"
        }
        ServiceDeskEmail = @{
            Link = "helpdesk@domain.pl" # if contains @ treated as email
            Text = "Service Desk"
            Color = "Red"
            Subject = "I need help with my password" # Email subject used for email links only / ignored for http/https links
        }
    }


    Template = "
    Hello <<DisplayName>>,
    Your password is due to expire in <<TimeToExpire>> days.

    To change your password:
    - press CTRL+ALT+DEL -> Change a password...

    If you have forgotten you password and need to reset it, you can do this by <<ClickingHere>>
    In case of problems please contact HelpDesk by <<VisitingPortal>> or by sending an email to <<ServiceDeskEmail>>.

    Alternatively you can always call Service Desk at +48 22 600 20 20

    Kind regards,
    Evotec IT"

    TemplateForManagers = "
    Hello <<ManagerDisplayName>>,

    Below you can find a list of users who are about to expire in next few days.

    <<ManagerUsersTable>>

    This is just an informational message.. There is no need to do anything about it unless you see some disprepency.

    Kind regards,
    Evotec IT"

}

Since this section is large enough we need to split this up a bit. I'll go from the bottom to the top as it will make more sense that way.

    Template = "
    Hello <<DisplayName>>,
    Your password is due to expire in <<TimeToExpire>> days.

    To change your password:
    - press CTRL+ALT+DEL -> Change a password...

    If you have forgotten you password and need to reset it, you can do this by <<ClickingHere>>
    In case of problems please contact HelpDesk by <<VisitingPortal>> or by sending an email to <<ServiceDeskEmail>>.

    Alternatively you can always call Service Desk at +48 22 600 20 20

    Kind regards,
    Evotec IT"

    TemplateForManagers = "
    Hello <<ManagerDisplayName>>,

    Below you can find a list of users who are about to expire in next few days.

    <<ManagerUsersTable>>

    This is just an informational message.. There is no need to do anything about it unless you see some disprepency.

    Kind regards,
    Evotec IT"

As you can see above you've 2 templates. First template is standard user template. 2nd template is what manager of the user will get (if you enable that of course). This is where you simply write how you want your email text to sound like. It's important to note here that spaces, blank lines do matter in here. So just format it the way you like it. This is also the section where you can use of 7 pre-defined Active Directory elements.

  • <<DisplayName>> – Display Name of the user
  • <<DateExpiry>> – Date/Time when account will expire
  • <<GivenName>> – First Name in AD
  • <<Surname>> – Surname in AD
  • <<TimeToExpire>> – Time to expire (in days)
  • <<ManagerDisplayName>> – Display Name for Manager assigned to user in AD
  • <<ManagerEmail>> – Manager's email set in Active Directory for user

Next part of the Formatting Section is a bit of secret souce. It's built on 3 subsections. Colors, Styles and Links. The way it works is that while you didn't had a need to define anything in HTML we now have to define which words, which sentences get the style & color treatment. It works by taking a Template and finding each defined word/sentence within particular section and applies color or style.

    Colors = @{
        Red = "reset it"
        Blue = "please contact", "CTRL+ALT+DEL"
        Green = "+48 22 600 20 20"
    }
    Styles = @{
        B = "To change your password", "<<DisplayName>>", "Change a password" # BOLD
        I = "password" # Italian
        U = "Help Desk" # Underline
    }  

The tricky part here is that one needs to be aware of order consequences. The script goes from the top to bottom. So it first applies Red color, then blue, and then green. So when you apply Red color to word “Reset” and then would like to apply Blue to “Reset it” the script won't be able to find that sentence. It happens because the script at that very moment sees the “Reset it” sequence as <font color=`”red`”>Reset</font>it and therefore it's no longer able to find Reset it as a single instance. The workaround for this is to change the order of colors or styles within subsection so that the script takes them in order you want.

Lastly while in the configuration file I've defined colors and styles you're free to add your own. As long as you know the color codes, style codes the script is “smart” enough to put whatever you need. So feel free to explore html colors/styles and add your own to the configuration. It should even work with ‘#00000' codes if you're into that high customization.

Then there's a Links subsection which basically works in a way where you put a marker in the template such as <<clickhere>> or <<thisismymarker>> and you build it's options such as Link, Text, Color and for emails Subject field. If @ is detected in Link field link is converted into email link. Keep in mind you can have as many links as you want as long as you keep to defined format.

    Links = @{
        ClickHere = @{
            Link = "https://password.evotec.pl"
            Text = "Click Here"
            Color = "Blue"

        }
        ClickingHere = @{ Link = "https://passwordreset.microsoftonline.com/"
            Text = "clicking here"
            Color = "Red"
        }
        VisitingPortal = @{ Link = "https://evotec.xyz"
            Text = "visiting Service Desk Portal"
            Color = "Red"
        }
        ServiceDeskEmail = @{
            Link = "helpdesk@domain.pl" # if contains @ treated as email
            Text = "Service Desk"
            Color = "Red"
            Subject = "I need help with my password" # Email subject used for email links only / ignored for http/https links
        }
    }

The last part of Formatting Section (being actually at the top of it) is the Template Type, Company Branding (logo and such) and font settings. Currently there is only one style template defined so not really anything you would like to change/add here but if there will be requests for new templates or other people will supply them… there is such option.

    CompanyBrandingTemplate = 'TemplateDefault'
    CompanyBranding = @{
        Logo = "https://evotec.xyz/wp-content/uploads/2015/05/Logo-evotec-012.png"
        Width = "200"
        Height = ""
        Link = "https://evotec.xyz"
    }

    FontFamily = "Calibri Light"
    FontSize = "9pt"

    FontHeadingFamily = "Calibri Light"
    FontHeadingSize = "12pt"

    FontTableHeadingFamily = "Calibri Light"
    FontTableHeadingSize = "9pt"

    FontTableDataFamily = "Calibri Light"
    FontTableDataSize = "9pt"
ConfigurationParameters Section
ConfigurationParameters – this section is all about who should get reminders (users, managers, admins) and when they should get it. You can put as many notifications as you want on 1st,3rd, 10th day or 1,2,6,10,15, you can test some things before your prime time rollout for  password notifications. This section can be divided into 3 smaller subsections. 1st section is all about users.
  • Enable – as the name suggests. Enables / Disables emails for Users
  • RemindersDisplayOnly – doesn't sent actual email but displays how the HTML will look like – useful only for debugging
  • SendToDefaultEmail – this one is key setting. If you set it to $true it will send email to user but instead of real users email it will use your default one. That way you can enable alerts and get all alerts to yourself. Useful for testing templates…
  • Reminders – you can define as many alerts on any days you want. The left part (name) doesn't really matter. You can call it Notification365 and assign it 15. The order doesn't matter as well. So you can put 1,5,3,10,20,50,90 if needed. While I could define this as a one variable for some reason I've defined each day separately. Maybe for future use. I don't know. Don't ask me why 🙂
    RemindersSendToUsers = @{
        Enable = $true # doesn't processes this section at all if $false
        RemindersDisplayOnly = $false # prevents sending any emails (good for testing) - including managers
        SendToDefaultEmail = $true # if enabled $EmailParameters are used (good for testing)
        Reminders = @{
            Notification1 = 1
            Notification2 = 3
            Notification3 = 10
        }
    }
Second section RemindersSendToManager is about managers. When you Enable this section if user has Manager the manager gets an email with  a table of users he manages that expire soon. It uses TemplateForManagers from the FormattingParameters section for that. It also utilizes <<ManagerUsersTable>> marker where to put the table.
    RemindersSendToManager = @{
        Enable = $true # doesn't processes this section at all if $false
        RemindersDisplayOnly = $false # prevents sending any emails (good for testing)
        SendToDefaultEmail = $true # if enabled $EmailParameters are used (good for testing)
        ManagersEmailSubject = "Summary of password reminders (for users you manage)"
        Reports = @{
            IncludePasswordNotificationsSent = @{
                Enabled = $true
                IncludeNames = 'UserPrincipalName', 'DisplayName', 'DateExpiry', 'DaysToExpire', 'SamAccountName', 'Manager', 'ManagerEmail', 'PasswordLastSet', 'EmailSent', 'EmailSentTo'
                TextBeforeReport = '"Following users which you are listed as manager for have their passwords expiring soon:"'

            }
        }
    }
  • Enable – as the name suggests. Enables / Disables emails for Managers
  • RemindersDisplayOnly – doesn't send actual email but displays how the HTML will look like – useful only for debugging
  • SendToDefaultEmail – this one is key setting. If you set it to $true it will send email to manager but instead of real managers email it will substitute it with the default one.
  • Reports – this section holds just 1 report and defined columns for that report. I would leave defaults … but you're free to change it
Third section RemindersSendToAdmins is all about informing admins about current users status. When you Enable this section Admins will get separate email each day with up to 4 reports:
    RemindersSendToAdmins = @{
        Enable = $true # doesn't processes this section at all
        RemindersDisplayOnly = $false # prevents sending any emails (good for testing)
        AdminsEmail = 'admin-it@evotec.pl', 'przemyslaw.klys@domain.pl'
        AdminsEmailSubject = "[Reporting Evotec] Summary of password reminders"
        Reports = @{
            IncludePasswordNotificationsSent = @{
                Enabled = $true
                IncludeNames = 'UserPrincipalName', 'DisplayName', 'DateExpiry', 'DaysToExpire', 'SamAccountName', 'Manager', 'ManagerEmail', 'PasswordLastSet', 'EmailSent', 'EmailSentTo'
            }
            IncludeExpiringImminent = @{
                Enabled = $true
                IncludeNames = 'UserPrincipalName', 'DisplayName', 'DateExpiry', 'DaysToExpire', 'PasswordExpired', 'SamAccountName', 'Manager', 'ManagerEmail', 'PasswordLastSet'
            }
            IncludeExpiringCountdownStarted = @{
                Enabled = $true
                IncludeNames = 'UserPrincipalName', 'DisplayName', 'DateExpiry', 'DaysToExpire', 'PasswordExpired', 'SamAccountName', 'Manager', 'ManagerEmail', 'PasswordLastSet'
            }
            IncludeExpired = @{
                Enabled = $true
                IncludeNames = 'UserPrincipalName', 'DisplayName', 'DateExpiry', 'PasswordExpired', 'SamAccountName', 'Manager', 'ManagerEmail', 'PasswordLastSet'
            }
        }
    }
  • IncludePasswordNotificationsSent – is report about all emails sent that day about expiring passwords
  • IncludeExpiringImminent – is report about users expiring soon (lowest value from reminders)
  • IncludeExpiringCountdownStarted – is report about users that counting just started for (highest value from reminders)
  • IncludeExpired – shows users who are already expired (and you can expect problems from their side)
Example config for Password Notifications (copy/paste)

Following configuration file/script to setup in Task Scheduler is prefilled with some information already. You just need to make few adjustments (or if you know what you're doing a lof of them and you're ready to go. If you don't play much with it you can have Password Notifications up and running within 1 hour.

Import-Module PSPasswordExpiryNotifications
Import-Module PSWriteColor
Import-Module ActiveDirectory

$EmailParameters = @{
    EmailFrom = "monitoring@domain.pl"
    EmailTo = "przemyslaw.klys@domain.pl" #
    EmailCC = ""
    EmailBCC = ""
    EmailReplyTo = "helpdesk@domain.pl"
    EmailServer = ""
    EmailServerPassword = ""
    EmailServerPort = "587"
    EmailServerLogin = ""
    EmailServerEnableSSL = 1
    EmailEncoding = "Unicode"
    EmailSubject = "[Password Expiring] Your password will expire on <<DateExpiry>> (<<TimeToExpire>> days)"
    EmailPriority = "Low" # Normal, High
}

$FormattingParameters = @{
    CompanyBrandingTemplate = 'TemplateDefault'
    CompanyBranding = @{
        Logo = "https://evotec.xyz/wp-content/uploads/2015/05/Logo-evotec-012.png"
        Width = "200"
        Height = ""
        Link = "https://evotec.xyz"
    }

    FontFamily = "Calibri Light"
    FontSize = "9pt"

    FontHeadingFamily = "Calibri Light"
    FontHeadingSize = "12pt"

    FontTableHeadingFamily = "Calibri Light"
    FontTableHeadingSize = "9pt"

    FontTableDataFamily = "Calibri Light"
    FontTableDataSize = "9pt"

    Colors = @{
        Red = "reset it"
        Blue = "please contact", "CTRL+ALT+DEL"
        Green = "+48 22 600 20 20"
    }
    Styles = @{
        B = "To change your password", "<<DisplayName>>", "Change a password" # BOLD
        I = "password" # Italian
        U = "Help Desk" # Underline
    }
    Links = @{
        ClickHere = @{
            Link = "https://password.evotec.pl"
            Text = "Click Here"
            Color = "Blue"

        }
        ClickingHere = @{ Link = "https://passwordreset.microsoftonline.com/"
            Text = "clicking here"
            Color = "Red"
        }
        VisitingPortal = @{ Link = "https://evotec.xyz"
            Text = "visiting Service Desk Portal"
            Color = "Red"
        }
        ServiceDeskEmail = @{
            Link = "helpdesk@domain.pl" # if contains @ treated as email
            Text = "Service Desk"
            Color = "Red"
            Subject = "I need help with my password" # Email subject used for email links only / ignored for http/https links
        }
    }


    Template = "
    Hello <<DisplayName>>,
    Your password is due to expire in <<TimeToExpire>> days.

    To change your password:
    - press CTRL+ALT+DEL -> Change a password...

    If you have forgotten you password and need to reset it, you can do this by <<ClickingHere>>
    In case of problems please contact HelpDesk by <<VisitingPortal>> or by sending an email to <<ServiceDeskEmail>>.

    Alternatively you can always call Service Desk at +48 22 600 20 20

    Kind regards,
    Evotec IT"

    TemplateForManagers = "
    Hello <<ManagerDisplayName>>,

    Below you can find a list of users who are about to expire in next few days.

    <<ManagerUsersTable>>

    This is just an informational message.. There is no need to do anything about it unless you see some disprepency.

    Kind regards,
    Evotec IT"

}
$ConfigurationParameters = @{
    RemindersSendToUsers = @{
        Enable = $true # doesn't processes this section at all if $false
        RemindersDisplayOnly = $false # prevents sending any emails (good for testing) - including managers
        SendToDefaultEmail = $true # if enabled $EmailParameters are used (good for testing)
        Reminders = @{
            Notification1 = 1
            Notification2 = 3
            Notification3 = 10
        }
    }
    RemindersSendToManager = @{
        Enable = $true # doesn't processes this section at all if $false
        RemindersDisplayOnly = $false # prevents sending any emails (good for testing)
        SendToDefaultEmail = $true # if enabled $EmailParameters are used (good for testing)
        ManagersEmailSubject = "Summary of password reminders (for users you manage)"
        Reports = @{
            IncludePasswordNotificationsSent = @{
                Enabled = $true
                IncludeNames = 'UserPrincipalName', 'DisplayName', 'DateExpiry', 'DaysToExpire', 'SamAccountName', 'Manager', 'ManagerEmail', 'PasswordLastSet', 'EmailSent', 'EmailSentTo'
                TextBeforeReport = '"Following users which you are listed as manager for have their passwords expiring soon:"'

            }
        }
    }
    RemindersSendToAdmins = @{
        Enable = $true # doesn't processes this section at all
        RemindersDisplayOnly = $false # prevents sending any emails (good for testing)
        AdminsEmail = 'admin-it@evotec.pl', 'przemyslaw.klys@domain.pl'
        AdminsEmailSubject = "[Reporting Evotec] Summary of password reminders"
        Reports = @{
            IncludePasswordNotificationsSent = @{
                Enabled = $true
                IncludeNames = 'UserPrincipalName', 'DisplayName', 'DateExpiry', 'DaysToExpire', 'SamAccountName', 'Manager', 'ManagerEmail', 'PasswordLastSet', 'EmailSent', 'EmailSentTo'
            }
            IncludeExpiringImminent = @{
                Enabled = $true
                IncludeNames = 'UserPrincipalName', 'DisplayName', 'DateExpiry', 'DaysToExpire', 'PasswordExpired', 'SamAccountName', 'Manager', 'ManagerEmail', 'PasswordLastSet'
            }
            IncludeExpiringCountdownStarted = @{
                Enabled = $true
                IncludeNames = 'UserPrincipalName', 'DisplayName', 'DateExpiry', 'DaysToExpire', 'PasswordExpired', 'SamAccountName', 'Manager', 'ManagerEmail', 'PasswordLastSet'
            }
            IncludeExpired = @{
                Enabled = $true
                IncludeNames = 'UserPrincipalName', 'DisplayName', 'DateExpiry', 'PasswordExpired', 'SamAccountName', 'Manager', 'ManagerEmail', 'PasswordLastSet'
            }
        }
    }

    DisplayConsole = @{
        ShowTime = $true
        LogFile = ""
        TimeFormat = "yyyy-MM-dd HH:mm:ss"
    }
    Debug = @{
        DisplayTemplateHTML = $false
    }

}

Start-PasswordExpiryCheck $EmailParameters $FormattingParameters $ConfigurationParameters