Home > Exchange Server, PowerShell > Script: New-ADPasswordReminder.ps1 – Email Users When Their Password Will Soon Expire

Script: New-ADPasswordReminder.ps1 – Email Users When Their Password Will Soon Expire

Note: Development on this script has moved to Github. Please see the repo at https://github.com/patrichard/New-AdPasswordReminder.

Description

In today’s increasingly mobile workforce, many users don’t login to Active Directory on a domain joined computer. Some use only Outlook Web Access/App, some use non-domain joined machines external to the company network, some just use mobile devices such as smartphones and tablets. And others may use Macs.

Users who login via domain joined machines on the company network get the reminder several days ahead. The default is 14 days, but can be configured in the Default Domain Group Policy under Interactive logon: Prompt user to change password before expiration.

OWA users see a notification when they login as well. In OWA 2007 running on IIS6, this can be adjusted via PasswordExpirePrenotifyDays. In fact, with OWA 2007 and 2010, you can even change your password after it expires, using the Password Reset Feature in Exchange 2007 and 2010. However, there are times when that’s just not a remedy. The password reset feature requires the Exchange server to be running on Windows 2008 or later, as it relies on IIS 7. Many Exchange 2007 shops are not on that platform yet.

Anyone who’s ever worked on a Help Desk knows that a LOT of users call to say they can’t login, only to determine it’s because their password expired. Many, if not most, are those types of users mentioned above. Others don’t notice, or simply ignore the notice when logging in. So let’s really make sure we notify them of the pending expiration. There are some third-party tools, including some that run on SharePoint, that enable a user to reset their password. But this is after the fact. Sure, we could use some third-party application to send a reminder, but… well… why? PowerShell to the rescue!

In the pre-Windows 2008 domain functional level days, we could just peek at the Default Domain GPO, and grab the Maximum Password Age parameter, since it was a global setting. Then we could go through Active Directory, find users who are not set to “never expire”, use some math, and come up with a list of users whose password expired soon.

But with the changes implemented with Windows Server 2008, we can now have Fine Grained Password policies, which allows us to have more than just one password policy in our organization. So, Executives get one password, IT people with elevated rights get another, etc. Cool in theory, but frustrating in our endeavor to notify users when they’ll expire.

I blatantly admit that I used part of a script by M. Ali, who wrote a blog post Find out when your Password Expires. The script looks checks Get-AdDomain, and looks at the DomainMode parameter in the results. From here, we know whether we can just peek at the Default Domain policy, or if we need to look deeper. Regardless of which way, we look through the users using Get-AdUser, and grab the PasswordExpired, PasswordNeverExpires, and PasswordLastSet fields. Obviously, if the account is expired, no need to keep reminding the user. And if the password never expires, then we also don’t need to notify the user. With PasswordLastSet, our math comes into play to determine when the password will expire. Not terribly short and sweet, but effective.

Once we know when the password will expire, we can then set a window for when we should notify the users. It makes sense to match what’s in the GPO so that notifications are consistent regardless of platform. This script is set to 14 days by default.

Next, we need to craft some information that we want to convey to the user. In this case, we’ll use some HTML formatting so that we can properly convey the importance of the info, as well as include some additional formatting. I’ve mocked up something based on some third-party tools, and on the comments and recommendations of IT Professionals and users. It’s simple enough to change, but be warned that many clients, including Outlook, don’t strictly adhere to HTML standards. So it can take quite a bit of trial and error to find out what does actually appear the way you want it to.

Installation and Setup

First, you need a receive connector that will accept mail from PowerShell. I cover that in Creating a receive connector to use for sending email from PowerShell. Next, the script must run on a machine with PowerShell 2.0 installed. This is a prerequisite for Exchange 2010 (and installed by default on Windows 2008 R2), but not for Exchange 2007. If you’re reluctant to upgrade PowerShell on your 2007 box, it can be run from any other box that has PowerShell 2.0 and the Exchange Management tools installed. Note: Exchange Management tools should always be updated and patched to the same level that your Exchange servers are.

Second, you’ll need the ActiveDirectory module available on the machine that will run the script. The ActiveDirectory module is installed when you add the Remote-Server Administration Tools feature in Windows Server 2008 R2. If the module is not detected, the script will attempt to install it automatically the first time it runs.

Next, grab the latest zip file from the DOWNLOAD section below. It includes the script and ScriptImages.zip contains a couple of images that are used in the warning for users who’s password expires in < 24 hours (seen in the Outlook screenshot above). The images need to be accessible to all users who will receive the reminder emails. This is likely to be a public web site.

Crack open the script in your favorite editor and update the lines in the param() block to match your environment. This includes $Company, $OwaUrl, $PSEmailServer, $EmailFrom, $HelpDeskPhone, $HelpDeskURL and $DaysToWarn. If you want to target a specific OU, set $OU. Also, set $ImagePath to a path holding the included image files (or those you add/edit). This path should be available to all users who may receive the reminder message. This is probably a public server.

param(
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[switch]$Demo,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[switch]$Preview,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[switch]$Install,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[string]$PreviewUser,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[switch]$Transcript,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Please specify a company name.")]
	[ValidateNotNullOrEmpty()]
	[string]$Company = "Contoso Ltd",
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Please specify an OWA URL")]
	[ValidateNotNullOrEmpty()][ValidatePattern("^https://")]
	[string]$OwaUrl = "https://mail.contoso.com",
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Please specify the IP address of your email server")]
	[ValidateNotNullOrEmpty()][ValidatePattern("\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b")]
	[string]$PSEmailServer = "10.9.0.11",
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Please specify a name and email address for the email 'from' field")]
	[ValidateNotNullOrEmpty()][ValidatePattern("\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b")]
	[string]$EmailFrom = "Help Desk ",
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[string]$HelpDeskPhone = "(586) 555-1010",
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[ValidatePattern("^http")]
	[string]$HelpDeskURL = "https://intranet.contoso.com/",
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[string] $TranscriptFilename = $MyInvocation.MyCommand.Name + " " + (hostname)+ " {0:yyyy-MM-dd hh-mmtt}.log" -f (Get-Date),
	[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$false, HelpMessage="This must be zero")]
	[ValidateNotNullOrEmpty()]
	[int]$global:UsersNotified = 0,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Please specify how many days before expiration that users should begin to be notified.")]
	[ValidateNotNullOrEmpty()]
	[int]$DaysToWarn = 14,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[string] $ImagePath = "http://www.contoso.com/images/new-passwordreminder.ps1",
	[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$false)]
	[ValidateNotNullOrEmpty()]
	[string] $ScriptName = $MyInvocation.MyCommand.Name,
	[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$false)]
	[ValidateNotNullOrEmpty()]
	[string] $ScriptPathAndName = $MyInvocation.MyCommand.Definition,
	[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$false, HelpMessage="Please specify an Organizational Unit")]
	[ValidateNotNullOrEmpty()]
	[string] $ou
)

Open an Exchange Management Shell session and run the script in demo mode to see a list of users that are expiring soon.The script won’t email the users in demo mode. It merely shows you who it WOULD, and how long till their password expires.

.\New-PasswordReminder.ps1 -demo

As we see in the example screenshot, Claudia’s password expires in 5 days, and the password policy that applies to her requires the password to be changed every 42 days. If we run the script normally, Claudia will receive the email reminder since it’s within the 14 day window defined in the script.

To run the script normally (non-demo mode), manually, just omit the -demo. There is no output to the screen when run normally, as the script is designed to be run as a scheduled task.

Once you’re satisfied that the script is running correctly, we can set it to run as a scheduled task. I have a blog post Running PowerShell scripts via Scheduled Tasks that details everything. In my production environment, it runs at 6am each day.

One of the hardest parts was getting a decently formatted email that looked good. This could take some trial and error, and the original script didn’t really have a way built in to preview what the end user would see. As a result, some hapless users would be flooded with your “test” messages. I fixed that by creating a preview mode. Manually run the script with the preview switch, and a user to send the email to. For example

.\New-PasswordReminder.ps1 -Preview -PreviewUser bgates

This will send an email to the user, bgates. The email is formatted for a password that expires in one day, so the user gets the additional banner near the top as well.

Next up was creating a scheduled task. Not really terribly difficult to do manually, but I could see where it might take some trial and error. So, I added the install switch, which will create a scheduled task for the script, configuring it to run at 6am each day. Of course, that time can be manually adjusted by opening the scheduled task once it’s created. The install mode will ask for credentials to run the scheduled task under. Install it as so:

.\New-PasswordReminder.ps1 -Install

Note: The scheduled task is configured to point to where the script is when you run the install switch. So don’t move it later!

To send an email that does not contain the images or their related formatting, specify $NoImages when running the script. This will send essentially an HTML formatted text email.

Next up, I added some simple logging to the application event log. The script will write a single entry when it starts, and a single entry when it finishes, noting how many users were processed (sent an email). I would love to hear how this script works in large environments. If you’re willing, please let me know (via comments below) how long it’s taking to run in your environment, and the number of users in AD.

Please send me your suggestions!

Donations

I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at https://www.paypal.me/PatRichard. Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.

Syntax

New-PasswordReminder.ps1 [-Demo] [-Install] [[-PreviewUser] ] [-NoImages] [-WhatIf] [-Confirm] []

Demo Runs the script in demo mode. Demo mode displays users who are expiring soon, but does not send them the reminder email.

Install Creates a scheduled task to run the script automatically every day at 6:00am

PreviewUser
Defines the user to send the preview email to.

NoImages
Specifies that a HTML text only message should be sent instead of one that contains the fancy formatting.

Installation

Execution Policy: Third-party PowerShell scripts may require that the PowerShell Execution Policy be set to either AllSigned, RemoteSigned, or Unrestricted. The default is Restricted, which prevents scripts – even code signed scripts – from running. For more information about setting your Execution Policy, see Using the Set-ExecutionPolicy Cmdlet.

In addition to the info listed above:

If you leave the following parameters blank, the related text will be removed from the email sent to users: $HelpDeskURL. This will get expanded in the future.

You can change the format of the date displayed in the email by changing the value of $DateFormat. The default is “d”, which yields a date such as 09/07/2012 (MM/dd/yyyy). If you’d like the European style, use “MM/dd/yyyy” instead.

Frequently Asked Questions

Question: Does this work with Exchange Server 2013

Answer: Yes

Download

v2.9 – 09-13-2013 New-ADPasswordReminder.v2.9.zip

v2.8 – 05-03-2013 New-ADPasswordReminder.v2.8.zip

v2.7 New-PasswordReminder.v2.7.zip

v2.6 New-PasswordReminder.v2.6.zip

v2.4 New-PasswordReminder.v2.4.zip

New-PasswordReminder.zip

ScriptImages.zip – image files used in emails

Changelog

See the changelog for this script which details all versions and their features.

  1. Julian
    September 29th, 2011 at 05:07 | #1

    Hi, i want to use this script for only one OU, and OU’s which are located under it. How can i do that?

    • Pat Richard
      September 29th, 2011 at 08:58 | #2

      That is not in the current version, but it is something I’m working on for the next version.

  2. Julian
    September 29th, 2011 at 09:22 | #3

    Hi Pat,
    ok, thanks for the information! Hm, but there is no option, where i can change the script easily?

    maybe there would be an easy way to change the get-aduser properties?
    Only a question…your script is still perfect! 🙂

    • Brad
      September 3rd, 2013 at 15:17 | #4

      Did you get the option working to have this work in a specific OU? We’re dealing with the same issue here.

      • Pat Richard
        September 12th, 2013 at 23:01 | #5

        That’s in v2.8, the last version released. Define the OU in the script. It’s around line 112.

  3. Pat Richard
    September 29th, 2011 at 13:22 | #6

    Julian – I have it working for a specific OU. Let me finish a couple more things I need for this version. I should be able to push it out tomorrow.

    • Julian
      September 30th, 2011 at 02:10 | #7

      Hi Pat,
      sure take your time and Thank You very much for your help!!!

      I have some more idea: It would be very fine, if you can choose how often a mail will send out to the user. For example: the first information will come 21 days before the password will expired, the second mail will come 12 days before the password will expire. And the last 7 days the mail will be send out daily.

      Or could you help me, how i can do that?

      • Brad
        September 18th, 2013 at 14:18 | #8

        The script works great! But the comment above would be a great feature to add to the wish list.

  4. Pat Richard
    September 30th, 2011 at 15:47 | #9

    I can add that to the wish list. I always welcome ideas!

    The new version was rolled out this morning. Check it out at https://www.ucunleashed.com/569

  5. Julian
    October 1st, 2011 at 03:16 | #10

    Hi Pat,

    thx a lot for the latest version! Your script is really cool, will try it on monday! Of course, please add it on the wish list, would be really cool to have this feature, too! 🙂

    Thx and have a nice weekend,

    Julian.

  6. Kishore Masagani
    October 12th, 2011 at 02:46 | #11

    Hi Pat,

    Thanks for your script. It has helped me to setup notification for my customer.

  7. October 26th, 2011 at 06:48 | #12

    When I executed the option -install, the scheduele job is created, but if I launch the job it doesn´t get executed.

    I think, that the schedule job don´t include the active directory powershell snappin.

    How can I include in the scheduele job all the powershell snappins of my server.

    • Pat Richard
      October 26th, 2011 at 16:38 | #13

      The script calls the module for AD. It doesn’t use snapins. If that feature (RSAT-AD-PowerShell) isn’t installed, it should install it (assuming it’s running with an account that has rights to do that).

      If you run it manually, does it work?

  8. October 28th, 2011 at 08:10 | #14

    It works if I run it manually, but don´t work with the scheduele job.

    My server where I execute te script is a Domain Controller with Windows 2008 R2 Standard.

    My domain funcional level is Windows 2003, but I´m going to upgrade the domain funcional level to Windows 2008 R2, the next week.

    Regards

  9. Matthew
    November 2nd, 2011 at 09:04 | #15

    Yes, works only like Carlos said. I have tried a lot of things with that, but i run out of ideas.

    The code after scheduled task: (0x1)
    Manually it works..

    • Pat Richard
      November 2nd, 2011 at 09:10 | #16

      Okay, and you’re running it on a Windows server? 2008 R2? And you’re using the script from the updated page at http://www.ehloworld.com/569?

    • Obi Ejiofor
      January 16th, 2013 at 04:59 | #17

      Dont use the install option from the script manually create the task with powershell to run the script. and use -ExecutionPolicy Bypass -File to run the script file

      • Pat Richard
        January 16th, 2013 at 10:30 | #18

        There really is no need to use that method. The install option works fine in every environment I’ve installed it in.

  10. Matthew
    November 2nd, 2011 at 09:32 | #19

    Yes,

    2008 R2 with Exchange 2010, newest script v2.2. I’ve used your tutorial “Running PowerShell scripts via Scheduled Tasks” (even with command -install).

    Thanks,

  11. UIf
    January 16th, 2012 at 10:52 | #20

    I tried the 2.4 version also. But still only users with no Policy assigned to (i.e. domain admins) shows up data for days to expire. No other users with password policies can be found. Tried to set DaysToWarn to 80 or 90 (policy says 90 days).

    • UIf
      February 22nd, 2012 at 03:54 | #21

      Problem solved. You can’t run the script on a domain controller. Use another server and it works like a charm.

      Only issue found is that the install makes a bad command line argument. Have to be modified manually to work.

      • Rob
        May 2nd, 2012 at 14:49 | #22

        UIF – Do you remember what needed to be modified manually?

        • Rob
          May 2nd, 2012 at 14:52 | #23

          Nevermind I saw below someone already figured it out. Quotes….awesome script and thank you very much.

  12. Ozgur Canibeyaz
    January 17th, 2012 at 04:03 | #24

    Best of its kind script. But kinda confused which server i should run it on. I am running an exchange server 2007 sp2 on windows server 2008. Domain controllers are windows server 2008 r2. Domain functional level is 2003. Can i just run it on a DC where exchange management tools is not installed ?

  13. Geoff Orr
    February 11th, 2012 at 20:01 | #25

    I am trying to use this with users who have a PSO vs GPO applied. When I run the demo it gets 0 results, which is incorrect. Could I have help troubleshooting?

    • Geoff Orr
      February 11th, 2012 at 23:25 | #26

      Never mind,

      I changed ge =3 and I’m good to go. Great post!

  14. February 13th, 2012 at 07:55 | #27

    Hi Pat

    I tried your script and it works perfectly. thanks for the great effort

    I need you help to achieve the following:
    I need the script to send the email to the users 1 month before his password expires and again send the email to the users 15 days before his password expires, then send it if the password will expire in 9 days.

    • Obi Ejiofor
      January 16th, 2013 at 10:31 | #28

      The script in its basic form will keep sending the email every day if the days before expire threshold is reached.

      In short if the thresh hold is set for 30 day if the script runs daily the users will get the expire mail 15 days before their password expires and 9 days before their password expires.

      • Pat Richard
        January 16th, 2013 at 11:17 | #29

        Interesting request. Let me see what I can do.

  15. lvjoe
    February 17th, 2012 at 12:48 | #30

    Great Script! Been hunting for a while and this is the best one by far.

    I too have the scheduled task issue and wonder if it’s UAC related? Will try commenting out the eventvwr write commands.

    Also, you can set the $PSEmailServer var but I do see where it’s applied. I added the -smtpServer $PSEmailServer to the send-mailmessage command, and can now direct emails through any smtp server I define.

    • lvjoe
      February 21st, 2012 at 12:34 | #31

      Using the “-Install” option, minor issue.

      My scheduled task inserted by the tool was not working. Eventually I determined the issue is related to the command line arguments not being enclosed in double quotes which is need when the path contains spaces. I manually added the double quotes below to the scheduled task.

      -psconsolefile “C:\Program Files\Microsoft\Exchange Server\V14\\Bin\exshell.psc1” -command “C:\My Scripts\New-PasswordReminder.ps1”

  16. February 19th, 2012 at 08:12 | #32

    Hello Pat

    please help us we are struggling…

    I need your help to achieve the following:
    I need the script to send the email to the users 1 month before his password expires and again send the email to the users 15 days before his password expires, then send it on daily basis if the password will expire in 9 days.

  17. March 6th, 2012 at 02:29 | #33

    Hello
    I managed to let the script to send the email to the users 1 month before his password expires and again send the email to the users 15 days before his password expires, then send it on daily basis if the password will expire in 9 days.

    i hope it will work well in the production environment…

    • John
      January 6th, 2014 at 18:41 | #34

      Fahad, could you share how you were able to modify the script to this? Trying to do something similar, just different time periods.

  18. Phil
    March 8th, 2012 at 11:56 | #35

    Hi, great looking script. I have amended the script and run from exchange 2010 server using EMS. When I execute command:

    .\New-PasswordReminder.ps1 -Preview -PreviewUser My_User

    The command runs OK but no test email is received?

    Any thoughts on what I might be doing wrong!
    Thanks,
    Phil.

    • Pat Richard
      March 8th, 2012 at 12:39 | #36

      Most common cause of this issue is junkmail or quarantine.

      • Phil
        March 9th, 2012 at 03:03 | #37

        Thanks for the reponse Pat. Nothing in my outlook client, i shall investigate further.

        Can you amend my name in my original post to Phil please?
        Thanks,
        Phil

      • Phil
        March 9th, 2012 at 03:28 | #38

        I have looked at the logs after running and see Evnet Id 70 information message

        Finished processing 0 account(s).

        For more information about this script, run Get-Help .\New-PasswordReminder.ps1. See the blog post at http://www.ehloworld.com/318.

        I ran command:
        .\New-PasswordReminder.ps1 -Preview -PreviewUser myFirstname_MyLastname
        Thanks,
        Phil.

      • Phil
        March 9th, 2012 at 03:36 | #39

        Pat further testing has shown the script does work, I appear to have misinterpreted the ‘Preview’ function. I ran against my AD account where the password is not due to expire. I ran against an alternate account where the password was due to expire and they received the email. Not ideal as I wanted to preview the email prior to going live with the scripts.

        Great work.
        Phil.

      • Phil
        March 9th, 2012 at 04:09 | #40

        Pat I now have the script working but I note an error. The demo correctly displayed password expiry for user accounts. Using one as an example password expiry was 9 days (confirmed by DumpSec). I sent a preview email to this account. The email password reminder advised the user that their password expired in 1 day 10/03/2012 (this should have been 9 days 18/03/2012).

        Any thoughts?
        Thanks,
        Phil.

        • Pat Richard
          March 9th, 2012 at 07:52 | #41

          That is by design, so that you can see all of the formatting. The message is a little different when it’s only one day, and I wanted the admin to be able to see that.

          • Phil
            March 10th, 2012 at 02:30 | #42

            Hi Pat. So the script only states 1 day in ‘Preview’ mode? Does the message differ much when > 1 day?

            Once again great work and I commend you for responding to user comments.
            Many Thanks,
            Phil.

          • Pat Richard
            March 14th, 2012 at 15:58 | #43

            No, the message doesn’t really change, but the red image above the text goes away when it’s >1 day.

  19. Fahad
    March 8th, 2012 at 14:33 | #44

    Hello Pat

    just suggestion to improve this script. if you can add a log file where all users got the email are logged there. this will help us to know the users their password will expire.

  20. March 16th, 2012 at 08:44 | #45

    Hi Pat
    Thanks for this, one of the best PS script aroung… thumbs up !!!

    Simular sugestion as Fahad has… either a log or a CC post to Admin

    Jon

  21. Chris
    March 26th, 2012 at 15:51 | #46

    Pat,

    First let me just say thank you, this is exactly what I’ve been looking to do to deal with users who REFUSE to log off their PCs more than twice a year.

    I’ve been able to get everything working just great except the scheduled task doesn’t seem to work properly giving me a results code of 0xFFFC0000. Running the script manually from the command line is flawless tho. Any idea what the issue might be?

    Thanks in advance

    Chris

    • Pat Richard
      March 26th, 2012 at 15:52 | #47

      If you take the entire line from the scheduled task, and pop it into a cmd prompt (not PowerShell), and it runs, then the likely issue is credentials/authentication.

      • Chris
        March 30th, 2012 at 08:34 | #48

        Thanks! Looks like i was having the same problem as lvjoe (should have read the comments better)

        Just needed the double quotes around

        “C:\Program Files\Microsoft\Exchange Server\V14\\Bin\exshell.psc1”

        and things are working beautifully.

  22. Tyo
    April 9th, 2012 at 22:49 | #49

    hi Pat,

    thanks for this great script its been working for about 2 month but lately,
    when i try to running from powershell it always result with error like this:

    “Send-MailMessage : Cannot validate argument on parameter ‘To’. The argument is null or empty.
    Supply an argument that i s not null or empty and then try the command again.
    At C:\Exchange Password Reminder Script\script-images\NEW-PasswordReminder.ps1:345 char:28
    + Send-MailMessage -To <<<< $accountObj.mail -Subject "Your password expires in $DaysTillE
    xpire day(s)" -Body $emailbody -From $EmailFrom -Priority High -BodyAsHtml
    + CategoryInfo : InvalidData: (:) [Send-MailMessage], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.SendMailMessage"

    would you please tell me what is wrong with the error?

    Tyo

    • Tyler
      June 12th, 2012 at 15:01 | #50

      Same error, not sure what the issue is. Script works in -Demo and -Preview -Previewuser but not simply running the script normally with no switches. Any ideas?

      • Pat Richard
        June 12th, 2012 at 15:06 | #51

        Waiting for Chuck to finish testing a new version that clears up the issue he reported.

  23. Patry
    April 18th, 2012 at 07:23 | #52

    Can this be used on Windows 2008 instead of Windows 2008 R2

    • Pat Richard
      April 18th, 2012 at 07:41 | #53

      I have not tested it on 2008.

  24. frank
    April 26th, 2012 at 09:24 | #54

    First pPat great script everthing work except the image of header do not show up it states they have moved?

    Any ideas how to fix it?

    I have tried c:\image\, \\servername\c$\image, \\serverip\c$\image, and \\127.0.0.1\c$\image

    please help

    • Pat Richard
      April 26th, 2012 at 09:26 | #55

      The images need to be in a location accessible by EVERY user who may receive the email. That includes users outside your environment who may be reading the email via OWA. I always use a subfolder of the organization’s website, but have gotten it to work with folders on a CAS server – but that brings up extra complexity if you have more than one CAS server.

  25. Bruce
    May 10th, 2012 at 11:35 | #56

    This script is working great except the password policy listed at the bottom of the email has the settings for my default domain policy and not the settings from the fine grained password policy. Anyone else run into this?

    • Pat Richard
      May 10th, 2012 at 11:37 | #57

      That’s a known issue with the script that I need to resolve when I have some time.

  26. Markus
    May 14th, 2012 at 07:27 | #58

    Hi Pat,

    I just want to thank you for the script and your efforts.
    I was able to setup V2.4 successfully with the steps described above and the note from Chris (missing /malformed path in the Scheduled Task “C:\Program Files\Microsoft\Exchange Server\V14\\Bin\exshell.psc1″ after using the -install option).

    Markus

    • Pat Richard
      May 14th, 2012 at 08:04 | #59

      Thanks. I’m working on a new install option that will not only resolve this, but also take into account being installed on servers that don’t have Exchange on them.

  27. annie
    May 18th, 2012 at 07:45 | #60

    How can i mention OU name in script, i have modified the OU in below script at the line highlighted in star.but i am getting whole user’s list and not in perticular OU’s users.

    # Get-ADUser -filter * -properties PasswordLastSet,EmailAddress,GivenName –
    **********************************************************
    SearchBase “OU=Users,DC=mydomain,DC=com” |foreach {
    *********************************************************
    if (!($preview)){
    if ($ou){
    $users = Get-AdUser -filter * -SearchScope subtree -SearchBase $ou -ResultSetSize $null
    }else{
    $users = Get-AdUser -filter * -ResultSetSize $null
    }

  28. annie
    May 19th, 2012 at 04:43 | #61

    Could please let me know where i need to change if i want this script to run on specific OU?

  29. Charles
    May 23rd, 2012 at 23:43 | #62

    Hi Pat,

    This script is just what i’m looking for. Thank you.

    One question though. How do i change the format of the date? from mm/dd/yyyy to dd/mm/yyyy

    Charles

    • Pat Richard
      September 7th, 2012 at 10:25 | #63

      Charles – look for this line:
      $DateofExpiration = (Get-Date($DateofExpiration) -f d)
      and change it to
      $DateofExpiration = (Get-Date($DateofExpiration) -f dd/MM/yyyy)

      I’ve added this as a parameter in v2.7

  30. Valtteri
    May 28th, 2012 at 03:30 | #64

    Hello! I need to use scandinavian letters with this script, but they don’t work.

    Letters ÄÖ and äö are all just plain ?? in email.

    Any idea why?

  31. Chris
    June 6th, 2012 at 13:09 | #65

    I’ve been running this script as a test with my existing Domain admin account which has full permission across the enteprise. As I want to set this up to run under a service account what permissions are needed on Exchange/Domain/Local Computer for it to run?

  32. June 12th, 2012 at 07:59 | #66

    Pat,
    I am new to PowerShell scripting but I am trying to learn on my own. I want to thank you for your work on this script, if I can get it to work this will be a huge benefit to my help desk.
    I am trying to run the script in Preview and Demo mode but each time I do it returns an error,
    Missing ‘)’ in function parameter list.
    At C:\scripts\New-PasswordReminder.ps1:174 char:108
    + [parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$true, HelpMessage=” <<<
    < No module name specified!")]
    + CategoryInfo : ParserError: (CloseParenToken:TokenId) [], ParseException
    + FullyQualifiedErrorId : MissingEndParenthesisInFunctionParameterList

    I dont know what module name the script would be looking for?

    Can you or anyone help?

    Thanks,
    Chuck

    • Pat Richard
      June 12th, 2012 at 08:01 | #67

      Just to clarify, you’re not trying to do Preview and Demo at the same time, right?

      • June 12th, 2012 at 09:35 | #68

        This is the command I am running in Powershell
        .\New-PasswordReminder.ps1 -Preview -PreviewUser chood

      • August 7th, 2014 at 04:54 | #69

        Hi PAT, We have tested this script in our environment and it works great, but before we implement this, our team suggest that we should re-edit the PASSWORD POLICY part. Can this be done as i understand the linec 437-442 call this table directly from AD. Or at least isthere is a way to remove it completely? and maybe insert a customized table.?

        • Pat Richard
          August 13th, 2014 at 09:46 | #70

          You could edit as needed, but I can’t provide support for that.

  33. June 12th, 2012 at 09:37 | #71

    I am running this directly on my exchange server 2010 using the Exchange Management Shell

  34. June 16th, 2012 at 15:02 | #72

    Pat,

    The issue came down to the AD Management Gateway Service on the PDC. After that was resolved the script ran fine.
    I did have one other issue with selecting a specific OU, do I need to have the entire path in quotes or do I just use the name of the OU?

    Thanks for your help.

    Chuck

  35. Pat Richard
    June 16th, 2012 at 15:04 | #73

    Glad to hear you got it working.

    The entire OU path needs to be in quotes.

    • June 16th, 2012 at 15:07 | #74

      Thanks again,
      I will give that a try. You seem to be very well versed on the power shell language, is there any particular book you would recommend for a newbie?

      Chuck

  36. martin
    June 18th, 2012 at 06:48 | #75

    hi pat, thx for your outstanding script. for me it works perfect in demomode or when i run it from powershell. but doesn’t work as scheduled task, and when i run from the cli, it runs but on the end it reports the following error:
    **************
    Get-Content : Cannot find path ‘C:\New-PasswordReminder.ps1’ because it does no
    t exist.
    At C:\scripts\chkexpiredpw\New-PasswordReminder.ps1:347 char:23
    + $result = Get-Content <<<< $path |
    + CategoryInfo : ObjectNotFound: (C:\New-PasswordReminder.ps1:Str
    ing) [Get-Content], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetCo
    ntentCommand
    *************
    script is running on a 2008 R2 DC with exchange2007 management tools installed.
    i would appreciate any help!
    regards martin

    • Pat Richard
      June 19th, 2012 at 13:02 | #76

      Look at the bottom of the script for a line that reads
      Remove-ScriptVariables -path $MyInvocation.MyCommand.Name
      put a # in front of it.
      # Remove-ScriptVariables -path $MyInvocation.MyCommand.Name

      Run it again and let me know the results.

      • martin
        June 20th, 2012 at 02:37 | #77

        ok, great. the line on the bottom reads like: “Remove-ScriptVariables($MyInvocation.MyCommand.Name)” i put a # in front, now it works. another tip: do nut create the task as a privilidged user other the one the task is running under. means, if you want to run the task as administrator”, create it also as administrator, othewrwise it wont work.
        thx to pat again, great job done!
        martin

        • Pat Richard
          June 20th, 2012 at 07:23 | #78

          Glad to see it’s working for you. I’ll have to look and see why Remove-ScriptVariables is throwing an error.

  37. June 19th, 2012 at 12:58 | #79

    I get the following when I run the script in demo mode. Active Directory is AD 2003 Native. Does it need to be AD DS 2008?

    [PS] C:\>.\New-PasswordReminder.ps1 -demo
    WARNING: Error initializing default drive: ‘Unable to find a default server with Active Directory Web Services
    running.’.
    Get-ADDomain : Unable to find a default server with Active Directory Web Services running.
    At C:\New-PasswordReminder.ps1:403 char:28
    + $global:dfl = (Get-AdDomain <<<< ).DomainMode
    + CategoryInfo : ResourceUnavailable: (SMA_NBCA:ADDomain) [Get-ADDomain], ADServerDownException
    + FullyQualifiedErrorId : Unable to find a default server with Active Directory Web Services running.,Microsoft.Ac
    tiveDirectory.Management.Commands.GetADDomain

    Get-ADUser : Unable to find a default server with Active Directory Web Services running.
    At C:\New-PasswordReminder.ps1:409 char:22
    + $users = Get-AdUser <<<< -filter * -ResultSetSize $null
    + CategoryInfo : ResourceUnavailable: (:) [Get-ADUser], ADServerDownException
    + FullyQualifiedErrorId : Unable to find a default server with Active Directory Web Services running.,Microsoft.Ac
    tiveDirectory.Management.Commands.GetADUser

    User Expires Policy
    ======================== ======= ===========
    Get-ADDefaultDomainPasswordPolicy : Unable to find a default server with Active Directory Web Services running.
    At C:\New-PasswordReminder.ps1:427 char:65
    + $DefaultDomainPasswordPolicy = Get-ADDefaultDomainPasswordPolicy <<<<
    + CategoryInfo : ResourceUnavailable: (Microsoft.Activ…nPasswordPolicy:ADDefaultDomainPasswordPolicy) [
    Get-ADDefaultDomainPasswordPolicy], ADServerDownException
    + FullyQualifiedErrorId : Unable to find a default server with Active Directory Web Services running.,Microsoft.Ac
    tiveDirectory.Management.Commands.GetADDefaultDomainPasswordPolicy

    Cannot convert value "" to type "System.Boolean", parameters of this type only accept booleans or numbers, use $true, $
    false, 1 or 0 instead.
    At C:\New-PasswordReminder.ps1:430 char:26
    + [bool]$PasswordComplexity <<<< = $DefaultDomainPasswordPolicy.ComplexityEnabled
    + CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException
    + FullyQualifiedErrorId : RuntimeException

    Get-ADUserPasswordExpirationDate : Cannot bind argument to parameter 'accountIdentity' because it is null.
    At C:\New-PasswordReminder.ps1:434 char:34
    + Get-ADUserPasswordExpirationDate <<<

    • Pat Richard
      June 19th, 2012 at 13:00 | #80

      Are you running it on a 2008 R2 domain controller?

      • June 19th, 2012 at 13:02 | #81

        No, I am running active directory on a Windows 2003 Server. Do I need to be on AD DS 2008 SP2 or AD DS 2008 R2?

        • Pat Richard
          June 19th, 2012 at 13:03 | #82

          Doubt that’s going to work, since your domain controllers aren’t going to have PowerShell on them.

      • June 19th, 2012 at 13:11 | #83

        Ok, Looks like I move the AD DS uopgrade ahead if this project now.

  38. sap
    June 21st, 2012 at 02:27 | #84

    will it work on win 2003 ? just curious.

    • Pat Richard
      June 21st, 2012 at 07:37 | #85

      Not likely. PowerShell doesn’t come in 2003, and I haven’t even looked at trying to come up with a solution for it.

  39. Aussupport
    June 22nd, 2012 at 01:02 | #86

    HI,
    I can run the script fin in demo and previewuser but not working in normal or task?

    PS C:\scripts> .\New-PasswordReminder.ps1 -demo

    User Expires Policy
    ======================== ======= ===========
    testuser01 2 90
    Development 4 90
    testuser1 11 90

    PS C:\scripts> .\New-PasswordReminder.ps1
    Send-MailMessage : Cannot validate argument on parameter ‘To’. The argument is null or empty. Supply an argument that i
    s not null or empty and then try the command again.
    At C:\scripts\New-PasswordReminder.ps1:346 char:28
    + Send-MailMessage -To <<<< $accountObj.mail -Subject "Your password expires in $DaysTillE
    xpire day(s)" -Body $emailbody -From $EmailFrom -Priority High -BodyAsHtml
    + CategoryInfo : InvalidData: (:) [Send-MailMessage], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.SendMailMessage

    Send-MailMessage : Cannot validate argument on parameter 'To'. The argument is null or empty. Supply an argument that i
    s not null or empty and then try the command again.
    At C:\scripts\New-PasswordReminder.ps1:346 char:28
    + Send-MailMessage -To <<<< $accountObj.mail -Subject "Your password expires in $DaysTillE
    xpire day(s)" -Body $emailbody -From $EmailFrom -Priority High -BodyAsHtml
    + CategoryInfo : InvalidData: (:) [Send-MailMessage], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.SendMailMessage

  40. alessandro
    July 7th, 2012 at 07:41 | #87

    It’s because one or more of the accounts involved in the query has the field MAIL empty in A.D. , fill them up (create to ALL of them (the list of -demo mode) a mailbox and it works), but now mine works only if I run it, I read somewhere about a double slash mistake and can’t find it anymore… someone can help me?

  41. Anil
    July 13th, 2012 at 00:48 | #88

    Please help me below queries.

    Only DC ip should be mentioned in Exchange SMPTP relay agent right ?
    Email id will be picked from DC account properties ( Email attribute ) ?
    I have to run only this script in DC ?
    Any additional requirement needs to be done on Exchange Server ?

  42. Rob Stewart
    July 16th, 2012 at 16:28 | #89

    This is an awesome script, Thanks you!

    We did run into one issue while testing. We did the preview command line but a couple of the admin users we tried to test against have their password set not to expire so they didn’t even get the preview email and they don’t show up in the demo list either.

    I haven’t looked to change the code but if it’s possible to not skip people like this in preview mode AND flag them in demo mode that would be great. In fact “demo” would really become “audit” at that point!

    • Pat Richard
      August 17th, 2012 at 16:43 | #90

      Ah – good catch. I’ll see what I can do.

  43. fredr0855
    July 18th, 2012 at 11:40 | #91

    Hello, thanks very much for this script. It is awesome! The only thing I can’t seem to get working is the transcript. Looks like for each email sent, it should write to a .log file. I did not see any instructions on how to config this to work. Any help would be appreciated!

    • Pat Richard
      July 19th, 2012 at 16:26 | #92

      Are you using the -transcript option? There’s probably not going to be much in there since the script is designed to run as a scheduled task (no real screen output). I’ll clarify that in the next version.

  44. chris326
    July 30th, 2012 at 17:51 | #93

    thanks for this awesome script Pat.
    A question for you though, when I run this in demo mode, the script only finds one user that is in the Built in Users OU. It does not see the other users in the Users OU nor does it find any other users in other OUs. I’m running this on a windows 2008 R2 server that has exchange 2010 installed and the domain functional level is 2008 R2. What am i missing?

    • Pat Richard
      August 17th, 2012 at 16:41 | #94

      Did you ever get this going?

  45. Eriq
    August 7th, 2012 at 12:17 | #95

    Thanks for this, I was able to customize it to do exactly what I want! The one thing I would like to request is the ability to email a list (formatted like the -demo option) to myself, or my team. Basically, it would be nice to receive an email every Monday with a list of the users with passwords expiring that week so we can also check remind them to change it, or at-least we will know to expect issues with them later in the week assuming they will still forget to change it (even after daily reminder emails). I’ve tried figuring out how to pipe the output of the -demo option to an email, but I’m still getting used to powershell. It would be awesome if a -admin option (or something like that) could be added to allow the list to be emailed to a specified address. If-not, maybe someone where can help me figure out how to properly pipe the output from -demo to the send-mailmessage cmdlet or something like that?

    Thanks again!
    ][Q}[

  46. David
    August 14th, 2012 at 03:46 | #96

    You made my day Pat, that Script is fantastic. I am still trying to follow all that functions and code you wrote 🙂

    The script itself is working fine, but I receive an error Message when the scripts hits an user without a mailbox, nevertheless the script is still excecuting like it should.

    
    

    Send-MailMessage : Cannot validate argument on parameter ‘To’. The argument is null or empty. Supply an argument that
    is not null or empty and then try the command again.
    At C:\New-Password\New-PasswordReminder.ps1:333 char:29
    + Send-MailMessage -To $accountObj.mail -Subject “Password will expire in ….”

    
    

    Is there any way to suppress that error? The first that is poping in my mind is to add that line on top of the script:

    
    

    $ErrorActionPreference = “SilentlyContinue”

    
    				
    • Pat Richard
      August 17th, 2012 at 16:39 | #97

      Is this running from an Exchange 2010 server, or Exchange 2010 management workstation?

    • Pat Richard
      August 17th, 2012 at 17:51 | #98

      David, this is fixed in 2.6, which should be out in a couple of days.

  47. Jon
    August 24th, 2012 at 03:48 | #99

    Hi Pat,

    Great script. Exactly what I’ve been looking for as many users we have use their mobile devices etc and never get the warning otherwise.

    It would be great to have some sort of log. Perhaps the output of the -demo switch could be sent to the helpdesk email every morning which would provide an overview of which users have had communication about their password.

    I think that would be quite easy to implement in the current script and perhaps you could include it as part of your 1.6 release?

    Thanks again for a great script so far! 🙂

    • Pat Richard
      August 25th, 2012 at 12:30 | #100

      Yeah, I’ve got some ideas for stuff like that, but I really need to build a lab that has some fine grained password policies so I can track down how to get the policy for a user. That’s a critical request, but something I haven’t had time to do.

      There is some event log features, too.

  48. Jon
    August 24th, 2012 at 03:49 | #101

    Sorry, meant 2.6 release!

  49. August 28th, 2012 at 07:01 | #102

    Nice Script 1st of all, thank you. keep good work going on

    I have a query when i change the default OU to a child one and run the script with demo parameter it still gives all the user’s info on the screen instead of the only existing in the child OU.

    Can you please explain its behavior?

    • Pat Richard
      September 4th, 2012 at 11:00 | #103

      Must be a weird quirk I’ll have to look into.

      • September 5th, 2012 at 07:05 | #104

        Please do let me know once you have done testing with it, as i want to run the test on a small OU and donot know if it would be executing all the 6000+ users as it doing with preview command, instead of child OU only.(3 users only)

  50. August 30th, 2012 at 16:25 | #105

    Exchange 2010 Sp1 running Windows 2008 R2 (doing CA, Hub, Transport, and Mailbox)

    Windows 2008 R2 Domain

    I have tried your script, and it works correctly till I test the
    New-PasswordReminder.ps1 -Preview -PreviewUser username

    I get the following error
    “Send-Mailmessage : The SMTP server requires a secure connection or the client was not authenticated. The Server response was: 5.7.1”

    I am running the script on the exchange server

    I did the “Creating a receive connector to use for sending email from PowerShell” using “New-ReceiveConnector -Name “Internal Relay” -Bindings 0.0.0.0:25 -RemoteIPRanges 127.0.0.1 -AuthMechanism Tls -Enabled $true -Fqdn “myserver.mydomain.com” -PermissionGroups AnonymousUsers -Server mysever”, changing the needed values.

    I did not run the following “Add-ADPermission -User “NT AUTHORITY\ANONYMOUS LOGON” -ExtendedRights “ms-Exch-SMTP-Accept-Any-Recipient””, I was unsure about opening up anonymous on the exchange server.

    Thanks

    • Pat Richard
      September 4th, 2012 at 10:59 | #106

      That only allows anonymous to relay to that receive connector, and only from the 127.0.0.1 address.

  51. September 4th, 2012 at 15:03 | #107

    I am truning to run the script

    Add-ADPermission -user “NT AUTHORITY\ANONYMOUS LOGON” -ExtendedRights “ms-Exch-SMTP-Accept-Any-Recipient”

    and I get
    cmdlet add-adpermission at command position 1
    supply values for the following parameters:
    Identity:

    Any help on this would be great.

    • Pat Richard
      September 4th, 2012 at 15:17 | #108

      As is mentioned in the dedicated post, you can pipe that all together at one time:

      New-ReceiveConnector -Name “Internal Relay” -Bindings 0.0.0.0:25 -RemoteIPRanges 127.0.0.1 -AuthMechanism Tls -Enabled $true -Fqdn “myserver.mydomain.com” -PermissionGroups AnonymousUsers -Server mysever | Add-ADPermission -User “NT AUTHORITY\ANONYMOUS LOGON” -ExtendedRights “ms-Exch-SMTP-Accept-Any-Recipient, ms-exch-bypass-anti-spam”

      If you ran the cmdlet to create the receive connector, and you’re not trying to add the AD permissions, you need to tell it what you’re assigning them to.

      Get-ReceiveConnector “Internal Relay” | Add-ADPermission -User “NT AUTHORITY\ANONYMOUS LOGON” -ExtendedRights “ms-Exch-SMTP-Accept-Any-Recipient”

  52. Hans
    September 6th, 2012 at 04:25 | #109

    Hit Pat,

    When i run the script manually it works fine, Mailing works. (demo or live)
    After i have run the command -install. The task is scheduled. But when the schedulling runs, no one receives an email. (not in spam or something like that)
    (even run it under domainadmin to be sure there are enough right) in the scheduler is see last run result (0X1) Also tried run with higest privileges. Also doenst work. Tried with or without quotes. Could you help.

  53. Hans
    September 6th, 2012 at 04:58 | #110

    Found it already
    there was a space in the string 🙂

    • Pat Richard
      September 6th, 2012 at 08:10 | #111

      Which string?

  54. Pat Richard
    September 7th, 2012 at 13:55 | #112

    Rolled out v2.6 today. I think it resolves most of the requests here, with the exception of displaying password policy requirements for fine grained password policies.

  55. David
    September 10th, 2012 at 05:04 | #113

    Thank you so much for sharing this with us 🙂 Release 2.6 fixed my issue with the error prompt when the script hits an user without a mailbox

  56. Len
    September 10th, 2012 at 11:46 | #114

    Hey Pat. Great script that I hope to have working soon.

    I’ve added the correct receive connector and the necessary permissions but I get the following error when I run the script in Preview mode;

    Send-MailMessage : The SMTP server requires a secure connection or the client was not authenticated. The server response was: 5.7.1 Client was not authenticated

    I have this corresponding error in my application log;

    The account ‘EXACTEARTH\Administrator’ provided valid credentials, but is not authorized to use the server; failing authentication.

    The script must be running as administrator when it’s sending the preview email. Any ideas?

    • Pat Richard
      September 10th, 2012 at 11:47 | #115

      Tell me what permissions settings are defined? Just anonymous?

      • Len
        September 10th, 2012 at 11:49 | #116

        Correct. Just Anonymous is checked under ‘Permission Groups’

  57. Len
    September 10th, 2012 at 11:54 | #117

    My bad. I had set PSEmailServer to the IP address of the CAS server (same system I’m running the script on). When I set it to 127.0.0.1 it works like a charm.

    Thanks!

  58. September 10th, 2012 at 16:34 | #118

    I am trying to run the script on a remote system.
    I can run the -demo with no error
    I can run the -Preview -PreviewUser username with no errors and I get the e-mail
    I can run the -install, and it creates the task with no error

    When I run the task I get a “0xFFC0000” error

    C:\Windows\system32\windowspowershell\v1.0\powershell.exe
    -psconsolefile “\Bin\exshell.psc1” -command “C:\inetpub\wwwroot\ftp.Hatch-inc.net\Password_Email\images\New-PasswordReminder.ps1”

    Thanks

    • Pat Richard
      September 10th, 2012 at 16:36 | #119

      Looks like the Exchange management tools aren’t installed on that remote machine. Notice the path to -psconsolefile.

  59. Jonathan
    September 18th, 2012 at 13:37 | #120

    I get the following error when trying to run the script.

    [PS] C:\Scripts>.\New-PasswordReminder.ps1 -demo
    You must provide a value expression on the right-hand side of the ‘/’ operator.
    At C:\Scripts\New-PasswordReminder.ps1:160 char:16
    + $env:UserName / <<<

    • Pat Richard
      September 18th, 2012 at 13:39 | #121

      What version is listed in the header? 2.6?

      • Jonathan
        September 18th, 2012 at 13:59 | #122

        yes v2.6

    • Jonathan
      September 19th, 2012 at 11:39 | #123

      Thank you Pat for getting it working. Apparently Dreamweaver added those line breaks to the code. Love this script, can’t wait to hear the feedback from the end users. =]

      • Pat Richard
        September 19th, 2012 at 15:38 | #124

        My pleasure. I’m pretty old school, and I use a text editor (UltraEdit) for my code. I’m always afraid that opening it in anything else is going to mangle it.

        Glad it’s working.

  60. September 21st, 2012 at 11:26 | #125

    Before I spen time looking at why it will not work, will it wok with Windows 2008R2 on a windows 2003 domain? Exchagne 2007.

    • Pat Richard
      September 21st, 2012 at 11:45 | #126

      Domain function level shouldn’t matter. So demo mode should work. I haven’t tested with Exchange 2007.

  61. Enrique
    September 23rd, 2012 at 16:31 | #127

    We are running AD 2003 with Office 365 as an e-mail provider. Would your script work in this case as well?

    We are running a “password sync” tool that syncs AD passwords to O365.

    • Pat Richard
      September 23rd, 2012 at 16:52 | #128

      In theory, yes. But I don’t have a suitable test environment to verify that. If you do test it, PLEASE include your results in the comments!

  62. Martyn B
    September 26th, 2012 at 11:28 | #129

    Hi

    We want to test this before changing our domain password policy to expire the passwords… Is there anyway to do this?

    Id like to verify I’ve got the email being able to flow correctly, and also that it can find accounts..

    If so i can then say yes we will turn on the domain policy but obviously cant do this until i can verify its working!

    I’m running it on Exchange 2010 server and its obviously working as im getting the event messages coming ok great!

    Had a look through the script and cant see anyway of switching it to either give last time password was set, or just notify regardless of what the current password policy is set as.

    Thanks

    Martyn

    • Pat Richard
      September 26th, 2012 at 19:48 | #130

      Yes, that’s explained in the post about -preview and -previewuser switches

  63. Matt
    September 26th, 2012 at 14:56 | #131

    Hello Pat,

    When I run the demo I recieve no results. We have seperate password policies set by group in AD. When I try to specify the OU I get the error:

    Get-ADUser : The supplied distinguishedName must belong to one of the following partition(s): …

    At C:\New-PasswordReminder.ps1:396 char:22

    Any Sugesstions?
    Thanks

    • Pat Richard
      September 26th, 2012 at 19:45 | #132

      When you defined an OU, did you include the full DN for the OU, and it’s in quotes? If you run it with -preview and -previewuser [username], do you get the email?

  64. Martyn B
    September 27th, 2012 at 03:50 | #133

    Thanks Pat

    The preview user doesnt show any details at all, and no emails are sent…
    Our passwords are not currently set to expire as we have lots of remote users who have issues with this. Which is why i want to implement the script, but checks its all ok first..

    Is there an option i can change to say – if the password is set to not expire then email??

    Thanks for this awesome script!!

  65. Pat Richard
    September 27th, 2012 at 07:41 | #134

    Preview option won’t show anything on the screen – it just sends the email to the user defined in -previewuser, regardless of what their expiration status is. If that’s not working, we need to find out why.

  66. Martyn B
    September 27th, 2012 at 11:32 | #135

    Thanks Pat – so if i use -preview -preview user “user here” it should send me an email?

    Cheers

    Martyn

  67. John Panicci
    October 15th, 2012 at 17:27 | #136

    Pat, thanks for the great script.. I dont have windows 2008 r2 domain controllers yet and its giving me, Unable to find a default server with Active Directory Web Services running. Can this script work without r2 domain controllers?

    • Pat Richard
      October 16th, 2012 at 11:12 | #137

      What version of PowerShell is installed on your DCs?

      • John Panicci
        October 16th, 2012 at 11:16 | #138

        version 2, ran $host.version and says Major 2, minor 0, build -1 and revision -1

  68. Bill H
    October 17th, 2012 at 08:22 | #139

    When I run demo from a 2010 server I don’t get any errors, however, I don’t get any results either. I set the days value to 60, 180, and as much as 1,000. I only changed the default values and not the OU.

    Version 14.2 Build 247.5 / 2008 R2 SP1 / All roles in single server

  69. James H
    October 23rd, 2012 at 16:48 | #140

    Awesome script, got it working and works perfectly! One suggested improvement would be to include in the event log entry, who the email was sent to.

    Thanks!

  70. ick
    October 26th, 2012 at 15:29 | #141

    Thanks for the great script. I have a question – our Exchange servers are in a resource forest seperate from the user accounts we want to report on. How can I get this script to run against user accounts in the user forest rather than the linked objects in the Exchange resource forest?

  71. Richard Eisenberger
    October 30th, 2012 at 08:31 | #142

    Pat, great job, thanks!
    1 question and 1 comment: When you specify an OU, does it recurse the sub OU’s under that?
    I second the request to (if possible of course) send an email to a set address with the list of who the password warning was sent to the last time the script was run.

    • Richard Eisenberger
      October 30th, 2012 at 08:36 | #143

      Well, my question is answered if I read correctly.
      Get-AdUser -filter * -SearchScope subtree The subtree value indicates all child objects, if I read it correctly.

  72. Andrea
    October 31st, 2012 at 13:13 | #144

    I have a problem with the HelpdeskURL. I will not have any image in the e-mail. I not set the $HelpdeskURL but nothing change. I try to set the variable with $null value but nothing changes. Anyone have issue with this variables?

    Andrea

    • Pat Richard
      October 31st, 2012 at 13:22 | #145

      I’ll be looking at adding a NoHTML option for future versions that should resolve this. Unknown as to when that might happen, though.

  73. NickC
    November 7th, 2012 at 11:54 | #146

    How can I change the script so the Demo mode shows DisplayName rather than samaccount name?

  74. Richard
    November 10th, 2012 at 19:44 | #147

    No decent editor here,, so I can’t giove you the line number.
    Look for “if ($demo) {Write-Host (“{0,-25}{1,-8}{2,-12}” -f $accountObj.Name, $DaysTillExpire, $PolicyDays)}”
    change $accountobj.name to $accountobj.displayname and give it a whirl

  75. Brandon
    November 12th, 2012 at 11:28 | #148

    Can you please post a generic example of how the $ou variable should look like when being set?

    • Pat Richard
      November 12th, 2012 at 11:49 | #149

      $OU needs to be in the “OU=users,OU=New York,dc=contoso,dc=com” format.

  76. Brandon
    November 20th, 2012 at 14:15 | #150

    Thank you!

  77. Andy
    November 22nd, 2012 at 13:59 | #151

    I still cant get the OU option to work – can anyone post a full example please – I think I’m being thick ;-/

    • Richard
      November 26th, 2012 at 11:06 | #152

      Andy,
      what do you have for youtr $ou string in the variables at the top?

  78. Bullet
    November 26th, 2012 at 08:32 | #153

    Thanks a lot for this great script. Recently it worked great but today when I add the parameter “demo” I don’t get any results. I have verified the pwdLastSet attribute in my ad and no one has changed the password yet. How is this possible?

    • Bullet
      November 26th, 2012 at 11:04 | #154

      Very strange, it stopped to work for 3h. Then I tried to debug it with “-v” for “verbose” and saw everything went through but no email was sent. Suddenly it started to work again, but there has never been an error given out so far.

      While my “-demo” stopped to work also the “-PreviewUser” didn’t send an email even tough it went through what I’ve seen so far with “verbose” output.

      I will monitor it a few days and report in my findings. If I have time I will also try to add functions like logging to email or a path for admins the actual result and also a text-only email instead of html email for instance as a switch.

      Thanks a lot again.

      Bullet

      • Pat Richard
        November 26th, 2012 at 11:06 | #155

        I’m working on some more logging features (including emailing an admin) and a -NoHTML option.

      • Bullet
        November 27th, 2012 at 03:51 | #156

        It happened again. I’m using Win2k8 R2 with PowerShell 2.0 / New-PasswordReminder.ps1 2.6 . The task scheduler is assigned to the user “SYSTEM” and runs at 06:00 every morning.

        As soon the this job has ended I cannot see any users with the “demo” argument anymore for a few hours. Job status is 0 (ok).

        _____

        PS C:\> .\New-PasswordReminder.ps1 -demo -v
        VERBOSE: Setting variables
        VERBOSE: Defining functions
        VERBOSE: Checking for ActiveDirectory module
        VERBOSE: Getting Domain functional level
        VERBOSE: Filtering users to OU=Users,OU=SubAcme,DC=Acme,DC=local
        VERBOSE: Demo mode

        User Expires Policy
        ======================== ======= ===========
        VERBOSE: Setting event log configuration
        VERBOSE: Getting password policy configuration
        VERBOSE: Getting the user info for user1
        VERBOSE: verifying that the password is not expired, and the user is not set to PasswordNeverExpires
        VERBOSE: Verifying if the date the password was last set is available
        VERBOSE: Getting the user info for user2
        VERBOSE: verifying that the password is not expired, and the user is not set to PasswordNeverExpires
        VERBOSE: Verifying if the date the password was last set is available
        VERBOSE: Getting the user info for user3
        VERBOSE: verifying that the password is not expired, and the user is not set to PasswordNeverExpires
        VERBOSE: Verifying if the date the password was last set is available
        VERBOSE: Getting the user info for user4
        VERBOSE: verifying that the password is not expired, and the user is not set to PasswordNeverExpires
        VERBOSE: Verifying if the date the password was last set is available

        _____

        I think it has something to do with this line and the “SYSTEM” account on line 199:
        if ($demo) {Write-Host (“{0,-25}{1,-8}{2,-12}” -f $accountObj.Name, $DaysTillExpire, $PolicyDays)}

        Maybe the output will be directed to somewhere else?

        Thanks in advance.

        • Bullet
          November 27th, 2012 at 07:29 | #157

          I tried to debug the script and found out, that the “pwdLastSet” field read by the script is empty. But when I use for instance AD Explorer from Sysinternals I can see that the fields aren’t empty. Any ideas for this temporary bug?

          Thanks Bullet

        • Pat Richard
          November 30th, 2012 at 09:16 | #158

          If you run the script manually with an elevated account, what happens?

  79. jam86
    November 26th, 2012 at 14:03 | #159

    Hello, thanks for all the hard work you’ve been putting on this script! The demo portion of it works great, but I’m not able to preview the e-mail. I’m having an issue with the send-MailMessage cmdlet. “Unable to connect to the remote server”. I’ve tried the FQDN and IP and it still doesn’t go through. Things to note:

    1.) Exchange 07, windows 2000 domain
    2.) Trying to run this script on another server.
    3.) I’ve ran other powershell scripts off that server before, so not sure why i’m getting this message..
    4.) Using v2.6 of the script.

    Any help is appreciated!

    • Pat Richard
      November 26th, 2012 at 14:06 | #160

      Send-Mail doesn’t require that the email server be Exchange. It’s just an SMTP cmdlet. So what’s likely the cause is you don’t have a receive connector in Exchange that will accept from the server running the script.

      • jam86
        November 30th, 2012 at 11:27 | #161

        I got it figured out, I thought it was the receive connector too, we had one enabled for this IP range (for the network printer e-mail functionality). I created one specifically for this one too but that did not resolve the issue. Turns out.. it was McAfee OAS blocking the SMTP attempts. Just ran a scheduled task (had to modify some settings thanks to lvjoe for his contribution) and so far everything has been running smooth.

        Only minor issue I’m having is that it doesn’t e-mail external users (non-domain e-mails). But that’s an issue unrelated to the script.

    • Bullet
      November 26th, 2012 at 15:28 | #162

      A very nice way to test mail functionality without to bother people is to use a dummy mail server for preview. Hassle free emulation of a working of a smtp server with just double click:
      http://smtp4dev.codeplex.com/

  80. Mark
    November 26th, 2012 at 15:42 | #164

    HI Pat,

    Thanks for this really nice scritp.
    I there a way i an make this script mail just Exhange users instead of doing both?
    Thanks in advance!

    Mark

    • Pat Richard
      November 30th, 2012 at 09:14 | #165

      I’m not sure I understand what you’re asking. What do you mean “both”?

  81. November 30th, 2012 at 10:39 | #166

    Thank-you for producing a really great script. I implemented it at my office with a customization of the email message body. I have scheduled a task to run a couple of times a week for anyone whose password needs to be changed within the next 14 days.

    When I run the script using demo mode, I get an expected listing of about a couple of dozen users and the days till expirartion and policy. When I use the -previewuser switch and my network username, I get a nicely formatted email So far so good.

    When the script runs as a scheduled task without these switches, the event log shows that it only processed just 2 or 3 users. The rest do not get emails. What can I do to troubleshoot and resolve this difficulty?

    • November 30th, 2012 at 11:04 | #167

      As a follow up to my prior comment, I have some new information. I had the scheduled task set to run using a service account that I typically use for such things. The task was set to run whether or not the account was logged in. When I reconfigured the task to run using my own domain admin account and watched the command windows pop up, it processed completely. The event log shows that all 28 accounts were correctly processed. I confirmed that users did get an email.

      So it seems to be related to how the task is run and not so much the script itself.

      • Pat Richard
        November 30th, 2012 at 11:28 | #168

        Is the box checked that says “run with highest privileges”? If not, check that box.

        • November 30th, 2012 at 11:41 | #169

          It wasn’t. I changed it. As curious as I am, I’m reluctant to keep spamming people, so I changed the task to run daily. I can check the event log over the weekend and see if this fixes it.

          I am curious why it is able to process a handful without “run with the hightest privileges.” I would expect that it would either run completely or not at all.

          • Pat Richard
            November 30th, 2012 at 11:58 | #170

            Not sure. I need to write a better function for creating the scheduled task so that I can touch more settings. So many things….

            Glad you got it working.

          • December 2nd, 2012 at 10:14 | #171

            You were spot on with your suggestion to check “run with highest privileges.” That definitely solved it for me. The scheduled task is running as expected with the service account and console logged out. Thanks so much!

            Another interesting curiosity is that the built-in Windows balloon help reminder seems to calculate one day less than than your script. A user who got a reminder that his password will expire in 14 days got a Windows balloon that said 13. At first I thought this might be because the script ran a few hours earlier than the balloon, but I ran the script subsequently in demo mode, and it still showed 14 days. I checked the value for “msDS-UserPasswordExpiryTimeComputed” on his account and based upon that, I’d say the balloon help in Windows calculated properly, whereas your script seems to be computing one more day. Any thoughts?

  82. Nick O’Toole
    December 6th, 2012 at 10:29 | #172

    Hi firstly thanks very much for this script, it is exactly what I was looking for. The script ran correctly however in the process it changed the AD account owner of all users from the Exchange Mailbox server to the main admins group. Can you tell me if its possible to remove this part of the script please. Thanks

    • Nick O’Toole
      December 6th, 2012 at 10:33 | #173

      As a follow up to my previous post this was discovered as users who previously could use the “Send As” feature now cannot use this and it will no longer allow me to enable this feature. Adding the Exchange Mailbox server back as the owner fixes this issue.

    • Pat Richard
      December 6th, 2012 at 11:14 | #174

      This script makes not changes to user accounts.

  83. David Fletcher
    December 6th, 2012 at 15:24 | #175

    Love this!
    Question: Can this be installed multiple times from different folders? We want to customize it per client company (we share our server with 3 other companies). This would allow us to customize company name and such for each client.

    Also, I’m using the msDS-UserPasswordExpiryTimeComputed ADUser property from the ActiveDirectory module in a logon script for our Remote Desktop servers to remind users on logon of an expiring password. Could that simplify your script? It seems to support fine grained password policies in our environment just fine (2008 R2).

    Thanks for your work on putting this together!

    • Pat Richard
      December 12th, 2012 at 13:01 | #176

      In theory, you should be able to run it multiple times from different folders.

      Haven’t looked at msDS-UserPasswordExpiryTimeComputed yet.

  84. André Boom
    December 19th, 2012 at 06:02 | #177

    Hi there, create info! The only problem is that I can’t find the location of the images that you used. I want to copy/adjust them and set them on my webserver. Do you have the images for us?

    • Pat Richard
      December 26th, 2012 at 00:04 | #178

      Those are linked in the download section

  85. Eric
    December 27th, 2012 at 14:54 | #179

    Hi,

    Great script. How can i modify the script to send the mail only to user with a specific mail extension. If there something else in the mail box i need to exclude them.

    Thanks

  86. Eric
    December 27th, 2012 at 15:01 | #180

    Ok, i read my comment and i think i’m not realy clear. So, I have users in my domain who have contact email like hotmail or gmail or no email adresses configured. So i need to exclude them from been warn.

    I hope you understand better.

    P.S. sorry for my traduction i’m a french speaking person

    Thanks

    • Pat Richard
      January 4th, 2013 at 19:36 | #181

      At this time, the script will fire on accounts that have an SMTP address in the Windows Email Address field.

      Unfortunately, I don’t have any plans to build in a bunch of exclusion options, as testing them would be extremely time consuming.

      • August 1st, 2013 at 09:22 | #182

        After working with a couple of other similar scripts before I found this I have to say THIS ROCKS!. I believe a simple tweak will accomplish the restriction however. In one of those I was using a filter such as Get-ADUser -filter {(mail -like “*@domain.com”) -and (Enabled -eq “True”) -and (PasswordNeverExpires -eq “False”)} which not only deals with non-domain emails, but also eliminates the need to check for accounts without emails, etc. I haven’t tried it yet but I’m wondering if that may not also improve processing speed. In my environment this script takes about 90 minutes to process. The other ones a was working with ran in a just a few minutes. They weren’t dealing with FGPP however so that may also be impacting things. Any thoughts as to what actually takes up the time? I’m looking at about 1000 users total, about 600 with emails.

        Thanks again for a great tool.

  87. Kapeng
    January 24th, 2013 at 17:21 | #183

    Hi, Thanks a lot for the script, it is really helpful.

    Just a quick question, if I schedule the script to run daily and set the DaysToWarn = 7, will it only send email on the 7th day once? Or it will send email once daily on the 7th, 6th, 5th, 4th …etc day till the exact expiry day?

    FYI, I would like the email remind user one week before their password is about to expire, then again when there are two days remaining to expire.

    Thank you.

    • Obi Ejiofor
      February 7th, 2013 at 16:54 | #184

      1 It will send the email daily until the expiration date

      2 The easiest solution is running two scripts one that runs weekly and one that runs daily

  88. Jay Victor
    January 29th, 2013 at 20:13 | #185

    Well I have got this wonderful script running except as a scheduled task. I can run it in demo perfect. I can run it in preview mode perfect. I can just run it in regular mode (no switches) perfect. My environment is 2008r2 DFL and the script is on one of my domain controllers at the moment. I used the install switch and that worked perfectly. I am running the task as my domain admin account and have the run with highest privileges box checked. in the task history it appears to run and complete just fine however there is no associated even in the application log stating that the script processed x number of users and no one recieves an email. Version 2.6 of the script. Any ideas?

  89. Andy
    February 12th, 2013 at 08:39 | #186

    Hi,
    Getting this error now..
    v2.7 running in the EMC on Server 2008 R2

    Cannot bind argument to parameter ‘accountIdentity’ because it is null.

    Any help appreciated

    Andy

  90. Andy
    February 12th, 2013 at 09:03 | #187

    Works great in -preview -previewuser mode to myself
    The above error is in -demo mode..
    Cannot test the script in normal mode as I need to test I have the OU$ parameter is right before hand so as not to annoy other admins in other OU’s ;-))

    Hope that makes sense.

  91. Andrew
    February 14th, 2013 at 12:03 | #188

    I’ve used the command Set-ExecutionPolicy RemoteSigned, but I still get a signature error when running.

    Also I’ve tried setting the OU command using:

    [string]$ou = “OU=Faulty\,\ Staff\ &\ Misc,DC=MyDomain,DC=ORG”

    But still get an error.

    • Andrew
      February 14th, 2013 at 12:22 | #189

      Ignore spelling mistake in “Faculty”

    • Andrew
      February 14th, 2013 at 12:56 | #190

      Fixed the signature issue, by copying all the text from the script and place it into a new locally created text file.

  92. Andy
    February 15th, 2013 at 06:08 | #191

    Fixed – I had a typo in mu OU string!! – working great – many thanks.

  93. February 15th, 2013 at 09:06 | #192

    This is proper fantastic! Was really sick of users being locked out during the middle of the day because they didn’t see the Win7 balloon informing them their password was about to expire.

    Setup was a breeze as well. Really big thanks!

  94. Brent
    February 15th, 2013 at 11:03 | #193

    Any way to get this to work with Microsoft Online Services?

    • Pat Richard
      February 16th, 2013 at 18:57 | #194

      Not that I’m aware of.

  95. Jon
    February 27th, 2013 at 19:45 | #195

    I must be a bit thick here as far as the OU is concerned.
    I have a Forest with three Child Domains. Users are only in the Child Domains. When I leave the $OU blank I only get a notice about one user in the Forest Root, but none in the Child domains.
    If I try to set one of the Child Domains in the $OU I get an error.
    I tried: $OU = “OU=Users,OU=Domain Users,DC=XXXXX,DC=internal”

    I am getting the following error;
    “Get-AdUser : The supplied distinguishedName must belong to one of the following partition(s): ‘DC=XXXXXX,DC=internal , CN=Configuration,DC=XXXXXX,DC=internal ,
    CN=Schema,CN=Configuration,DC=XXXXXX,DC=internal , DC=DomainDnsZones,DC=XXXXXX,DC=internal ,
    DC=ForestDnsZones,DC=XXXXXX,DC=internal’.
    At C:\Users\XXXXXX.XXXXXX\Desktop\New-PasswordReminder.ps1:423 char:12
    + $users = Get-AdUser -filter * -SearchScope subtree -SearchBase $ou -ResultSetS …

    • Pat Richard
      February 27th, 2013 at 20:12 | #196

      What happens when you run it from within one of the child domains?

  96. Paul
    February 28th, 2013 at 01:41 | #197

    Hello Richard,
    I am struggling to get this to work when I run
    PS C:\> .\New-PasswordReminder.ps1 -demo -v

    VERBOSE: Setting variables
    VERBOSE: Defining functions
    VERBOSE: Checking for ActiveDirectory module
    VERBOSE: Getting Domain functional level
    VERBOSE: Filtering users to OU=Users,OU=SubAcme,DC=Acme,DC=local
    VERBOSE: Demo mode

    User Expires Policy
    ======================== ======= ===========
    VERBOSE: Setting event log configuration
    VERBOSE: Getting password policy configuration
    VERBOSE: Getting the user info for user1
    VERBOSE: verifying that the password is not expired, and the user is not set to PasswordNeverExpires
    VERBOSE: Verifying if the date the password was last set is available
    VERBOSE: Getting the user info for user2
    VERBOSE: verifying that the password is not expired, and the user is not set to PasswordNeverExpires
    VERBOSE: Verifying if the date the password was last set is available
    VERBOSE: Getting the user info for user3
    VERBOSE: verifying that the password is not expired, and the user is not set to PasswordNeverExpires
    VERBOSE: Verifying if the date the password was last set is available
    VERBOSE: Getting the user info for user4
    VERBOSE: verifying that the password is not expired, and the user is not set to PasswordNeverExpires
    VERBOSE: Verifying if the date the password was last set is available

    it runs against every users but I get no results.
    domain level server 2008
    Ex 2010
    server 2008r2

    I have a pso with max password age 365 days applied to all members of a group which is working fine.

  97. Marshall
    March 4th, 2013 at 14:55 | #198

    Pat,

    We are getting an error in PS that states ‘The parameter “TranscriptFilename” is declared in parameter-set “_AllParameter Sets” multiple times.’

    Any ideas?

    • Pat Richard
      March 4th, 2013 at 15:01 | #199

      What version are you using?

      • Paul
        March 4th, 2013 at 16:55 | #200

        Hi Richard,
        if you are replying to my Post it is version 2.7

        • Pat Richard
          March 4th, 2013 at 16:57 | #201

          No, I was replying to Marshall.

  98. March 6th, 2013 at 05:49 | #202

    Hello,
    Thx for the lovely script.I wondering If i can send email in arabic content. I tried pasting some arabic content in the script but all i get is ???????.
    Pls advice.

  99. Kevin
    March 20th, 2013 at 12:05 | #203

    How could this script be altered to check for users in a specific Security Group?

    • Pat Richard
      March 20th, 2013 at 16:36 | #204

      You’d have to enumerate the members of the security group, then query the password info for each member. Unfortunately, I don’t have the resources to implement that.

  100. Patrick
    March 26th, 2013 at 22:37 | #205

    Great Script Pat! Please clarify something for me though. Is the process capable of reading a 2008 R2 AD Custom Password Expiration GPO or only the Default Domain Policy with the Password Expiration set? Reason I ask is I created a custom one and linked it to different OU’s and in the demo mode and demo -v mode the OU’s are correctly read but the Password Expiration Custom GPO isn’t. Only when I apply a Password Expiration policy to the Default Domain Policy does the demo mode show the expected results. Thank You.

    • Pat Richard
      March 26th, 2013 at 22:39 | #206

      The demo mode does show the expected results. Unfortunately, I have been unable to figure out how to read the granular policies. It’s on my to-do list.

      • Patrick
        March 27th, 2013 at 00:19 | #207

        Thanks for the clarification Pat!

  101. andre
    April 15th, 2013 at 03:06 | #208

    Goodmorning Pat,
    I tried to add it in the scheduled job with my admin account.
    I added it manually and with the adminsitrators account.
    I can run it manually but for some reason the task won’t run as a scheduled job.
    I run it with the highest privilleges but no luck.
    The settings are:
    Program/Script: c:\windows\system32\windowspowershell\v1.0\powershell.exe
    Add arguments(optional): -psconsolefile “C:\Program Files\Microsoft\Exchange Server\V14\Bin\exshell.psc1” -command “New-PasswordReminder.ps1”
    Start in: E:\website

    Do you have any suggestions?

  102. André
    April 15th, 2013 at 03:48 | #209

    Hi Pat,
    I think I figured it out. I will share the solution with you. After the command switch you have to include the entire path to the ps script. You should ignore the start in option in the scheduled task. After making this change ( in quotes ) it runs like a charm.

  103. Everton Bruno Bernardi
    April 16th, 2013 at 13:21 | #210

    Thanks for this great piece of script, Pat.
    I’m having some issues with special characters (ã, á, é, õ, ç) within the message body.
    I’ve checked the header and the charset is UTF-8 but it doesn’t seem to work.

    Could anybody please test it or tell me how to enable these characters?

    Kind regards.

  104. Bruce McKay
    April 18th, 2013 at 18:28 | #211

    Excellent. Works well.
    One thing we would like is to know that the script ran, emailed these perople, and was successfull. Maybe email a status to an admin email address with a summary??

  105. Nathan
    April 22nd, 2013 at 10:01 | #212

    Want to use this password expiry notification script but soon upgrading infrastructure to windows 2012 and exchange 2013 – will it still work? thanks

    • Pat Richard
      April 22nd, 2013 at 13:16 | #213

      It should work fine.

  106. David
    April 22nd, 2013 at 11:00 | #214

    Sorry if this post comes up twice but I got a timeout error the first time.
    Is there a way of excluding a specific child OU beneath the target? Or perhaps specifying multiple target OUs?

    • Pat Richard
      April 22nd, 2013 at 13:16 | #215

      Not at this time.

  107. Nathan
    April 23rd, 2013 at 06:44 | #216

    Pat – I’ve put your fantastic script in to a live environment after successful testing. I am getting an Exception calling “WriteEntry” with “3” arguments: The source was not found, but some or all event logs could not be searched. Inaccessible logs: Security

    I notice a long long thread on technet where you were having this problem:
    http://social.technet.microsoft.com/Forums/en-US/winserverpowershell/thread/e172f039-ce88-4c9f-b19a-0dd6dc568fa0/

    Is there anything I can do to get rid of this message? Will it cause a problem with the scheduled task running nightly?

    The password email is received correctly by the -preview user but dont want this error.
    Thanks
    Nathan

  108. Nathan
    April 23rd, 2013 at 06:58 | #217

    Сергей Попов – are you talking about my problem “WriteEntry” ?

  109. Robert
    April 25th, 2013 at 04:05 | #218

    Thank you for the script! I wonder if you or anyone else can help me. Is there a way to change from the Display name to a combination of given and sn?

    • Pat Richard
      April 25th, 2013 at 14:09 | #219

      There is always a way with PowerShell. Unfortunately, I don’t have much free time now.

  110. Edward
    May 7th, 2013 at 01:21 | #220

    Hello,

    I ran the .ps1 script ( v2.8 ) with the -demo option and no errors are displayed on screen. No list of users either

    PS C:\xxxxxxxxxxx> .\New-ADPasswordReminder.ps1 -demo

    User Expires Policy
    ======================== ======= ===========
    PS C:\xxxxxxxxxxx>

    Running Windows Server 2008 R2 Forest Functional Level and Exchange 2010

    Kind Regards
    Edward

    • Pat Richard
      May 7th, 2013 at 08:28 | #221

      Are you positive you have users with passwords expiring soon?

      • Edward
        May 8th, 2013 at 20:56 | #222

        Hello Pat,

        That was the problem. Thanks for the help. Everything appears to be working. I have yet to see the scheduled task kick in but have checked the highest privileged checkbox and will see how it goes.

        Kind Regards,
        Edward

  111. Edward
    May 7th, 2013 at 02:04 | #223

    Hello Where would you specify the encrytion Unicode? when saving the file?

    • Pat Richard
      May 7th, 2013 at 08:27 | #224

      Is there a reason why?

      • Denis
        June 4th, 2013 at 05:18 | #225

        For example, if i write in Russian in the email. In this case, email will be unreachable.

  112. Corey Zamara
    May 21st, 2013 at 13:58 | #226

    By far the best notification script out there. 1000+ users and it works flawlessly! Great job!

  113. George Billios
    May 30th, 2013 at 01:26 | #227

    First of all thanks for the script. Can I suggest a modification to the user search filter from * to {(objectclass -eq “User”) -and (Enabled -eq “True”) } in order to only return the non disabled users as there is no point to inform disabled users that their password will expire.

    Thanks

  114. flyntof
    May 31st, 2013 at 19:38 | #228

    Pat

    Fantastic script. Works really well as designed

    Thanks again for you efforts

  115. Tony
    June 4th, 2013 at 05:23 | #229

    I really good script, thanks.

    What i would like to have is a builtin “variable”/Funktion so i can install the script towards different domains, i have now in the script changed all the rows that have something with get-ad….. with extension “-server domain.local”.

  116. Pat Richard
    June 7th, 2013 at 08:59 | #230

    Yeah, I might need to add an encoding option to the parameters.

  117. Eyal
    June 20th, 2013 at 06:00 | #231

    hi,
    Thanks very much for this script.
    How can i modify the script to send the mail only to specific user ?

  118. Christheo
    July 31st, 2013 at 03:10 | #232

    Hi Pat. I have a domain that is used for terminal services only and has no Exchange server. Will this script be able to work in a domain with out a exchange server but with an SMTP Virtual Server? will it require the Exchange Management tools?

    • Pat Richard
      August 2nd, 2013 at 21:50 | #233

      In theory, as long as you have an SMTP server to send to, it should work.

  119. Richard
    August 2nd, 2013 at 04:03 | #234

    Hey Pat, I am one of your great follower and the scripts have made my line of work so easy. Keep up the good work. my issue here is do you have scripts for office 365. we have just migrated and I do not seem to be receiving the password change email.

  120. Obi Ejiofor
    August 2nd, 2013 at 04:33 | #237

    creating the automated scheduled tasks does’nt work to well

    the parameters for manualy creating the scheduleds task are

    scheduled task

    action
    start a program

    program
    C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe

    argument
    -ExecutionPolicy Bypass -File passwordreminder.ps1

    Start in
    C:\powershellscripts\

  121. Richard
    August 5th, 2013 at 03:43 | #238

    Hey Pat, this is the best, i had just implemented this at my office. because we are a big organization, we were asked to migrate to office 365, now i can not receive password expiry mails. How do i integrate this with office365.

    • Pat Richard
      August 11th, 2013 at 00:46 | #239

      That has not been attempted or tested with this script. Sorry.

  122. August 5th, 2013 at 11:02 | #240

    Thanks for a great script. How do I get it to work on my child domains?

    • Pat Richard
      August 6th, 2013 at 13:15 | #241

      It needs to be installed in each domain at this point.

  123. Erik Drake
    August 9th, 2013 at 16:18 | #242

    I am trying to get this working on my Exchange 2010 Server running on Win Svr 2008 R2 Enterprise with no luck.

    I receive the following when I run in Demo mode:

    Exception calling “WriteEntry” with “3” argument(s): “The source was not found, but some or all event logs could not be
    searched. Inaccessible logs: Security.”
    At C:\Scripts\New-ADPasswordReminder.ps1:459 char:16
    + $evt.WriteEntry <<<< ($EventLogText,$infoevent,70)
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    Any ideas?

    • Pat Richard
      August 11th, 2013 at 00:37 | #243

      Have you changed the permissions on your security event log? If it can’t access the log, it can’t see if the source is available so that it can write to event logs.

      • Erik Drake
        August 12th, 2013 at 12:50 | #244

        I tried changing the perms on the log, but I still can not get it to work. Part of the issue is that I am not sure what user/group/service etc. I should be adding or changing. What should I change exactly? I tried adding domain admins (which should allow my account write access), Local service, network service, Exchange servers, etc… All with no joy.

    • Jessica Hodges
      August 28th, 2013 at 17:53 | #245

      I had the same issue until I restarted power shell and selected “run as administrator”.

      • Pat Richard
        August 28th, 2013 at 22:13 | #246

        Yep – most of the issues with it not running are resolved when it’s run with elevated rights.

    • Pat Richard
      September 12th, 2013 at 22:56 | #247

      Test the script manually in an elevated session. I see that error when it’s not an elevated session.

    • Ian
      November 18th, 2013 at 12:39 | #248

      It needs to be run as an administrator.

  124. Steve
    August 12th, 2013 at 01:33 | #249

    Brilliant – worked first time on our Exchange 2013 and WIn2K8 environment. Well done Sir !

  125. Sebastian
    August 12th, 2013 at 07:15 | #250

    Hi all!

    we had the issue that the script lists all the “hidden” AD users which are created internally for domain trusts. As those users are managed by the AD itself but don’t have a password that never expires they are listed..if you try to mail them..it fails because they don’t have a mail address.. I altered the filter to exclude those users from the get-aduser command.

    I used the following LDAPFilter: -ldapfilter ‘(!(name=*$))’ instead of the -filter * part.

    Change that in line 423 or 425, whichever you use.

    Probably that can be included in the next version..

    • Pat Richard
      September 12th, 2013 at 23:03 | #251

      It’s been added to v2.9. Thanks for the info.

      • Ryan
        September 22nd, 2013 at 19:48 | #252

        Hey Pat!
        Not sure if you saw my last post, but is there a way to have this run against a single OU instead of the whole domain forest?

  126. August 22nd, 2013 at 07:40 | #253

    I’ve been working with this and during testing it works flawlessly. I’ve run into an odd problem however when the scheduled task runs. It runs, and it appears to do it’s job just fine, but a spot check with some of my users revealed that they weren’t getting the notification emails. I tweaked the script to bcc my test account with all the emails and in this morning’s run, despite the fact that there were 14 users with expiring passwords that should have been notified, only 2 emails got delivered. I ran the script manually and all 14 go through with no problems. Very odd. I’d understand if it didn’t work at all, etc., but why piecemeal? Any ideas?
    Thanks

  127. Ryan
    September 2nd, 2013 at 02:22 | #254

    Is there anyway to limit this to run against users in a specific OU

  128. Navdeep
    September 18th, 2013 at 04:41 | #255

    HI Pat, I am trying to use the script on Server 2012 with exchange 2010 but i am getting the error as below. please suggest

    Import-Module : The ‘C:\Windows\system32\WindowsPowerShell\v1.0\Modules\ActiveDirectory\ActiveDirectory.psd1’ module cannot be imported because its manifest contains one or more members that are not valid. The valid manifest members are (‘ModuleToProcess’, ‘NestedModules’, ‘GUID’, ‘Author’, ‘CompanyName’, ‘Copyright’, ‘ModuleVersion’, ‘Description’, ‘PowerShellVersion’, ‘PowerShellHostName’, ‘PowerShellHostVersion’, ‘CLRVersion’, ‘DotNetFrameworkVersion’, ‘ProcessorArchitecture’, ‘RequiredModules’, ‘TypesToProcess’, ‘FormatsToProcess’, ‘ScriptsToProcess’, ‘PrivateData’, ‘RequiredAssemblies’, ‘ModuleList’, ‘FileList’, ‘FunctionsToExport’, ‘VariablesToExport’, ‘AliasesToExport’, ‘CmdletsToExport’). Remove the members that are not valid (‘HelpInfoUri’), then try to import the module again.At C:\Program Files\Microsoft\Exchange Server\V14\Scripts\New-PasswordReminder.ps1:121 char:17
    + Import-Module <<<< -Name "$name" + CategoryInfo : InvalidData: (C:\Windows\syst…eDirectory.psd1:String) [Import-Module], InvalidOperatio nException + FullyQualifiedErrorId : Modules_InvalidManifestMember,Microsoft.PowerShell.Commands.ImportModuleCommand

    Get-AdDomain : The term 'Get-AdDomain' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. At C:\Program Files\Microsoft\Exchange Server\V14\Scripts\New-PasswordReminder.ps1:391 char:28
    + $global:dfl = (Get-AdDomain <<<< ).DomainMode
    + CategoryInfo : ObjectNotFound: (Get-AdDomain:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

    Get-AdUser : The term 'Get-AdUser' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. At C:\Program Files\Microsoft\Exchange Server\V14\Scripts\New-PasswordReminder.ps1:398 char:22
    + $users = Get-AdUser <<<< -filter * -ResultSetSize $null
    + CategoryInfo : ObjectNotFound: (Get-AdUser:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

    User Expires Policy
    ======================== ======= ===========
    Get-ADDefaultDomainPasswordPolicy : The term 'Get-ADDefaultDomainPasswordPolicy' is not recognized as the name of a cmd let, Junction, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. At C:\Program Files\Microsoft\Exchange Server\V14\Scripts\New-PasswordReminder.ps1:420 char:65
    + $DefaultDomainPasswordPolicy = Get-ADDefaultDomainPasswordPolicy <<<<
    + CategoryInfo : ObjectNotFound: (Get-ADDefaultDomainPasswordPolicy:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

    PasswordComplexity : Cannot convert value "" to type "System.Boolean", parameters of this type only accept booleans or numbers, use $true, $false, 1 or 0 instead. At C:\Program Files\Microsoft\Exchange Server\V14\Scripts\New-PasswordReminder.ps1:423 char:26
    + [bool]$PasswordComplexity <<<< = $DefaultDomainPasswordPolicy.ComplexityEnabled
    + CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException
    + FullyQualifiedErrorId : RuntimeException

    Get-ADUserPasswordExpirationDate : Cannot bind argument to parameter 'accountIdentity' because it is null. At C:\Program Files\Microsoft\Exchange Server\V14\Scripts\New-PasswordReminder.ps1:427 char:34
    + Get-ADUserPasswordExpirationDate <<<

    • Pat Richard
      September 18th, 2013 at 08:26 | #256

      Open a new elevated PowerShell session, and try running Import-Module ActiveDirectory. Does it succeed without error?

      • Navdeep
        September 18th, 2013 at 08:46 | #257

        HI Pat, just tried and it went to next line without any errors

        • navdeep
          October 22nd, 2013 at 07:46 | #258

          but still dosent work :/

  129. Sam Speight
    September 22nd, 2013 at 23:47 | #259

    Hi Pat, I would like to use this in my organisation. However I want to use it with the -noimages switch. It works correctly except I would like it to use a font that isn’t Times new roman in the body. Is there an easy way I can set this? Sorry I have no HTML knowledge and my fumblings have achieved nothing useful.

    Thanks and great job, this is really useful stuff.

  130. MC
    October 1st, 2013 at 05:00 | #260

    i got this error while run in demo.

    [PS] C:\New-ADPasswordReminder.v2.9>.\New-ADPasswordReminder.ps1 -demo
    Property ‘SupportsPaging’ cannot be found for type ‘System.Management.Automation.CmdletBindingAttribute’.
    At C:\New-ADPasswordReminder.v2.9\New-ADPasswordReminder.ps1:86 char:15
    + [CmdletBinding <<<< (SupportsShouldProcess = $True, SupportsPaging = $True)]
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyAssignmentException

    • Pat Richard
      October 1st, 2013 at 08:59 | #261

      Let me guess. It’s running on a server that’s not Windows 2012? Look for the line that reads
      SupportsShouldProcess = $True, SupportsPaging = $True
      and change it to
      SupportsShouldProcess = $True

      • MC
        October 1st, 2013 at 10:07 | #262

        It works, thank you..

        • Mexifinn
          October 28th, 2013 at 09:56 | #263

          Regarding this – it says “requires v2”. This should say “requires v3” since supportspaging is a Powershell V3 command

          • Pat Richard
            October 28th, 2013 at 09:57 | #264

            Actually, everything else in the script support v2. If you remove the supportspaging part, it runs fine in v2. Next update will resolve that.

      • Ian
        November 13th, 2013 at 12:46 | #265

        Do you mean change it to false?

        • Pat Richard
          November 13th, 2013 at 13:09 | #266

          No – remove the “SupportsPaging = $True” part.

      • Ian
        November 13th, 2013 at 12:49 | #267

        Oh, you mean remove “SupportsPaging = $True” from line 86. Duh.

  131. navdeep
    October 22nd, 2013 at 07:38 | #268

    I am trying to schedule the task to send emails with no image but it dosent seems to run when i add -noimage at the end

  132. Surendharan.GS
    October 24th, 2013 at 01:31 | #269

    Thank you, the script worked well , but am having user getting an email even though they changed password.

  133. adzm
    November 3rd, 2013 at 11:48 | #270

    do you have to have OWA?

    We don’t use Outlook 🙁

    • Pat Richard
      November 3rd, 2013 at 11:50 | #271

      No. This script has nothing to do with HOW you change your passwords. You just have to have a method to change them, and edit the script accordingly.

  134. Ian
    November 13th, 2013 at 12:44 | #272

    Hi Tony,

    I’m getting an error in Powershell when trying to run this script in demo mode. I’m running Powershell as an admin in Windows 7.

    This is the error –
    PS C:\ADPasswordReminder> .\New-ADPasswordReminder.ps1 -demo
    Property ‘SupportsPaging’ cannot be found for type ‘System.Management.Automatio
    n.CmdletBindingAttribute’.
    At C:\ADPasswordReminder\New-ADPasswordReminder.ps1:86 char:15
    + [CmdletBinding <<<< (SupportsShouldProcess = $True, SupportsPaging = $True)]
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyAssignmentException

    I've tried googling around for this error but I cannot find anything on it. Any ideas? I tried the previous versions of the script and they worked fine, this is the latest version and it's failing.

    I do not intend to run this on an exchange server, is that the reason why I'm getting this error?

    • Eric
      December 25th, 2013 at 09:07 | #273

      I am getting this same error. I am running this on a 2008r2 Terminal Server with the Exchange management tools and other mentioned requisites installed.

  135. Kevin
    December 16th, 2013 at 12:19 | #274

    Is there an easy way to have it so that the line “ALERT: You must change your password today or you will be locked out!” only shows up on the last day? It would great if I could replace it with “ALERT: If you don’t change your password in days, you will be locked out!” Is this possible?

    • Pat Richard
      December 16th, 2013 at 12:23 | #275

      Anything is possible in PowerShell. 🙂

      I’ll add it to the wish list.

  136. David
    December 17th, 2013 at 13:12 | #276

    Quick Question… will this script ignore accounts that have their password set to never expire? If not, I can work around that as I really should put them in their own OU for organizational reasons.

    • Pat Richard
      December 17th, 2013 at 13:14 | #277

      It should ignore those. Let me know if you see otherwise.

      • David
        December 17th, 2013 at 17:46 | #278

        Excellent. And am I right in gather that this script is intended to be run from the exchange server itself? Could I modify the receiver configuration and run it from a different server?

        • Pat Richard
          December 18th, 2013 at 10:02 | #279

          It doesn’t have to run from an Exchange server. The line that begins
          [string]$PSEmailServer
          is where you define the email server.

  137. Kevin
    December 18th, 2013 at 09:26 | #280

    Another question, how do I have this display the message in Chinese, German and Portuguese? Is this possible?

    • Corey Zamara
      December 18th, 2013 at 09:58 | #281

      sometimes people give you a really cool gift for free and you still want the world on a silver platter.

    • Pat Richard
      December 18th, 2013 at 09:59 | #282

      There is no support for other languages, and unfortunately, I have no time to adapt it for that.

      • Kevin
        December 18th, 2013 at 10:18 | #283

        Ok, thanks. Great script btw.

  138. John
    January 6th, 2014 at 18:34 | #284

    Love the script, and have it running against a pilot OU right now. I’d like to have one scheduled task that send out a notification only if a user’s password is at 10 days to expiration, and then another at 5 days or less (5,4,3,2,1). Having problems with this though, what’s the best way to modify the script to be able to accomplish this?

  139. VP
    January 20th, 2014 at 12:03 | #285

    When I try to run .\New-ADPasswordReminder.ps1 -Install on Windows 2008 R2 Enterprise I get below error message

    Property ‘SupportsPaging’ cannot be found for type ‘System.Management.Automation.CmdletBindingAttribute’.
    At C:\Scripts\New-ADPasswordReminder.ps1:86 char:15
    + [CmdletBinding <<<< (SupportsShouldProcess = $True, SupportsPaging = $True)]
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyAssignmentException

    My server already have AD Module for Windows Powershell installed

    • Pat Richard
      January 20th, 2014 at 12:05 | #286

      Open the script in notepad. Find the line
      [CmdletBinding(SupportsShouldProcess = $True, SupportsPaging = $True)]
      replace it with
      [CmdletBinding(SupportsShouldProcess = $True)]

  140. VP
    January 20th, 2014 at 12:22 | #287

    Hi Pat,

    Thanks for your help and this has resolved my problem. One question will it be possible for me to test for single user as when I try to execute “.\New-ADPasswordReminder.ps1 -Preview -PreviewUser UserADID” I get below error message

    C:\Scripts\New-ADPasswordReminder.ps1 : Missing an argument for parameter ‘PreviewUser’. Specify a parameter of type ‘S
    ystem.String’ and try again.
    At line:1 char:38
    + .\New-ADPasswordReminder.ps1 -Preview <<<< -PreviewUser paliathv -NoImages
    + CategoryInfo : InvalidArgument: (:) [New-ADPasswordReminder.ps1], ParameterBindingException
    + FullyQualifiedErrorId : MissingArgument,New-ADPasswordReminder.ps1

    • Pat Richard
      January 20th, 2014 at 12:37 | #288

      My email address is in the script header. Send me an email with a screenshot.

  141. VP
    January 20th, 2014 at 12:46 | #289

    Hi Pat,

    I have managed to fix my account issue, however when I run .\New-ADPasswordReminder.ps1 -Demo I get below error message

    [PS] C:\Scripts>.\New-ADPasswordReminder.ps1 -Demo

    Import-Module : The following error occurred while loading the extended type data file:
    Microsoft.PowerShell, C:\Windows\system32\WindowsPowerShell\v1.0\Modules\ActiveDirectory\ActiveDirectory.Types.ps1xml :
    File skipped because it was already present from “Microsoft.PowerShell”.
    Microsoft.PowerShell, C:\Windows\system32\WindowsPowerShell\v1.0\Modules\ActiveDirectory\ActiveDirectory.Types.ps1xml :
    File skipped because it was already present from “Microsoft.PowerShell”.
    Microsoft.PowerShell, C:\Windows\system32\WindowsPowerShell\v1.0\Modules\ActiveDirectory\ActiveDirectory.Types.ps1xml :
    File skipped because it was already present from “Microsoft.PowerShell”.
    At C:\Scripts\New-ADPasswordReminder.ps1:131 char:17
    + Import-Module <<<< -Name "$name"
    + CategoryInfo : InvalidOperation: (:) [Import-Module], RuntimeException
    + FullyQualifiedErrorId : FormatXmlUpateException,Microsoft.PowerShell.Commands.ImportModuleCommand

    The term 'Get-AdDomain' is not recognized as the name of a cmdlet, function, script file, or operable program. Check th
    e spelling of the name, or if a path was included, verify that the path is correct and try again.
    At C:\Scripts\New-ADPasswordReminder.ps1:419 char:28
    + $global:dfl = (Get-AdDomain <<<< ).DomainMode
    + CategoryInfo : ObjectNotFound: (Get-AdDomain:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

    The term 'Get-AdUser' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the
    spelling of the name, or if a path was included, verify that the path is correct and try again.
    At C:\Scripts\New-ADPasswordReminder.ps1:428 char:22
    + $users = Get-AdUser <<<< -ldapfilter '(!(name=*$))' -ResultSetSize $null
    + CategoryInfo : ObjectNotFound: (Get-AdUser:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

  142. Gary
    January 23rd, 2014 at 03:26 | #292

    Thanks for that great script. But it do not work.
    Can you please help me with this error?

    Property ‘SupportsPaging’ cannot be found for type ‘System.Management.Automation.CmdletBindingAttribute’.
    At C:\Scripts\New-ADPasswordReminder.ps1:86 char:15
    + [CmdletBinding <<<< (SupportsShouldProcess = $true, SupportsPaging = $true)]
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyAssignmentException

    Regards

    • Pat Richard
      January 23rd, 2014 at 08:18 | #293

      That’s discussed in the comments here.

      • Gary
        January 23rd, 2014 at 08:28 | #294

        yes sorry… i didnt see it before…..Script is working great.
        Thank you very much.

        regards

  143. PSY
    February 11th, 2014 at 00:34 | #295

    Many thanks for the great script!. It works like a real smoothie… I had a query regarding our environment. We have an OU which contains non-mail Exchange users, they have external email add configured in the email property of AD account. Is it possible to send mail to external users? Cheers

  144. JIL
    February 12th, 2014 at 00:28 | #296

    Hi, Kudos and thank you for the awesome script, this makes things soo easy!!. I am currently facing an issue while running this script int the demo mode. It is showing incorrect value of 180 days whereas the Policy is set to 60 days. Could you please point me to the right direction. Thanks!

  145. February 12th, 2014 at 15:13 | #297

    Love this script it saved me from having to rewrite the one we were using and broke. One thing though you should check for account disabled since those accounts should not get reminder emails and frequently their email is forwarded to other users so could lead to confusion. This is easy to add in case anyone else wants to just modify the ForEach ($user in $users) loop as follows:

    ForEach ($user in $users){
    If ($user.Enabled)
    {
    Get-ADUserPasswordExpirationDate $user.samaccountname
    }
    }

  146. February 26th, 2014 at 08:25 | #298

    When I try to run C:\New-ADPasswordReminder.ps1 -Preview -PreviewUser username
    I receive this error
    C:\New-ADPasswordReminder.ps1 : Missing an argument for parameter ‘PreviewUser’. Specify a parameter of type ‘System.St
    ring’ and try again.
    At line:1 char:39
    + C:\New-ADPasswordReminder.ps1 -Preview <<<< -PreviewUser ant
    + CategoryInfo : InvalidArgument: (:) [New-ADPasswordReminder.ps1], ParameterBindingException
    + FullyQualifiedErrorId : MissingArgument,New-ADPasswordReminder.ps1

    Any ideas?

    • Jay
      March 12th, 2014 at 10:31 | #299

      Leave out “-preview”, leaving just “-previewuser “.

  147. Duncan
    March 6th, 2014 at 06:34 | #300

    Hi,
    This doesn’t seem to filter ‘disabled’ accounts, how could I modify it to do so?
    Cheers,

    • Pat Richard
      March 6th, 2014 at 09:30 | #301

      That’s coming in a future release.

      • Duncan
        March 6th, 2014 at 10:10 | #302

        Excellent. A ‘report’ feature might be nice as well, to send a list of passwords expiring within the next few days to an admin account.
        Thank you, it’s already saved me a ton of work!

  148. Jay
    March 12th, 2014 at 08:00 | #303

    Nice script. Thank you. A minor point: The preview syntax seems to be just “-previewuser” (not “-preview” as the in-script documentation states at lines 43 and 44).

    I chose the add the following lines after the Send-MailMessage line (actually after ‘$global:UsersNotified++’), so we can see who got sent them emails and when:

    $EventLogText = “Password expiry email sent to $emailto ($DaysTillExpire days(s) until expiry)”
    $evt.WriteEntry($EventLogText,$infoevent,71)

    So, after each run of the script you’ll get an Event ID 70 summarising how many mails were sent and the same number of ID 71s detailing each individual who got the email. Sweet!

  149. Chris
    April 8th, 2014 at 19:15 | #304

    Hi thanks for your efforts on this promising looking script. When I run it in -demo mode I get the following error.

    Property ‘SupportsPaging’ cannot be found for type ‘System.Management.Automatio
    n.CmdletBindingAttribute’.
    At D:\Password Expire Script\New-ADPasswordReminder.ps1:86 char:15
    + [CmdletBinding <<<< (SupportsShouldProcess = $True, SupportsPaging = $True)]
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyAssignmentException

    I am running this on the exchange server which is Windows 2008 R2 with powershell 2.0. I have the "Active Directory modeule for powershell" installed. Any help would be greatly appreciated.

    thanks
    Chris

  150. Kenyon
    June 17th, 2014 at 01:28 | #305

    I have had this script running great on my 2008 R2 server for a year and a half. I’m retiring that old server, and moving this to a 2012 server running Exchange 2010. In my testing I’m having issues getting the script to run. I execute with the -demo parameter and get this:

    “Import-Module : The ‘C:\Windows\system32\WindowsPowerShell\v1.0\Module\ActiveDirectory\ActiveDirectory.psd1’ module cannot be imported because its manifest contains one or more members that are not valid….. It then lists the valid members and fails on a bunch of other cmdlets.

    So, questions. Will this script run on a 2012 server running Exchange 2010? If yes, what do I need to do to make it work? I have confirmed that the AD Module for PowerShell is installed and am running the script as the admin. I’ve tested the old script, and also downloaded the latest version.

    I know this thread hasn’t had any activity for a year, but hopefully someone knows something. Thank you.

  151. Kyle
    June 30th, 2014 at 13:14 | #306

    Hey Pat. We’ve been using this script in our Exchange 2007 environment flawlessly and it has cut down on the amount of servicedesk related tickets for password resets. I would like to sincerely thank you for your work on this script. We have recently migrated to Office 365 (very painfully) and obviously this has broken the password reminder script from emailing our users as our old Exchange server is no longer in production. Any info on how to get this up and running or will I have to contact Microsoft to try and get this to work with their email servers. I’ve tried to point the email server to the correct new address, but still receiving an error: “Unable to read data from the transport connection: net_io_connectionclosed.”. Please tell me there is a chance to get this working with Office 365 servers!

    Thank you,
    Kyle

    • Kyle
      July 7th, 2014 at 08:22 | #307

      Pat never mind this comment. Since we use dirsync I have figured a way to get this to work without any major changes. Your script is a life saver and now back in business!

      Thanks again,
      Kyle

  152. Speights
    July 7th, 2014 at 00:09 | #308

    Anyone know if this works on Exchange 2013?

  153. Dinesh
    July 8th, 2014 at 05:11 | #310

    how to remove table format & set in Normal mail body?

  154. Patrick
    August 13th, 2014 at 09:36 | #311

    Awesome script. Is there a way to add in searching for accounts which passwords have expired and then emailing an address, in this case our helpdesk, so they can then reach out to users proactively?

    • Pat Richard
      August 13th, 2014 at 09:46 | #312

      Not at this time. I can add it as a feature request.

  155. Dave
    February 25th, 2015 at 14:27 | #313

    Hi Pat,

    Thanks for writing this script, it works perfectly. You have saved a lot of admin from the headache of password reset/account lockout issues. I was wondering if you have a place to donate to your work? I checked the page, but didn’t seem to find one. Also, I read through the comments and saw a particular feature that would be helpful. I would like to add a CC of the email to a manager of the person that has their password expiration notice sent. Would you mind assisting with this or is that a feature you might add?

    Regards!

  156. Hans
    April 9th, 2015 at 03:01 | #314

    can i install it on another machine then exhange?
    we are working with o365,

    the .\install installs -psconsolefile \Bin\exshell.psc1 (but i dont have exchange)

  157. LarryR
    April 16th, 2015 at 11:51 | #315

    Pat this looks great. I can’t wait to try this out. Will let you know the results. We run exchange 2010 on 2008R2 servers on a 2008R2 functional level domain.

  158. Lars
    May 27th, 2015 at 18:20 | #316

    Send-MailMessage : The specified string is not in the form required for an e-mail address.

    Why do I get this error when i run this script and also in preview mode?

  159. Markus
    June 5th, 2015 at 03:13 | #317

    Hi!

    Could do it with variable too 🙂 (didn’t notice that post above till now, uhoh)

    Added variable $enc and a switch -Encoding $enc to the Send-MailMessage.

    To the start where variables are defined:

    # Set encoding to one of these values: System.Text.ASCIIEncoding, System.Text.UnicodeEncoding, System.Text.UTF32Encoding, System.Text.UTF7Encoding, System.Text.UTF8Encoding
    $enc = New-Object System.Text.utf8encoding

    And the send-command:

    Send-MailMessage -To $emailto -Subject “Salasanasi vanhenee {$DaysTillExpire}:n päivän päästä” -Body $emailbody -From $EmailFrom -Priority High -BodyAsHtml -Encoding $enc

  160. Red
    July 1st, 2015 at 07:26 | #318

    Hi,
    great work and tnx for sharing.
    Is there away to have the script search in current domain and ALL subdomains? something like “IgnoreDefaultScope” ?!?
    Regards.

    Red.

  161. Alex Samad
    July 13th, 2015 at 23:01 | #319

    @Nathan

    Was there a fix for this ?

  162. Markus
    July 14th, 2015 at 03:46 | #320

    I understood that this script ought to take fine grained password policy into consideration too, but at least in demo-mode it doesn’t seem to do that. Couldn’t find a definitive answer from here does it support that or not?

  163. fred
    July 14th, 2015 at 15:52 | #321

    @Markus
    Yes we use fine grain password policy and it works great!

  164. Valtteri
    August 7th, 2015 at 06:09 | #322

    Hi!

    How to restrict email messages only to active user accounts? So Disabled user accounts would be skipped

    Thanks!

  165. Chris
    August 7th, 2015 at 12:03 | #323

    Hi, trying to run this on a Windows 2008 server, getting error.

    Property ‘SupportsPaging’ cannot be found for type ‘System.Management.Automation.CmdletBindingAttribute’.
    At E:\adpwscript\New-ADPasswordReminder.ps1:86 char:15
    + [CmdletBinding <<<< (SupportsShouldProcess = $True, SupportsPaging = $True)]
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyAssignmentException

    • Pat Richard
      August 12th, 2015 at 09:22 | #324

      You have a version of Powershell that’s too old.

  166. Derek
    September 28th, 2015 at 16:42 | #325

    Get-AdUser : The object name has bad syntax
    At \\98pwdsct001\c$\scripts\FirstQ_PasswordExpiration.ps1:384 char:12
    + $users = Get-AdUser -filter * -SearchScope Subtree -SearchBase $ou #-ResultSet …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Get-ADUser], ADException
    + FullyQualifiedErrorId : The object name has bad syntax,Microsoft.ActiveDirectory.Management.Commands.GetADUser

    User Expires Policy
    ======================== ======= ===========

    • Pat Richard
      October 6th, 2015 at 00:21 | #326

      What happens when you run it directly from that machine, instead of via UNC path?

  167. zabolyx
    October 9th, 2015 at 16:18 | #327

    I’ve now read every post above looking for an answer.

    Running on 2012 R2 and it is a Hybrid Exchange for our o365.

    When running Preview or Demo it works great. Get a nicely formatted email from preview and demo does find the users needing the emails as they should. Freaking awesome script.

    I’m having an issue when the task runs. It completes as it should no error, but it sends an email to everyone in the company everyday, not just the users under 14 days. The email they get is also only the footer image and the helpdesk link message.

    Preview shows everything in the test email correctly.

    I’m sure it is something that I have dorked up while editing the code. I am not what you might call well acquainted with Powershell so you might need to use small words.

  168. zabolyx
    October 27th, 2015 at 18:50 | #328

    @zabolyx
    Figured out my issue. Must have broken it when editing the HTML.

    Started from scratch and rebuild the email portion and it seems to be working like a champ.

    Now to mod another copy of it to disable users email access to o365 when their password expires and send an email to helpdesk about the user’s lack to comply.

    Awesome script. Thanks

  169. February 4th, 2016 at 04:56 | #329

    Hi, first of all,thanks a lot.
    Script running fine on 2012R2 Exchange 2013 BUT expiration date calculation is wrong. always get: Your xxxx password expires in 1 day(s),

    tried with several users in -preview mode.
    Any suggestion on this?
    thanks!

    • Pat Richard
      February 4th, 2016 at 09:06 | #330

      Preview mode always shows the 1 day warning by design.

  170. February 5th, 2016 at 03:51 | #331

    @Pat Richard

    BAAAM! Thanks a lot!

  171. February 29th, 2016 at 09:01 | #332

    Hi!

    Script works grate, except for users that have FGPP. The expiration time is shorter than in the default policy. Could that be the problem?

    When I tested by removing the check has password expired, those users came up as well.

  172. March 7th, 2016 at 03:02 | #333

    @Markus
    *great (not grate 😀 )

  173. Jakob
    March 16th, 2016 at 04:35 | #334

    I cant seem to figure out how to install it with the -noimage flag active? Anyone care to help?

  174. JinaGouda Kalchimad
    May 23rd, 2016 at 05:26 | #335

    HI Richard,

    Thank you for such great post.
    I need your help.
    the notification sent out should have two headings;
    1. in red colour mentioning the action required.
    2. in green colour mentioning the steps to change the password.

    any help would be appreciated.

    Thanks.

  175. Tyson
    May 31st, 2016 at 10:00 | #336

    @Pat Richard

    This works great, but like many others we are moving to O365. Any chance you will be working on an updated version for O365?

    • Pat Richard
      May 31st, 2016 at 10:18 | #337

      Highly unlikely.

  176. Jon McNamara
    May 31st, 2016 at 10:27 | #338

    @Kyle
    Hi Kyle, How do you have this setup with O365 as we will be migrating our Exchange to O365 soon and would like to keep this notification in play.
    Thanks,
    Jon.

  177. Frosty
    June 12th, 2016 at 16:20 | #339

    ^^ I got this to work with Office365, but clients AD for still on sites just the exchange is on Office365.

    http://o365info.com/smtp-relay-in-office-365-environment/
    REF# Part 1 – Implementing SMTP mail relay using IIS server

  178. Ron van Saanen
    December 1st, 2016 at 05:56 | #340

    Hi, is there a way to CC the administator in this email, so that we can doublecheck that the email arrives at the users.
    Greetings, Ron

  179. UFUK
    July 12th, 2018 at 04:44 | #341

    I have issue about Turkish character. How to fix it?

    For example

    “Değişim zaman”ı seems like “De?i?im zaman?!”

    I changed in Get-AdUserPasswordExpirationDate.ps1

    to

    not working. How to fix it?

  180. October 11th, 2021 at 04:44 | #342

    how do i include the credentials for the smtp sender id in it. without which i will not be able to send the mails.

  1. December 26th, 2012 at 14:30 | #1
  2. April 17th, 2014 at 18:19 | #2
  3. August 31st, 2014 at 18:09 | #3
  4. September 17th, 2014 at 04:39 | #4
  5. December 8th, 2018 at 17:31 | #5