
Active Directory Password Expiration Notifications
Background
The whole story began with the fact that the time has come for the next IT audit. Serious uncles from Price Waterhouse Coopers came and gave us a lot of instructions and a couple of scripts that had to be run on the domain controller in order to send them the logs. After familiarizing themselves with the texts of the scripts (and you never know what, security is above all), the logs were provided to them. And then it began.
PWC requirements in most cases related to security policies. One of them was to introduce password complexity policy and pasword lifetime. To do this, of course, was quite easy, but soon we encountered the following problem: Windows does not notify the user about the expiration of his password if the connection is via a VPN from an external network. The problem turned out to be quite serious because simply updating the password and unlocking the account of such a user was no longer enough. It was necessary for the laptop to be in the native office network. Considering that some users “live” on eternal business trips, the problem turned out to be very serious. Manually tracking them is still a headache, and not in the admin somehow. Here the idea arose to organize the distribution of automatic notifications. After a short time,a script that remotely tried to perform the necessary actions. I had to finalize it.
Since this was my first experience writing a script under PowerShell, a lot of time was spent (almost a full day).
Script
And here is what I got:
Import-Module ActiveDirectory
#System globalization
#$ci = New-Object System.Globalization.CultureInfo("ru-RU")
#SMTP server name
$smtpServer = "mail.domain.local"
#Creating a Mail object
$msg = new-object Net.Mail.MailMessage
#Creating a Mail object for report
$msgr = new-object Net.Mail.MailMessage
#Creating SMTP server object
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
#E-mail structure
Function EmailStructure($to,$expiryDate,$upn)
{
$msg.IsBodyHtml = $true
$msg.From = "notification@domain.com"
$msg.To.Clear()
$msg.To.Add($to)
$msg.Subject = "Password expiration notice"
$msg.Body =
"This is an automatically generated message from Exchange service.
Please note that the password for your account Domain\$upn will expire on $expiryDate.
Please change your password immediately or at least before this date as you will be unable to access the service without contacting your administrator."
}
Function EmailStructureReport($to)
{
$msgr.IsBodyHtml = $true
$msgr.From = "notification@domain.com"
$msgr.To.Add($to)
$msgr.Subject = "Script running report"
$msgr.Body =
"This is a daily report.
Script has successfully completed its work.
$NotificationCounter users have recieved notifications:
$ListOfAccounts
"
}
#Set the target OU that will be searched for user accounts
$OU = "OU=Organisation,DC=domain,DC=local"
$ADAccounts = Get-ADUser -LDAPFilter "(objectClass=user)" -searchbase $OU -properties PasswordExpired, extensionAttribute15, PasswordNeverExpires, PasswordLastSet, Mail, Enabled | Where-object {$_.Enabled -eq $true -and $_.PasswordNeverExpires -eq $false}
$NotificationCounter = 0
$ListOfAccounts = ""
Foreach ($ADAccount in $ADAccounts)
{
$accountFGPP = Get-ADUserResultantPasswordPolicy $ADAccount
if ($accountFGPP -ne $null)
{
$maxPasswordAgeTimeSpan = $accountFGPP.MaxPasswordAge
}
else
{
$maxPasswordAgeTimeSpan
= (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge
}
#Fill in the user variables
$samAccountName = $ADAccount.samAccountName
$userEmailAddress = $ADAccount.ExtensionAttribute15
$userPrincipalName = $ADAccount.UserPrincipalName
if ($ADAccount.PasswordExpired)
{
Write-host "The password for account $samAccountName has expired!"
}
else
{
$ExpiryDate = $ADAccount.PasswordLastSet + $maxPasswordAgeTimeSpan
$TodaysDate = Get-Date
$DaysToExpire = $ExpiryDate - $TodaysDate
#Calculating DaysToExpireDD to DD format (w/o fractional part and dot)
$DaysToExpireDD = $DaysToExpire.ToString() -Split ("\S{17}$")
Write-host
"The password for account $samAccountName expires on: $ExpiryDate. Days left: $DaysToExpireDD"
if (($DaysToExpire.Days -eq 15) -or
($DaysToExpire.Days -eq 7) -or ($DaysToExpire.Days -le 3))
{
$expiryDate = $expiryDate.ToString("d",$ci)
#Generate e-mail structure and send message
if ($userEmailAddress)
{
EmailStructure $userEmailAddress $expiryDate $samAccountName
$smtp.Send($msg)
Write-Host
"NOTIFICATION - $samAccountName :: e-mail was sent to $userEmailAddress"
$NotificationCounter = $NotificationCounter + 1
$ListOfAccounts =
$ListOfAccounts + $samAccountName + " - $DaysToExpireDD days left. Sent to $userEmailAddress
"
}
}
}
}
Write-Host "SENDING REPORT TO IT DEPARTMENT"
EmailStructureReport("itdepartment@domain.com")
$smtp.Send($msgr)
Save this as text to a file with the extension .ps1.
Launch command
Next - create a file with the extension .cmd and write the script launch parameter into it. It looks like this for me : Both files are on my mail server. You can try your option.
powershell D:\ExchangeTools\pwde.ps1
Create a timeline
Next, we schedule the daily launch of the .cmd file. I have it running every day at 11 am.
Start> All programs> Accesories> System tools> Task scheduler.
Click Action> Create new task.
On the General tab, click the "Change user" button, select the user on whose behalf all this will work. The user must have permission to read the parameters necessary for the script from AD. Set “Run whether user logged on or not” (run regardless of whether the user is logged in or not). When saving the task, the system will ask you to enter the user password.
Next is the Triggers tab. Everything is simple here - configure the launch time as you need.
Actions tab, click "New", select "Start a program" and specify the path to our .cmd file. The last two tabs can not be touched, but if necessary, make changes as you see fit.
Notifications are sent 15, 7 and 3 days or less.
ATTENTION
Exchange server needs to specify its own address as a relay if it is planned to send to addresses outside the domain (duplication to a personal address, for example).
Some will probably ask - “why take the address from ExtensionAttribute if there is a standard field containing the user's e-mail?”. The answer is simple - a copy of each notification is also sent to the IT department, the user may have more than one address and some system accounts do not have mailboxes in principle, but notifications about them are needed to make life easier for administrators themselves. You can enter addresses in ExtensionAttribute15 by going to the Active Directory tree on a domain controller with administrator rights. In the properties of the account will be the tab "Attribute Editor". If there will be several addresses in the field, they should be separated by a comma.