I am very glad to see there are more than 50,000 views in my blog when starting on counting “total visits” in my blog 7 months ago. Below are the top views from my blog.

Today, I want to share a small IT PowerShell script which is used to send notification to domain users before their password will be expired.
According to MS link (https://learn.microsoft.com/en-us/windows/win32/adschema/a-msds-userpasswordexpirytimecomputed), ms-DS-User-Password-Expiry-Time-Computed attribute is used to contain user password’s expire time in Active Directory. So, using the PowerShell code below to query this value in AD.
Get-ADUser -filter {Enabled -eq $True -and PasswordNeverExpires -eq $False} -Properties DisplayName, EmailAddress, msDS-UserPasswordExpiryTimeComputed

However, the value “133214896056482435” does not have any meaning, so we use [datetime]::FromFileTime($_.”msDS-UserPasswordExpiryTimeComputed”) to convert this value from FromFileTime to DateTime format in PowerShell.
# Query AD user
$Users = Get-ADUser -filter {Enabled -eq $True -and PasswordNeverExpires -eq $False} -Properties DisplayName, EmailAddress, msDS-UserPasswordExpiryTimeComputed | `
Select-Object -Property DisplayName, EmailAddress, @{Name="ExpirationDate";Expression={[datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")}} | `
Sort-Object "ExpirationDate"
$users | sort-object ExpirationDate
The PowerShell outputs a list of Domain users corresponding to “Display Name”, “EmailAddress” and “ExpirationDate”.

Next, check if the password will be expired soon (14 days) and send email notification to domain users.
# Query AD user
$Users = Get-ADUser -filter {Enabled -eq $True -and PasswordNeverExpires -eq $False} -Properties DisplayName, EmailAddress, msDS-UserPasswordExpiryTimeComputed | `
Select-Object -Property DisplayName, EmailAddress, @{Name="ExpirationDate";Expression={[datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")}} | `
Sort-Object "ExpirationDate"
#$users | sort-object ExpirationDate
# Check if the password will be expired soon and send email notification.
$UserList = foreach ($User in $users) {
if ($User.ExpirationDate -le (Get-Date).AddDays(14) -and $User.ExpirationDate -ge (Get-Date))
{
# Create PSCustomObject to save a list of users whom will have a password expired soon
[PSCustomObject]@{
Name = $User.DisplayName
EmailAddress = $User.EmailAddress
ExpiryDate = $User.ExpirationDate
}
}
}
$userlist | sort-object ExpirationDate
PowerShell outputs a list of domain users who have the domain password will be expired soon.

Now, we will write an entire script to send email notification before AD password will be expired.
Get a password from the console, encrypt it and save into the file C:\Shared\admincred.txt
Read-Host -Prompt "Enter your tenant password" -AsSecureString | ConvertFrom-SecureString | Out-File "C:\Shared\admincred.txt"

cat C:\Shared\admincred.txt

Below is a full PowerShell script to send email notification.
# PowerShell script that can send email notifications to domain users before their passwords will be expired.
# Query AD user
$Users = Get-ADUser -filter {Enabled -eq $True -and PasswordNeverExpires -eq $False} -Properties DisplayName, EmailAddress, msDS-UserPasswordExpiryTimeComputed | `
Select-Object -Property DisplayName, EmailAddress, @{Name="ExpirationDate";Expression={[datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")}} | `
Sort-Object "ExpirationDate"
# Check if the password will be expired soon and send email notification.
$UserList = foreach ($User in $users) {
if ($User.ExpirationDate -le (Get-Date).AddDays(14) -and $User.ExpirationDate -ge (Get-Date))
{
# Create PSCustomObject to save a list of users whom will have a password expired soon
[PSCustomObject]@{
Name = $User.DisplayName
EmailAddress = $User.EmailAddress
ExpiryDate = $User.ExpirationDate
}
# Send email
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$SMTP = "smtp-mail.outlook.com"
$From = "admin@outlook.com"
$username = "admin@outlook.com"
#Read-Host -Prompt "Enter your tenant password" -AsSecureString | ConvertFrom-SecureString | Out-File "C:\Shared\admincred.txt"
$Pass = Get-Content "C:\Shared\admincred.txt" | ConvertTo-SecureString
$cred = New-Object System.Management.Automation.PSCredential -argumentlist $username, $Pass
$Subject = "Password Expiration Warning"
$Body = "Dear $($User.DisplayName)," + "`n`nYour password will expire in $User.ExpirationDate. Please change your password as soon as possible." + "`n`nRegards," + "`nIT Department"
$EmailBody = "Hello $($User.DisplayName)," + "`n`n" +
"This is a reminder that your password will expire in $($User.ExpirationDate)." + "`n`n" +
"Please take a moment to change your password to ensure continued access to your account." + "`n`n" +
"Thank you," + "`n" +
"IT Team"
# Try to send the email, catch any exceptions, and log them
Try {
# Send-MailMessage -From $From -To $User.EmailAddress -Subject $Subject -Body $EmailBody -smtpserver $SMTP -usessl -Credential $cred -Port 587
$EmailMessage = @{
From = $From
To = $User.EmailAddress
Subject = $Subject
Body = $EmailBody
SmtpServer = $SMTP
UseSsl = $True
Credential = $cred
Port = 587
}
Send-MailMessage @EmailMessage
# Log success
Add-Content -Path "C:\Shared\Logs\EmailSuccess.log" -Value "Email sent successfully to $($User.DisplayName) at $($User.EmailAddress) on $(Get-Date) "
}
Catch {
# Write the error message to the file EmailFailure.log under C:\Shared\Logs.
Write-Host "Error sending email to $($User.DisplayName) at $($User.EmailAddress): $_"
Add-Content -Path "C:\Shared\Logs\EmailFailure.Log" -Value "Error sending email to $($User.DisplayName) at $($User.EmailAddress) on $(Get-Date) $_"
}
}
}
$Userlist | sort-object ExpirationDate
Run the script.
C:\Users\Administrator\passwordnotify.PS1

Checking the EmailSuccess.log file, the script was successfully sent email notification to AD users before their password will be expired.

The script sent an email notification to AD users.

Finally, we can add the script into the Task Scheduler to check domain users’ password expiration and send email notification.

Trigger tab.

Actions tab.

Actions Settings:
Program: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe
Arguments: -ExecutionPolicy Bypass -File C:\Shared\passwordnotify.PS1
The task was running successfully in Task Scheduler history.


The email notification was sent to Gmail’s account.
