Password Change Reminder PowerShell Script Updated!

powershell2xa4Back in 2012 i wrote a script to help me remind users about their password expiry, to reduce the number of calls i got on the helpdesk. I decided to share it and published it on the TechNet Gallery,

It has been quite popular since then, with over 8,000 downloads!

Throughout that time i have received a number of questions about how to tweak the script to do various things or fix bugs and errors that people have found.

I have been tweaking it as i go, without much notification to anyone. I have just finished quite a big change, so i thought i would put up a post here, to let people know.

So what’s new?

Comments. Well firstly i added a lot more comments to the script itself. Not very exciting, but for those wanting to tweak it themselves, kind of important.


Logging. I have added logging so any notifications generated can also be outputted into a CSV file.


Testing. A lot of people wanted to be able to test it, but not have users emailed directly, so now you can have all notifications emailed to a separate address. Which you can see in the above image, all the emails going to .


As always i welcome any comments or suggestions for the script over at the TechNet Gallery.

About Robert Pearman
Robert Pearman is a UK based Small Business Server enthusiast. He has been working within the SMB IT Industry for what feels like forever. Robert likes Piña colada and taking walks in the rain, on occasion he also enjoys writing about Small Business Technology like Windows Server Essentials or more recently writing PowerShell Scripts. If you're in trouble, and you can find him, maybe you can ask him a question.

81 Responses to Password Change Reminder PowerShell Script Updated!

  1. Lee Lucas says:

    Hi Robert,

    Great script thank you for your time and effort, and for sharing this with us.


  2. Arnab says:

    Nice list of Test Users you got there in your CSV.
    Awesome script!

  3. Vadim says:

    Small grammatical mistake that you may want to fix, should be choose not chose:

    To change your password on a PC press CTRL ALT Delete and chose Change Password

  4. This is awesome! Thanks much for sharing.

  5. Shaun says:

    Hi this looks like it could be what we’re after, does it need to run on the exchange server or can it be run on a DC?


  6. Shaun says:

    Great thanks for this, looks like it’s doing the job nicely. Cheers

  7. JKM says:

    Robert – I have tried a few different ways to get the UPN for the username instead of the SamAccountName in both the notice to the IT Admin and the user email, I modified the original Get-QADUser to include -UserPrincipleName but all results came back blank. I also changed the variable $user.LogonName to $user.UserPrincipleName and the results returned 0.

    Most of the users on this particular site use the UPN for login, and this is what I need to display to them in the notifications and the admin report. Any ideas?

  8. I just want to say that this script is awesome! It works really well and helps our support staff tremendously! Big thanks to the creator!


    The Netherlands

  9. melanie says:

    Hi there,

    I love this script! having an issue where it sends to users *even though they have already changed their password*. Everything else works great, it sends 14 days before expiration and I can BCC myself. A user will change their password but they will still get the notification the next day.

    It might be helpful to know that we have two fine grained password policies. One is 90 days for most users, and the rest of the users are on 180. Our default domain policy is *not* used at all, in fact it’s disabled.

    • That seems odd, you would expect it to update AD straight away with a password set date.

      Will investigate, perhaps you can let me know what your infrastructure is like, DC OS etc.

  10. Peter Johnston says:

    Hi there

    Any ideas on how to get this to work on SBS 2008?

    It throws an error at Import-Module ActiveDirectory

    Import-Module : The specified module ‘ActiveDirectory’ was not loaded because no valid module file was found in any module directory.

    Not sure if it is possible to get the ActiveDirectory module installed on SBS 2008?



  11. mike labara says:

    Robert i cannot get it to work. I run the Powershell command on a windows 2008 r2 AD and nothing gets emailed or anything ?? any clues or ideas i should be looking for?

  12. Shaun says:

    Hi Robert,
    This isworking really well for us at the moment, we have included a hyperlink to a document on our Extranet in the body of the text, however the link is only clickable from normal Outlook and not OWA? is this a restriction of OWA do you think or is ther a way in powershell to make such links work? the link is https://www.ourdomainname/filelocation .
    Thanks for any advice on this

  13. Baxter says:

    Hi Robert,

    Edited the configurable items to fit our environment, ran script, .csv was created but had no users data in it. Went to run Line 40 on its own, and realized that it is only pulling data from one OU that only has 5 users in it that fit all critera (none of which are within the expiration scope – hence no results in csv). Nowhere in the script did I filter the get-aduser to only pull from one OU.


    • Baxter says:

      When I run “$users = get-aduser -filter * -properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress” I am able to pull all users from all OUs, however, that includes disabled users and service accounts that have passwords that never expired, which is why I liked your refined search.

      But as soon as I refine some of those properties, it goes back to pulling from a single OU.

  14. Steve Rizkalla says:

    Hi, again thank you for the script. It’s been running great for about 2 months and all of a sudden the other day, the script is empty. I can’t imagine there is nobody in our directory that is scheduled to change their password. If that is true, what could make the report come out empty? Only the headings are in the log file. No data. thanks!

    • The best way to troubleshoot this is to run the $users query manually in PowerShell and find out, does it select any users? If so, run the rest of the script, minus the email part and see if you get any errors.

      • Steve Rizkalla says:

        When running the $users= query, it returns all users and all information about the user as requested in the command. When commenting the email part, the log file still remains empty. I even tried to run an older version of the script from before we went live with it and that one generate an empty log file. Seems like something may have changed on our side… What else do you think can cause this?

      • Someone has changed the password policy. Try the below, I have not tested this but it should dump all users from $users out to the file you specify ($logfile) and show you the date their password expires.

        $date = Get-Date -format ddMMyyyy
        $logFile = “” # ie. c:\mylog.csv
        $users = get-aduser -filter * -properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress |where {$_.Enabled -eq “True”} | where { $_.PasswordNeverExpires -eq $false } | where { $_.passwordexpired -eq $false }
        $maxPasswordAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge
        foreach ($user in $users)
        $Name = (Get-ADUser $user | foreach { $_.Name})
        $emailaddress = $user.emailaddress
        $passwordSetDate = (get-aduser $user -properties * | foreach { $_.PasswordLastSet })
        $PasswordPol = (Get-AduserResultantPasswordPolicy $user)
        # Check for Fine Grained Password
        if (($PasswordPol) -ne $null)
        $maxPasswordAge = ($PasswordPol).MaxPasswordAge
        $expireson = $passwordsetdate + $maxPasswordAge
        $today = (get-date)
        $daystoexpire = (New-TimeSpan -Start $today -End $Expireson).Days
        Add-Content $logfile “$date,$Name,$emailaddress,$daystoExpire,$expireson”


  15. Steve Rizkalla says:

    Thank you for this!!! The reports shows that there isn’t any password ready to expire. So your assumption is correct. The Password Policy was changed from 45 to 90 days. So I can assume the reports are just blank because there aren’t any passwords ready to expire?

  16. Nandakishor says:

    Hi Robert,

    I am kishor , basically we are using server 2012 Data center environment. and we are trying to do password expiry notification on mail but how ever we are not able to do this in 2K12.

    if do you any solution please revert.


  17. Marek says:

    Hi Robert

    We trying to use your script, everything works well, except emails goes directly to junk email to all the users who’s password is about to expired.
    We using simple, non-exchange smtp server to send those emails, all our users on O365.

    Do you know how we can fix that?


  18. Clandis Smith says:

    How can I change the script so that it pulls users from a group instead of all of active directory?

    • Line 40
      $users = Get-ADGroupMember GroupName

      • Jerry says:

        Hi Robert,
        I tried to edit line 40 to point to a group but when I run it I get the error below. I appreciate any help on this as I am new to Powershell scripting and this script of yours is what i am looking for except I am trying to figure out how to tweak it to search through specific AD groups rather than all users. Thank you

        New-TimeSpan : Cannot bind parameter ‘End’. Cannot convert the “90.00:00:00” va
        lue of type “System.TimeSpan” to type “System.DateTime”.
        At C:\admin\Password_Change_Notification-IT2.ps1:59 char:53
        + $daystoexpire = (New-TimeSpan -Start $today -End <<<< $Expireson).Days
        + CategoryInfo : InvalidArgument: (:) [New-TimeSpan], ParameterBi
        + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerSh

      • what did you put on line 40?

      • Jerry says:

        I replaced line 40 with only “$users = Get-ADGroupMember MyGroupName” and kept everything else the same and that is when I got this error.

  19. Nate says:

    I am struggling with getting this script scheduled. I can run the script via the AD powershell, but calling the module seems to fail.
    When I run this command from a DOS window I get the following error:
    l.exe -nologo -noninteractive -command import-module ActiveDirectory -File “c:\
    Import-Module : A parameter cannot be found that matches parameter name ‘File’.
    At line:1 char:36
    + import-module ActiveDirectory -File <<<< c:\jobfiles\PasswordChangeNotificat
    + CategoryInfo : InvalidArgument: (:) [Import-Module], ParameterB
    + FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.Comm

    • Nate says:

      After much messing with the command line I finally go it to work as a task. I am running Powershell version 4… I could not get -file to work. The working command line is:


      Then I set the following arguments
      -nologo -noninteractive -command “import-module ActiveDirectory;& C:\Jobfiles\PasswordChangeNotification.ps1”

      • You should not try and run PowerShell Scripts from a CMD window, it is much easier to launch PowerShell directly.

        As for the scheduled task, the way I do it is to set the program to launch as ‘PowerShell.exe’ with arguments “-command \Script.ps1”

  20. john Trask says:

    Hello, How can I get this to only search a specific OU?

  21. johntrask says:

    Hello, How can I get the script to only search a specific OU?

  22. Tom says:

    How could this script be modified to put a daily popup to change email??
    That is, replace the email part with the popup message??
    You don’t have to provide code if you don’t want to, please do suggest what code to comment out so it’s not used and the popup message appears instead.
    I want a popup so people can be reminded daily until they do change their pass.
    Thank you, Tom

  23. Andrew B. says:


    I thought this might be the answer to a requirement that we have but then I saw it wasn’t tested on Server 2012.

    Having given it a go it anyway, it looks like the cmdlets have changed now since “The term ‘get-aduser’ is not recognized as the name of a cmdlet, function, script file, or operable program” and I can’t see that in the list of available commands in the right hand pane within Powershell ISE. In fact, it looks like a few other “get-” cmdlets are invalid as well.

    Do you have any plans to update this to run on Server 2012? – it’s the future you know!


    • As far as I know it should work perfectly well in 2012 or 2012 R2.

      I would check to make sure your Active Directory PowerShell Module is installed and loaded, if memory serves it should be by default on 2012 if it is a DC but it may not be. You can run Get-Module to see what modules are available, or run Get-WindowsFeature RSAT-AD-PowerShell

      • Rob says:

        This script would be great for us. We have multiple users via VPN and based on the config don’t sync to get the alert that the password will expire. My issue is I am not a powershell guy. If I wanted to customize the message and only send this to certain users only in our employees OU, so Service accounts aren’t clicked how would I configure this? Maybe any reference page to customization for this? I saw the test part so running it as a test before hand would be great. Any help would be fantastic and if accepting donations for your work please direct me to a location…..

  24. Mike D. says:

    Nice work!

  25. Reginald says:

    Is there a way to send the password expiration to users using office365?

  26. vini says:

    Can you please send me a script which will work for gmail server as the smtp server.
    i modified the script to do smtpserver : but no email was sent

    output was generated saying email was sent.
    I using windows 2012 r2 server.
    But did not receive any email.

    An ideas as to how to setup gmail server for your script so i can test it also.

  27. Champ says:

    Hi Robert,

    this is really a great script, and it seems it still alive and kinking til 2016..haha just want to ask regarding the test email, is it only for testing purposes? Can I just put in my e-mail? or should I need to create a logfile on where it listed all the staffs name and email address on where system can use as database?

  28. Dave Rimmer says:

    Hi Robert, great script, thank you.

    I am having some weird problems with it. We use GMail for business, and I successfully managed to get the script to send using a specific email account with a dedicated app password. Embed this into the script and amend the send-MailMessage command accordingly and it partially worked.

    The script required to send email using GMail:

    $username = “”
    $pwd = “dedicated app password”
    $securepwd = ConvertTo-SecureString $pwd -AsPlainText -Force
    $credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $securepwd
    $From = “”
    $To = “”
    $Subject = “Test Notification”
    $Body = “Testing script to send emails”
    $SMTPServer = “”
    $SMTPPort = “587”
    Send-MailMessage -From $From -to $To -Subject $Subject -Body $Body -SmtpServer $SMTPServer -port $SMTPPort -UseSsl -Credential $credential

    I incorporated the new $ variables into your script:

    # Please Configure the following variables….
    $username = “”
    $pwd = “dedicated app password”
    $securepwd = ConvertTo-SecureString $pwd -AsPlainText -Force
    $credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $securepwd
    $SMTPPort = “587”
    $expireindays = 14, 7, 3, 1
    $from = “”
    $logging = “enabled” # Set to Disabled to Disable Logging
    $logFile = “C:\temp\notification_logs.csv” # ie. c:\mylog.csv
    $testing = “disabled” # Set to Disabled to Email Users
    $testRecipient = “”

    and amended the Send-EmailMessage line to:

    # Send Email Message
    Send-MailMessage -From $from -To $emailaddress -Subject $subject -Body $message -dno OnSuccess, OnFailure -SmtpServer $smtpServer -port $SMTPPort -UseSSL -Credential $credential
    Start-Sleep -Seconds 10 #Adding a 30sec pause between emails to prevent SPAM block
    } # End Send Message

    I also had issues with the script acquiring the email address from AD, the way I found to do that was:

    $emailaddress = (Get-ADUser $user -properties mail).mail

    To use this, I amended your script to bring the mail property as well

    $users = get-aduser -SearchBase “OU=MyOU,OU=MyOtherOU,DC=MyDomain,DC=COM” -filter * -properties Name, Mail, PasswordNeverExpires, PasswordExpired, PasswordLastSet, UserPrincipalName |where {$_.Enabled -eq “True”} | where { $_.PasswordNeverExpires -eq $false } | where { $_.passwordexpired -eq $false }

    In the log file, I see some weird behaviour. It brings back each user 4 times.

    Also, I only ever see a total of 3 emails sent. Never more, never less.

    Any advice you can offer would be much appreciated, I am not sure what I have done to cause this.

  29. Obi Ejiofor says:

    Hi nice script.

    i need to filter is on a security group.

    in short only members in a specific security group need to recieve the mail.

    how would i do that ?

  30. Sandman says:

    Can it be configured to run automatically on a scheduled task so we won’t have to be running it manually every time?

  31. Brad Wilkins says:

    Thank you for the script it makes my job a little easier. However, I have come across where a user gets an email notification and they change their password that day and the next few days they are still getting email notifications after they have changed their password. I have confirmed/verified that they changed their password in AD and also checking the log file shows they have received the email each day. Any ideas on what could be causing this?

  32. Larry Grant says:

    I am having an issue with the logging. When writing this to a csv it is separating the first and last name into separate columns as my display name has a comma…

    • I’m not sure what you can do to work around that, in a CSV file.

      You could change the value that is logged, to remove the comma from the display name. I don’t know if that would suit your requirements though.

  33. Matt says:

    Could you help with an error I’m having with the script ?

    It appears that the script can’t find my Fine grained password policy, error message displays…

    Get-ADUserResultantPasswordPolicy : Cannot find an object with identity: ‘CN=FGPP-1 …etc

    Any ideas why this might be ?


  34. MOGE says:

    Hi, is it possible to change the word days in Email Subject and body to Tage? nice work!!

  35. Harpreet Singh says:

    I Rob, how can I exclude one specific OU to not get the password reminder ?


  36. Shandy says:

    Great script, works amazingly, thanks.
    Reduced so many support calls, thanks to this.

Leave a Reply to Shaun Cancel reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: