Troubleshooting my Password Change Notification script
September 17, 2014 24 Comments
I regularly receive questions on my PowerShell Script for Password Change Notification. Whilst i have tried to keep the Q&A limited to the Technet Gallery page, it occurred to me i might be better off putting together a basic troubleshooting guide a bit like a FAQ.
What you have to understand is i am inherently lazy, which is why i wrote the script in the first place, and being a bit short on ideas for posts.. well, here we are.
So with that in mind, i will dissect the script and cover some of the most common questions.
You will probably need a copy of the script handy and also PowerShell ISE running.
The first 39 lines are dedicated to explanation and variables you will need to change to suit your environment, for example the log file name/location the SMTP Server address and your email addresses.
Lines 25 to 36 deal with Logging, and will check your log file setting to see if the file exists, if not, it will create the file with the relevant column headings. The log file is in a CSV format so it is easy to review in Excel.
Line 40 is the first line where we actually attempt to gather some information, and it is the best line to start with when troubleshooting. If line 40 does not work, then the rest of the script will be useless.
So, how can we test that?
$users = get-aduser -filter * -properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress |where {$_.Enabled -eq “True”} | where { $_.PasswordNeverExpires -eq $false } | where { $_.passwordexpired -eq $false }
We can simply put that command into any PowerShell window on our server, and see which users it finds. The command as shown here, will find ALL USERS in the Domain, and then process each one to only store those that the Account is enabled, the Password does expire and where the Password is not currently expired.
Looking back now, this command could be made more efficient but that is another topic.
What result do i get when running this command?
Interestingly, it only returns one user. Which it should not.
Like anything in this situation we need to broaden our search. Lets remove the last bit of the query for people who have passwords that have already expired.
$users = get-aduser -filter * -properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress | where {$_.Enabled -eq “True”} | where { $_.PasswordNeverExpires -eq $false }
This time i get the 5 results i was expecting. This tells me that the accounts i was expecting to see had Passwords that had already expired. Simple.
Next we can look at another common question, relating to emails not being sent. Part of the information we pull out of Active Directory, is the users Email address. I did that because the environment where i designed the script had an Exchange Organisation, so every user had an address populated. First place to start troubleshooting is to make sure the email address is actually populated.
You can see above that none of these users have an email address stored in Active Directory. That is relatively easy to solve by simply editing the user account properties.
Any other issues with email are likely to be related to the SMTP Server.
You can test that with a simple command in any PowerShell Session.
Send-MailMessage –smtpServer <my smtp server> –from <my address> –to <your address> –subject <mysubject> –body <my body>
Some people have asked if they can have the script email when various days remain till the password expires.
This is really simple to achieve.
If you look at line 99 we are saying “if the number of days till this password expired is more than 0 but less than $expireinDays, then send an email”. This means, that an email will always be triggered regardless of how many days there are until the password expires, as long as it is between those two values. To be more specific and notify only when a certain number of days are remaining you could do the following.
# Send Email Message
if (( ($daystoexpire) -eq “2”) -or (($daystoexpire) -eq “4”) -or (($daystoexpire) -eq “6”))
{….}
Which, you guessed it, would mean a notification being sent only if the $daystoExpire was 2, 4, or 6.
Another question recently related to empty log files. No errors were created. Which suggests the script thinks it is doing everything right,. Well, like any man knows it’s not what you say, its the way you say it that is important.
So if you get an empty log file, chances are something is not right on line 99.
Creating a slightly modified version of the script to remove the email component altogether and turn it into a reporting tool, we can quickly see whether or not users are matched by the script, and the date there password will expire/expired.
Inserting HTML images is the last common question i want to look at.
Maybe you need a company logo, or just want to insert a picture of a kitten into their reminder to garner their attention. Who knows. I certainly don’t, but i do know how to do it.
The body section starting on line 77 is relatively sparse when you download it, and it can be fully customised using any HTML code you like. The important thing to remember is you need to double quote anything that requires quotes. Otherwise PowerShell thinks you are ending the section contained within the quotes..
The good thing about using PowerShell ISE is it will alert you to when things are going to go wrong. As you can see above, the color changes and we get a red underline. If we amend that to have double quotes..
The red underline disappears, and we can begin to mock those who need a reminder to change their password.
Another important factor is that the image be accessible wherever this email may be opened up, for that reason i used an image hosted online. Putting in a local image path or a UNC path may not work as you expect, as it is not embedded into the email.
Hope this was useful, there are plenty of other questions and answers over here if this did not help!
I adjusted the script a bit for our needs, as we only need to inform our Mobile Users and also speed up the AD search with giving a Searchbase to the get-aduser.
added:
$SearchBase = “OU=Usergroup,DC=domain,DC=com” # Start search here
$SecurityGroup = “*Mobile-User*” # Only inform users in that group
changed:
$users = get-aduser -SearchBase $SearchBase -filter * -properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress, CannotChangePassword, MemberOf |where {$_.Enabled -eq “True”} | where { $_.PasswordNeverExpires -eq $false } | where { $_.passwordexpired -eq $false } | where { $_.CannotChangePassword -eq $false } | where { $_.MemberOf -like $SecurityGroup}
Thanks so much for your effort, Really it’s a very great script.
But I need a way to run it automatically what i’m did as follow:
1- start–>administrative tools –> Task schedulers
2- In General Tap –> I give a name for the task and in the security options i chose “Run whether user is logged on or not” and I used the domain Administrator user and Password
3- in Triggers Tap i specify a daily task to run at a specific time.
4- in Actions Tap I chose Action: Start a program
in Program/Script: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
in Add arguments:c:\Password.ps1
In Start in: c:\
Is there any additional information i should add after all of this and if the information mentioned above is right so why the script is not sending any mails and it give that the task is completed.
use ‘powershell.exe’ as the program and under arguments ” -command (path to script)”
Worked for me a treat :)
Thanks
Worked well for me thanks.
Hi Robert,
if I try to run the script email when various days remain till the password expires, like in your sample:
# Send Email Message
if (($daystoexpire) -eq ((“2”) –or (“4”) –or (“6”) –or (“8”))
{….}
I´m only get E-Mails for i.e. 10/100 affected Users and it always writing days to expire is one, but that´s not true.
If I´m using the normal script it´s output all users in the specified time.
One hint: you missed a “)” at the end of your example.
your example ->
if (($daystoexpire) -eq ((“2”) –or (“4”) –or (“6”) –or (“8”))
working ps line ->
if (($daystoexpire) -eq ((“2”) –or (“4”) –or (“6”) –or (“8”)))
Thanks Tim I will update that.
Hi Robert
When I run this query it gives me noting and when I run the script it is empty:
$users = get-aduser -filter * -properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress | where {$_.Enabled -eq “True”} | where { $_.PasswordNeverExpires -eq $false }
What if you run this?
get-aduser -filter * -properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress
What if you run this?
get-aduser -filter * -properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress | where {$_.Enabled -eq “True”}
and this?
get-aduser -filter * -properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress | where {$_.Enabled -eq “True”} | where { $_.PasswordNeverExpires -eq $false }
Hello guys,
When executing the script I receive the following error:
New-TimeSpan : Cannot bind parameter ‘End’. Cannot convert the “90.00:00:00” value of type “System.TimeSpan” to type “System.DateTime”.
At W:\EXC-PowerShell\Password Change Notification.ps1:56 char:54
+ $daystoexpire = (New-TimeSpan -Start $today -End $Expireson).Days
+ ~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [New-TimeSpan], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.NewTimeSpanCommand
Is it something with dates/time format ?
This is usually because the value for ‘end’ is null.
Is it for all users or just some?
You will need to run some manual tests on the password set date and max age values to see if they are calculated correctly.
Solved. Checked the calculations you mentioned and found what was wrong. Thanks!
Great script and a huge thank you for making this freely available. I’ve got it working swimmingly, and have made some tweaks successfully based on your comments, videos and research.
I’m curious if you have any suggestions for a weird issue, that would be otherwise painful to track down.
Some of our accounts don’t have a “First Name” and “Last Name”, as I’ve built this to display the name in the log/report
$first = $user.GivenName
$surname = $user.Surname
$name = $first + ” ” + $surname
As is, the report just has a blank name column, and lists my “admin” e-mail account as the e-mail account for those accounts. Therefore, I have no easy way to ID which account it actually is (I thought, for a time, it was order in AD but that doesn’t seem always the case and obviously doesn’t scale well).
Looking at what is possible in get-aduser, I’ve identified the “SamAccountName” attribute, with the goal of adding that to the log/report (.csv file).
I’ve modified this entry to add the headers
Add-Content $logfile “Date,Name,SamAccountName,EmailAddress,DaystoExpire,ExpiresOn,Notified”
The above change works, and the .csv correctly adds the header.
I’ve modified the $users line and -properties to include the SamAccountName,
In the Process for each user I’ve added
$SamAccountName = $user.SamAccountName
And lastly
Add-Content $logfile “$date,$Name,$SamAccountName,$emailaddress,$daystoExpire,$expireson,$sent”
However, when the data is entered into the report, I run into the following problems:
The SamAccountName is never piped, and, all the other data is pushed over one column so it falls out of sorts
I assume I’m missing something trivial – any tips?
You shouldn’t need to request SamAccountName under -properties, as it is part of the default set.
I would try removing that first, because the rest of what you did looks ok.
Ah, good catch.
So I made the changes and removed the value from the -properties section, and made some progress, although its strange.
What it’s doing, is putting the SamAccountName in the .csv for some folks, but not most. If it puts the SamAccountName in place, everything is aligned correctly. If not, the emailaddress is in the SamAccountName field, and everything is one field over, causing all the data to be skewed.
Here is a copy of the text, with data changed, for reference
Date Name SamAccountName EmailAddress DaystoExpire ExpiresOn Notified
4052016 Tom Coughlin admin@domain.com 18 5/23/2016 10:36 No
4052016 Tito Fernandez tfernandez admin@domain.com 10 5/15/2016 9:32 Yes
You’d think if I had something wrong it wouldn’t work at all, strange. Any thoughts?
It does seem strange.
You can send me your full script if you want and I can take a look.
That would be awesome – can you drop me a note with your e-mail address at
rosederekj at gmail dot com
I can reply with a scrubbed down version of the script – thanks!
http://titlerequired.com/support
Ah, that works too. I just sent it, thanks!
OK, I ran it on my lab system and it worked fine – no issues with SamAccountName, so, with that in mind I would probably drill down further and just get a list of all users and there SamAccountName value and see if anything odd occurs.
From my testing it doesn’t appear to be an issue with your code.
Great – thanks for taking a look. I’ll go down that path and see what I can come up with. Appreciate the extra set of eyes.
So I found the bulk of my problem – I only updated the Add-Content $logfile with the $SamAccountName to the $sent = “No” section, which is what was causing the $sent = “Yes” to push the data over. Oops :D
Thanks for the second set of eyes regardless, apologies for taking up your time with something so silly!
We really love this script and are using it daily. We noticed a difference in the actual expire date vs what the email states. Is there a way to fix that? See below for one scenario. We run your script daily.
“Yesterday I got a notification email that said I had two days before my password expires but today I got an email that says it expires today. My password doesn’t actually expire until tomorrow but the email says it expires today.”
It’s to do with rounding the dates if I remember correctly. https://youtu.be/az_POurjDmQ