In this article I will show you how PowerShell can automatically send an e-mail notification to end users when their Active Directory password is set to expire soon. I looked over several other similar scripts on TechNet but ended up writing one from scratch to incorporate several features I needed, as well as improve on the overall script flow.
Some of the requirements I needed to meet were as follows:
- Get users email addresses from the Email Address value in Active Directory, if it’s empty look at the default address in the proxyaddresses attribute
- Send E-mail to users with passwords that were expiring in 7 days or less
- Include directions users can follow to reset their Active Directory password
- Send E-mail with high priority
- Get E-mail notification if sent or failed
- Store the credential in a Cred object so it’s secure
- Advanced logging on the script for troubleshooting
- Send E-mail from Office 365
- Set up the script as a scheduled task
User E-Mail Address
One of the key pieces I wanted was to check the users proxyaddresses attribute for a default e-mail address if the E-mail entry in the users Active Directory properties was empty. Many scripts I found online only looked at the E-mail value, so if there was nothing there it would just fail instead of looking at other places. In a lot of environments I have seen that not all users had the E-mail value filled out, but had their default e-mail address and aliases in the proxyaddresses attribute. For Office 365, if you sync your Active Directory this is where it will get the default e-mail address, and any other proxy addresses for Exchange. The script will find the default value by looking at what’s in the proxyaddresses attribute and looking for the value with a capital “SMTP”.
E-Mail Format
In my e-mail I wanted to first greet the user by their Name. This isn’t hard as all we have to do is pass something like their DisplayName value to a variable and add that to the body. Second, I wanted to send the e-mail with high importance, this will show a red exclamation mark next to the e-mail. The subject of the email would tell the user how many days until their password is expired and lastly, if the user wanted to reach to our IT team it would have our contact information followed by our signature.
An e-mail notification would be sent back to the sending address on any failure or any success. This would include the actual e-mail that was sent to the user in an attachment, and the subject of the e-mail. If you have a password portal, on-premise exchange or Office 365 password write-back you could also include a link in the e-mail where users can click and reset their passwords.
SMTP
Since my mail server is Office 365 I want to send mail authenticated, and use SSL. The user I am using to authenticate against the SMTP server must also either be the sending user, or have Send-As rights on the sending user. When the script runs it will search for its credential object, if the credential object is not there it will prompt you for credentials (its best to run this manually first to input and have it store the credentials before setting it up as a scheduled task). As we see in the Get-Credential message it reminds you that this account must be the sender, or have proper permissions.
Sending E-Mail
There are two ways you can go about sending mail with PowerShell. You can use the System.Net.Mail Namespace or use the Send-MailMessage cmdlet. If you want to set this up as a scheduled task I recommend using the System.Net.Mail Namespace (which I use in the script), as you will run into issues trying to use Send-MailMessage unattended.
In the scriptblock below I am calling the Namespace, setting the From, SMTP Host, SSL, Credentials, To, Subject, Delivery Notifications, Priority and Body.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
$SmtpClient = new-object system.net.mail.smtpClient
$MailMessage = New-Object system.net.mail.mailmessage
#Who is the e-mail sent from
$mailmessage.From = $FromEmail
#SMTP server to send email
$SmtpClient.Host = $SMTPHost
#SMTP SSL
$SMTPClient.EnableSsl = $true
#SMTP credentials
$SMTPClient.Credentials = $cred
#Send e-mail to the users email
$mailmessage.To.add(“$emailaddress”)
#Email subject
$mailmessage.Subject = “Your password will expire $daystoexpire days”
#Notification email on delivery / failure
$MailMessage.DeliveryNotificationOptions = (“onSuccess”, “onFailure”)
#Send e-mail with high priority
$MailMessage.Priority = “High”
$mailmessage.Body = ...
|
Logging
Using Write-Host and Try/Catch blocks I can log every step of the script and any running errors. The log will be saved at the same $DirPath location which I set at C:\Automation\PasswordExpiry. In this folder it will store the log as well as the credential object. Here is my log file:
If you run the script manually the shell will also display its progress to you.
Credential
The script will store your Office 365 / authentication credentials by using the Export-CLIXML cmdlet. This allows us to store away the entire System.Management.Automation.PSCredential Object as an XML file. the Export-CliXml
cmdlet encrypts credential objects using the Windows standard Data Protection API. It’s always best to store credentials of a user with no administrative roles in your Office 365 / Mail environment.
The Script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
<#
.NOTES
===========================================================================
Created on: 3/27/2018 7:37 PM
Created by: Bradley Wyatt
Version: 1.0.0
Notes:
The variables you should change are the SMTP Host, From Email and Expireindays. I suggest keeping the DirPath
SMTPHOST: The smtp host it will use to send mail
FromEmail: Who the script will send the e-mail from
ExpireInDays: Amount of days before a password is set to expire it will look for, in my example I have 7. Any password that will expire in 7 days or less will start sending an email notification
Run the script manually first as it will ask for credentials to send email and then safely store them for future use.
===========================================================================
.DESCRIPTION
This script will send an e-mail notification to users where their password is set to expire soon. It includes step by step directions for them to
change it on their own.
It will look for the users e-mail address in the emailaddress attribute and if it’s empty it will use the proxyaddress attribute as a fail back.
The script will log each run at $DirPath\log.txt
#>
#VARs
#SMTP Host
$SMTPHost = “smtp.office365.com”
#Who is the e-mail from
#Password expiry days
$expireindays = 7
#Program File Path
$DirPath = “C:\Automation\PasswordExpiry”
$Date = Get-Date
#Check if program dir is present
$DirPathCheck = Test-Path -Path $DirPath
If (!($DirPathCheck))
{
Try
{
#If not present then create the dir
New-Item -ItemType Directory $DirPath -Force
}
Catch
{
$_ | Out-File ($DirPath + “\” + “Log.txt”) -Append
}
}
#CredObj path
$CredObj = ($DirPath + “\” + “EmailExpiry.cred”)
#Check if CredObj is present
$CredObjCheck = Test-Path -Path $CredObj
If (!($CredObjCheck))
{
“$Date – INFO: creating cred object” | Out-File ($DirPath + “\” + “Log.txt”) -Append
#If not present get office 365 cred to save and store
$Credential = Get-Credential -Message “Please enter your Office 365 credential that you will use to send e-mail from $FromEmail. If you are not using the account $FromEmail make sure this account has ‘Send As’ rights on $FromEmail.”
#Export cred obj
$Credential | Export-CliXml -Path $CredObj
}
Write-Host “Importing Cred object…” -ForegroundColor Yellow
$Cred = (Import-CliXml -Path $CredObj)
# Get Users From AD who are Enabled, Passwords Expire and are Not Currently Expired
“$Date – INFO: Importing AD Module” | Out-File ($DirPath + “\” + “Log.txt”) -Append
Import-Module ActiveDirectory
“$Date – INFO: Getting users” | Out-File ($DirPath + “\” + “Log.txt”) -Append
$users = Get-Aduser -properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress -filter { (Enabled -eq ‘True’) -and (PasswordNeverExpires -eq ‘False’) } | Where-Object { $_.PasswordExpired -eq $False }
$maxPasswordAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge
# Process Each User for Password Expiry
foreach ($user in $users)
{
$Name = (Get-ADUser $user | ForEach-Object { $_.Name })
Write-Host “Working on $Name…” -ForegroundColor White
Write-Host “Getting e-mail address for $Name…” -ForegroundColor Yellow
$emailaddress = $user.emailaddress
If (!($emailaddress))
{
Write-Host “$Name has no E-Mail address listed, looking at their proxyaddresses attribute…” -ForegroundColor Red
Try
{
$emailaddress = (Get-ADUser $user -Properties proxyaddresses | Select-Object -ExpandProperty proxyaddresses | Where-Object { $_ -cmatch ‘^SMTP’ }).Trim(“SMTP:”)
}
Catch
{
$_ | Out-File ($DirPath + “\” + “Log.txt”) -Append
}
If (!($emailaddress))
{
Write-Host “$Name has no email addresses to send an e-mail to!” -ForegroundColor Red
#Don’t continue on as we can’t email $Null, but if there is an e-mail found it will email that address
“$Date – WARNING: No email found for $Name” | Out-File ($DirPath + “\” + “Log.txt”) -Append
}
}
#Get Password last set date
$passwordSetDate = (Get-ADUser $user -properties * | ForEach-Object { $_.PasswordLastSet })
#Check for Fine Grained Passwords
$PasswordPol = (Get-ADUserResultantPasswordPolicy $user)
if (($PasswordPol) -ne $null)
{
$maxPasswordAge = ($PasswordPol).MaxPasswordAge
}
$expireson = $passwordsetdate + $maxPasswordAge
$today = (get-date)
#Gets the count on how many days until the password expires and stores it in the $daystoexpire var
$daystoexpire = (New-TimeSpan -Start $today -End $Expireson).Days
If (($daystoexpire -ge “0”) -and ($daystoexpire -lt $expireindays))
{
“$Date – INFO: Sending expiry notice email to $Name” | Out-File ($DirPath + “\” + “Log.txt”) -Append
Write-Host “Sending Password expiry email to $name” -ForegroundColor Yellow
$SmtpClient = new-object system.net.mail.smtpClient
$MailMessage = New-Object system.net.mail.mailmessage
#Who is the e-mail sent from
$mailmessage.From = $FromEmail
#SMTP server to send email
$SmtpClient.Host = $SMTPHost
#SMTP SSL
$SMTPClient.EnableSsl = $true
#SMTP credentials
$SMTPClient.Credentials = $cred
#Send e-mail to the users email
$mailmessage.To.add(“$emailaddress”)
#Email subject
$mailmessage.Subject = “Your password will expire $daystoexpire days”
#Notification email on delivery / failure
$MailMessage.DeliveryNotificationOptions = (“onSuccess”, “onFailure”)
#Send e-mail with high priority
$MailMessage.Priority = “High”
$mailmessage.Body =
“Dear $Name,
Your Domain password will expire in $daystoexpire days. Please change it as soon as possible.
To change your password, follow the method below:
1. On your Windows computer
a. If you are not in the office, logon and connect to VPN.
b. Log onto your computer as usual and make sure you are connected to the internet.
c. Press Ctrl-Alt-Del and click on ““Change Password”“.
d. Fill in your old password and set a new password. See the password requirements below.
e. Press OK to return to your desktop.
The new password must meet the minimum requirements set forth in our corporate policies including:
1. It must be at least 8 characters long.
2. It must contain at least one character from 3 of the 4 following groups of characters:
a. Uppercase letters (A-Z)
b. Lowercase letters (a-z)
c. Numbers (0-9)
d. Symbols (!@#$%^&*…)
3. It cannot match any of your past 24 passwords.
4. It cannot contain characters which match 3 or more consecutive characters of your username.
5. You cannot change your password more often than once in a 24 hour period.
If you have any questions please contact our Support team at [email protected] or call us at 555.555.5555
Thanks,
The Lazy Administrator
555.555.5555″
Write-Host “Sending E-mail to $emailaddress…” -ForegroundColor Green
Try
{
$smtpclient.Send($mailmessage)
}
Catch
{
$_ | Out-File ($DirPath + “\” + “Log.txt”) -Append
}
}
Else
{
“$Date – INFO: Password for $Name not expiring for $daystoexpire days” | Out-File ($DirPath + “\” + “Log.txt”) -Append
Write-Host “Password for $Name does not expire for $daystoexpire days” -ForegroundColor White
}
}
|