Taking a Bite Out of Password Expiry Helpdesk Calls

Published: 2023-04-19
Last Updated: 2023-04-20 00:31:24 UTC
by Rob VandenBrink (Version: 1)
3 comment(s)

By call volume, the top problem that most Helpdesks face is expired or forgotten passwords.  In this story we'll try to make a dent in the first one - expired passwords.

It's simply human nature to leave things to the last minute, especially if you don't get nagged (aka "reminded") about it.  Active Directory passwords are especially bad for this, as they tend to just leap out at you with "your password is expired, it's time to change it NOW".

This leaves your people at a disadvantage.  Not only can't they remember what the policy is or what we talked about during that part of their security awareness training, but they can't get to their notes because they CAN'T. LOG. ON.

This script tries to take care of this for you.  it'll send them a daily reminder for the 7 days (or whatever you set) before their expiry date, with links to the policy and a reminder of what not to use in their password, and why.

OK, let's get to the code.

Before we start, we'll need today's date in a variable:

$now - get-date

Next, collect all the user accounts in AD, note that neat "Expression" block where we compute the password expiry date for each

$b = Get-ADUser -filter {Enabled -eq $True -and PasswordNeverExpires -eq $False} –Properties SAMAccountName,"DisplayName", "msDS-UserPasswordExpiryTimeComputed","emailaddress",mobilephone, officephone, telephoneNumber | Select-Object -Property SamAccountName,"Displayname",@{Name="ExpiryDate";Expression={[datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")}},"emailaddress", mobilephone, officephone, telephoneNumber

Note that "ExpiryDate" computed field. 

So who will be expiring in the next 7 days?

$expirydays = 7
$pwdpeople = $b | where-object {($_.expirydate -$now).days -le $expirydays} | where-object {($_.expirydate -$now).days -ge 0} | sort-object -property expirydate


We collected the phone numbers because we're also interested in the helpdesk phone folks who have expired and maybe don't know it yet.  So who has expired in the last 30 days?

$ExpiredPeople = $b | where-object {($_.expirydate -$now).days -lt 0} | where-object {($_.expirydate -$now).days -ge -30} | sort-object -property expirydate

How do we let folks know that they're expiring soon?  We'll use email, because that's what corporate runs on (and it's easier than slack or teams or whatever cool chat / collab thing you are using). We'll send using the send-mailmessage command.  This covers everyone, if they leave things until they expire, you'll be able to tell them we TOLD YOU, 7 times (or however many days you choose), and you'll have the emails to back you up.
Note that the send-mailmessage command is SMTP only, so it's clear-text.  You'll want to send this to an internal relay host that will in turn forward it to the affected folks in a more secure way.  
Microsoft doesn't have a secure alternative to this in Powershell, but I'll cover off a few alternatives (one actually from Microsoft) in my next story.

Anyway, the command looks like this:

send-mailmessage -smtpserver $mailserver -subject $subj -from $from -to $p.emailaddress -Body $emailtext -BodyAsHtml


The "$emailtext" variable has to be a string (a simple text file will not fly, that would be too easy), the -BodyasHtml parameter has that string being interpreted as HTML.


Putting it all together ..

$now = get-date

# Collect all users with their respective password expiry dates
# note the computed field ExpiryDate

$b = Get-ADUser -filter {Enabled -eq $True -and PasswordNeverExpires -eq $False} –Properties SAMAccountName,"DisplayName", "msDS-UserPasswordExpiryTimeComputed","emailaddress",mobilephone, officephone, telephoneNumber | Select-Object -Property SamAccountName,"Displayname",@{Name="ExpiryDate";Expression={[datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")}},"emailaddress", mobilephone, officephone, telephoneNumber

# who has expired in the last 30 days? (note that $recent is negative)
$recent = -30
$ExpiredPeople = $b | where-object {($_.expirydate -$now).days -lt 0} | where-object {($_.expirydate -$now).days -ge $recent} | sort-object -property expirydate

# who will be expiring within the next week?
$expthreshold = 7
$pwdpeople = $b | where-object {($_.expirydate -$now).days -lt $expthreshold} | where-object {($_.expirydate -$now).days -ge 0} | sort-object -property expirydate

# Define mail parameters
$mailserver = "mail_or_relay_server_fqdn"
$from = "helpdesk@yourcompany.com"
$msg = (get-content ./mailskeleton.txt)

# send the note to each person in turn
foreach ($p in $pwdpeople) {
    $days = ($p.expirydate - $now).days
    $subj = "Your Password will expire in "+$days+" days"
    $emailtext = $msg.replace("XXX",$days) | out-string
    send-mailmessage -smtpserver $mailserver -subject $subj -from $from -to $p.emailaddress -Body $emailtext -BodyAsHtml
    }

# send the two lists of people (expired and soon to expire) to the helpdesk
$subj = "List of recently expired accounts"
$expiredpeople | export-csv ./ExpiredAccounts.csv
$body = "List of recently expired accounts.  Please contact each of them within the next 24 hours."
$to = "helpdesk@yourcompany.com"
send-mailmessage -smtpserver $mailserver -subject $subj -Body $body -attachments ./Expiredaccounts.csv -from $from -to $testaddr


$subj = "List of accounts that will expire soon"
$pwdpeople | export-csv ./ExpiringAccounts.csv
$body = "List of accounts that will expire soon.  Please expect them to contact you shortly."
send-mailmessage -smtpserver $mailserver -subject $subj -Body $body -attachments ./Expiringaccounts.csv -from $from -to $to

 

So, what does a typical email look like?  The one that my skeleton creates looks like this:

 

What else can you use this for?  Sending expiring passwords of service accounts to the helpdesk (or whoever owns those accounts) is another common thing I've seen.

If you find this code useful, you can find it on my github: https://github.com/robvandenbrink

If you see a better way to code any of this, or have another use for this bit of code, by all means let us know in the comment section!

===============
Rob VandenBrink
rob@coherentsecurity.com

3 comment(s)

Comments

According to recent NIST guidelines, two-factor authentication is a better idea than password expiry or password strength rules anyway.
If possible, sure, going with MFA is 100% ideal, and getting there for your personal logins is something lots of folks can do.
But in AD we still see lots of things that don't play nicely with MFA - starting with every corporate app that uses LDAP for authentication, and hasn't even stepped up to LDAPS yet, let alone MFA. Service accounts too, no MFA in most automated processes.
Anything with that uses RADIUS with an AD back-end you can usually wrangle to use MFA somehow though.
You definitely can do Windows Hello for AD these days (also some other MFA solutions), but it's a new enough thing, and a large enough project that most orgs haven't gone there yet.
Which in the best case sadly still leaves us with AD passwords that need to be managed in most shops....
Agreed, MFA is not always the easiest thing to implement, either from a technical or a user perspective,but the point I was primarily trying to make is that recent guidelines and research highlight the fact that too stringent password rules might actually cause unintended behavior in end users (just Google for "strict password rules makes passwords less secure" and you'll get a lot of background information).

Diary Archives