Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save meoso/de56bdc68eced50a65d38e99e306ee42 to your computer and use it in GitHub Desktop.
Save meoso/de56bdc68eced50a65d38e99e306ee42 to your computer and use it in GitHub Desktop.
#################################################################################################################
#
# Version 1.4 February 2016
# Robert Pearman (WSSMB MVP)
# TitleRequired.com
# Script to Automated Email Reminders when Users Passwords due to Expire.
#
# Requires: Windows PowerShell Module for Active Directory
#
# For assistance and ideas, visit the TechNet Gallery Q&A Page. http://gallery.technet.microsoft.com/Password-Expiry-Email-177c3e27/view/Discussions#content
# Or Checkout my Youtube Channel - https://www.youtube.com/user/robtitlerequired
#
##################################################################################################################
# Please Configure the following variables....
$smtpServer="mail.server.com"
$expireindays = 21
$from = "Company Administrator <support@mycompany.com>"
$logging = "Enabled" # Set to Disabled to Disable Logging
$logFile = "<log file path>" # ie. c:\mylog.csv
$testing = "Enabled" # Set to Disabled to Email Users
$testRecipient = "testuser@company.com"
#
###################################################################################################################
# Check Logging Settings
if (($logging) -eq "Enabled")
{
# Test Log File Path
$logfilePath = (Test-Path $logFile)
if (($logFilePath) -ne "True")
{
# Create CSV File and Headers
New-Item $logfile -ItemType File
Add-Content $logfile "Date,Name,EmailAddress,DaystoExpire,ExpiresOn,Notified"
}
} # End Logging Check
# System Settings
$textEncoding = [System.Text.Encoding]::UTF8
$date = Get-Date -format ddMMyyyy
# End System Settings
# Get Users From AD who are Enabled, Passwords Expire and are Not Currently Expired
Import-Module ActiveDirectory
$users = get-aduser -filter * -properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress |where {$_.Enabled -eq "True"} | where { $_.PasswordNeverExpires -eq $false } | where { $_.passwordexpired -eq $false }
$DefaultmaxPasswordAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge
# Process Each User for Password Expiry
foreach ($user in $users)
{
$Name = $user.Name
$emailaddress = $user.emailaddress
$passwordSetDate = $user.PasswordLastSet
$PasswordPol = (Get-AduserResultantPasswordPolicy $user)
$sent = "" # Reset Sent Flag
# Check for Fine Grained Password
if (($PasswordPol) -ne $null)
{
$maxPasswordAge = ($PasswordPol).MaxPasswordAge
}
else
{
# No FGP set to Domain Default
$maxPasswordAge = $DefaultmaxPasswordAge
}
$expireson = $passwordsetdate + $maxPasswordAge
$today = (get-date)
$daystoexpire = (New-TimeSpan -Start $today -End $Expireson).Days
# Set Greeting based on Number of Days to Expiry.
# Check Number of Days to Expiry
$messageDays = $daystoexpire
if (($messageDays) -gt "1")
{
$messageDays = "in " + "$daystoexpire" + " days."
}
else
{
$messageDays = "today."
}
# Email Subject Set Here
$subject="Your password will expire $messageDays"
# Email Body Set Here, Note You can use HTML, including Images.
$body ="
Dear $name,
<p> Your Password will expire $messageDays<br>
To change your password on a PC press CTRL ALT Delete and choose Change Password <br>
<p>Thanks, <br>
</P>"
# If Testing Is Enabled - Email Administrator
if (($testing) -eq "Enabled")
{
$emailaddress = $testRecipient
} # End Testing
# If a user has no email address listed
if (($emailaddress) -eq $null)
{
$emailaddress = $testRecipient
}# End No Valid Email
# Send Email Message
if (($daystoexpire -ge "0") -and ($daystoexpire -lt $expireindays))
{
$sent = "Yes"
# If Logging is Enabled Log Details
if (($logging) -eq "Enabled")
{
Add-Content $logfile "$date,$Name,$emailaddress,$daystoExpire,$expireson,$sent"
}
# Send Email Message
Send-Mailmessage -smtpServer $smtpServer -from $from -to $emailaddress -subject $subject -body $body -bodyasHTML -priority High -Encoding $textEncoding
} # End Send Message
else # Log Non Expiring Password
{
$sent = "No"
# If Logging is Enabled Log Details
if (($logging) -eq "Enabled")
{
Add-Content $logfile "$date,$Name,$emailaddress,$daystoExpire,$expireson,$sent"
}
}
} # End User Processing
# End
@MadRegime
Copy link

Hi, great script! Is there a possibility to email the user at 21 days, 14 days, 7 days and the last 3 days?

@meoso
Copy link
Author

meoso commented Jun 30, 2021

modify this line: if (($daystoexpire -ge "0") -and ($daystoexpire -lt $expireindays))
with if (($daystoexpire -ge "0") -and ($daystoexpire -lt $expireindays) and ( ($daystoexpire -eq "21") -or ($daystoexpire -eq "14") -or ($daystoexpire -eq "7") -or ($daystoexpire -le "3" ) )
maybe, or something similar, or maybe just work something out not too complex

@zortmanc
Copy link

zortmanc commented Jul 7, 2021

Can this be set to run against specific OUs?

@meoso
Copy link
Author

meoso commented Jul 7, 2021

Can this be set to run against specific OUs?

Research the cmdlet get-aduser for searchbase options, then modify your script.

@c1thunder
Copy link

How to put cc to the script?

@meoso
Copy link
Author

meoso commented Jul 12, 2021

How to put cc to the script?

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/send-mailmessage?view=powershell-7.1
tells how to use the cmdlet (-cc) . just code another variable and mod the Send-Mailmessage line.
but really i dont understand a use-case. the admins receive a report. why CC someone about another users account?

@Mahi24Suma
Copy link

Mahi24Suma commented Aug 25, 2021

Can we use below script as well for notification alert on password expiry.

##############Variables#################            
$verbose = $true            
$notificationstartday = 14            
$sendermailaddress = "no-reply@contoso.com"            
$SMTPserver = "mail.contoso.com"            
$DN = "DC=contoso,DC=com"            
########################################            
            
##############Function##################            
function PreparePasswordPolicyMail ($ComplexityEnabled,$MaxPasswordAge,$MinPasswordAge,$MinPasswordLength,$PasswordHistoryCount)            
{            
    $verbosemailBody = "Below is a summary of the applied Password Policy settings:`r`n`r`n"            
    $verbosemailBody += "Complexity Enabled = " + $ComplexityEnabled + "`r`n`r`n"            
    $verbosemailBody += "Maximum Password Age = " + $MaxPasswordAge + "`r`n`r`n"            
    $verbosemailBody += "Minimum Password Age = " + $MinPasswordAge + "`r`n`r`n"            
    $verbosemailBody += "Minimum Password Length = " + $MinPasswordLength + "`r`n`r`n"            
    $verbosemailBody += "Remembered Password History = " + $PasswordHistoryCount + "`r`n`r`n"            
    return $verbosemailBody            
}            
            
function SendMail ($SMTPserver,$sendermailaddress,$usermailaddress,$mailBody)            
{            
    $smtpServer = $SMTPserver            
    $msg = new-object Net.Mail.MailMessage            
    $smtp = new-object Net.Mail.SmtpClient($smtpServer)            
    $msg.From = $sendermailaddress            
    $msg.To.Add($usermailaddress)            
    $msg.Subject = "Your password is about to expire"            
    $msg.Body = $mailBody            
    $smtp.Send($msg)            
}            
########################################            
            
##############Main######################            
$domainPolicy = Get-ADDefaultDomainPasswordPolicy            
$passwordexpirydefaultdomainpolicy = $domainPolicy.MaxPasswordAge.Days -ne 0            
            
if($passwordexpirydefaultdomainpolicy)            
{            
    $defaultdomainpolicyMaxPasswordAge = $domainPolicy.MaxPasswordAge.Days            
    if($verbose)            
    {            
        $defaultdomainpolicyverbosemailBody = PreparePasswordPolicyMail $PSOpolicy.ComplexityEnabled $PSOpolicy.MaxPasswordAge.Days $PSOpolicy.MinPasswordAge.Days $PSOpolicy.MinPasswordLength $PSOpolicy.PasswordHistoryCount            
    }            
}            
            
foreach ($user in (Get-ADUser -SearchBase $DN -Filter * -properties mail))            
{            
    $samaccountname = $user.samaccountname            
    $PSO= Get-ADUserResultantPasswordPolicy -Identity $samaccountname            
    if ($PSO -ne $null)            
    {                         
        $PSOpolicy = Get-ADUserResultantPasswordPolicy -Identity $samaccountname            
        $PSOMaxPasswordAge = $PSOpolicy.MaxPasswordAge.days            
        $pwdlastset = [datetime]::FromFileTime((Get-ADUser -LDAPFilter "(&(samaccountname=$samaccountname))" -properties pwdLastSet).pwdLastSet)            
        $expirydate = ($pwdlastset).AddDays($PSOMaxPasswordAge)            
        $delta = ($expirydate - (Get-Date)).Days            
        $comparionresults = (($expirydate - (Get-Date)).Days -le $notificationstartday) -AND ($delta -ge 1)            
        if ($comparionresults)            
        {            
            $mailBody = "Dear " + $user.GivenName + ",`r`n`r`n"            
            $mailBody += "Your password will expire after " + $delta + " days. You will need to change your password to keep using it.`r`n`r`n"            
            if ($verbose)            
            {            
                $mailBody += PreparePasswordPolicyMail $PSOpolicy.ComplexityEnabled $PSOpolicy.MaxPasswordAge.Days $PSOpolicy.MinPasswordAge.Days $PSOpolicy.MinPasswordLength $PSOpolicy.PasswordHistoryCount            
            }            
            $mailBody += "`r`n`r`nYour IT Department"            
            $usermailaddress = $user.mail            
            SendMail $SMTPserver $sendermailaddress $usermailaddress $mailBody            
        }            
    }            
    else            
    {            
        if($passwordexpirydefaultdomainpolicy)            
        {            
            $pwdlastset = [datetime]::FromFileTime((Get-ADUser -LDAPFilter "(&(samaccountname=$samaccountname))" -properties pwdLastSet).pwdLastSet)            
            $expirydate = ($pwdlastset).AddDays($defaultdomainpolicyMaxPasswordAge)            
            $delta = ($expirydate - (Get-Date)).Days            
            $comparionresults = (($expirydate - (Get-Date)).Days -le $notificationstartday) -AND ($delta -ge 1)            
            if ($comparionresults)            
            {            
                $mailBody = "Dear " + $user.GivenName + ",`r`n`r`n"            
                $delta = ($expirydate - (Get-Date)).Days            
                $mailBody += "Your password will expire after " + $delta + " days. You will need to change your password to keep using it.`r`n`r`n"            
                if ($verbose)            
                {            
                    $mailBody += $defaultdomainpolicyverbosemailBody            
                }            
                $mailBody += "`r`n`r`nYour IT Department"            
                $usermailaddress = $user.mail            
                SendMail $SMTPserver $sendermailaddress $usermailaddress $mailBody            
            }            
            
        }            
    }            
}

I am getting below error when i run this after making change in my WIn10 systems where i am doing testing before deploying for all user.

Get-ADDefaultDomainPasswordPolicy : The term 'Get-ADDefaultDomainPasswordPolicy' is not recognized as the name of a cmdlet, 
function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is 
correct and try again.
At C:\passnot.ps1:35 char:17
+ $domainPolicy = Get-ADDefaultDomainPasswordPolicy
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Get-ADDefaultDomainPasswordPolicy:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException
 
Get-ADUser : The term 'Get-ADUser' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the 
spelling of the name, or if a path was included, verify that the path is correct and try again.
At C:\passnot.ps1:47 char:20
+ foreach ($user in (Get-ADUser -SearchBase      -Filter * -properties  ...
+                    ~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Get-ADUser:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

What changes i need to do in this script if want to test for single user.

Thanks

@Mahi24Suma
Copy link

Mahi24Suma commented Aug 31, 2021

@ALL ,

Thanks,

I used the script but getting below error

#####################################################################

PS C:\Users\XXXXX> C:\Passnotification.ps1
Send-Mailmessage : Error in processing. The server response was: 5.7.3 STARTTLS is required to send mail [PN2PR01CA0019.INDPRD01.PROD.OUTLOOK.COM]
At C:\Passnotification.ps1:122 char:9
+ Send-Mailmessage -smtpServer $smtpServer -from $from -to $ema ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.Mail.SmtpClient:SmtpClient) [Send-MailMessage], SmtpException
+ FullyQualifiedErrorId : SmtpException,Microsoft.PowerShell.Commands.SendMailMessage

##########################################################################################

So as suggested in comments added "-UseSsl " in the script
command added:

####################################################################################

 # Send Email Message
Send-Mailmessage -smtpServer $smtpServer -from $from -to $emailaddress -subject $subject -body $body -bodyasHTML -priority High -Encoding $textEncoding -UseSsl"

After adding  "UseSsl" getting below error :

"PS C:\Users\XXXX> C:\Passnotification.ps1
Send-Mailmessage : The SMTP server requires a secure connection or the client was not authenticated. The server response was: 5.7.57 Client not authenticated to send mail.
[PN1PR01CA0079.INDPRD01.PROD.OUTLOOK.COM]
At C:\Passnotification.ps1:122 char:9
+ Send-Mailmessage -smtpServer $smtpServer -from $from -to $ema ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.Mail.SmtpClient:SmtpClient) [Send-MailMessage], SmtpException
+ FullyQualifiedErrorId : SmtpException,Microsoft.PowerShell.Commands.SendMailMessage

##########################################################################

requesting you to please help us and suggest the solution for this.

NOTE: I am testing this from my office system( Win10) on Windows Powershell ISE

Thanks and Best Regards.

@ccedrick
Copy link

@ALL

I wanted to test this script, it would really a big help for me. may I know if required to provision a Mail Server for the email notification?

Thanks and Best Regards!

@MadRegime
Copy link

@ALL

I wanted to test this script, it would really a big help for me. may I know if required to provision a Mail Server for the email notification?

Thanks and Best Regards!

Yes you do or a SMTP service

@MadRegime
Copy link

MadRegime commented Sep 10, 2021

@ALL ,

Thanks,

I used the script but getting below error

[....redacted.....]

Thanks and Best Regards.

Maybe you are not authorized to send mail via that mailsever

@CalebMeyer45
Copy link

@ALL

How can this code be adjusted to just set this up for one specific user in AD rather than every user?

@meoso
Copy link
Author

meoso commented Sep 15, 2021

@ALL

How can this code be adjusted to just set this up for one specific user in AD rather than every user?

weird request, but you change the filter specs for get-aduser
look at the line with $users = get-aduser -filter * [...] google cmdlet get-aduser

@meoso
Copy link
Author

meoso commented Sep 15, 2021

Can we use below script as well for notification alert on password expiry.

Sorry i was not available to reply for many days. Since this is a completely different script you've posted, you should probably ask the source. It's not really polite to ask about a completely different script, although it is certainly similar.
That said, Get-ADDefaultDomainPasswordPolicy and Get-ADUser are not working because you need to import-module activedirectory.

@Belgithup
Copy link

I hope someone can help. I ran a test after modifying the script on windows server 2019 and got - error: could not send email to me@wlfs.com via smtp.office365.com. I went into the sender's account and unchecked Authenticated SMTP to see if helps but did not.

@meoso
Copy link
Author

meoso commented Mar 3, 2022

I hope someone can help. I ran a test after modifying the script on windows server 2019 and got - error: could not send email to me@wlfs.com via smtp.office365.com. I went into the sender's account and unchecked Authenticated SMTP to see if helps but did not.

i would suggest experimenting with simple Send-Mailmessage test-code using PowerShell ISE until you find a working result that you could integrate into the script. i suspect authentication is your issue, which is outside of the scope of this gist. here are some resources for Send-Mailmessage :

@StrongDoBongSoon
Copy link

StrongDoBongSoon commented Sep 14, 2022

Two questions: 1) How do I exclude certain email addresses or groups? 2)How do I stop the email from sending 1 day after the expiration date so it doesn't keep sending an email intermittently? Thank you.

This script is just a copy of Robert Pearman's original script. i prefer not to offer "support" for someone else's work.
However, i do have a highly modified version here: https://gist.github.com/meoso/3488ef8e9c77d2beccfd921f991faa64 which does account for question #2.
As far question #1, i would presume you could resolve this in a number of ways including modifying the original $user = get-aduser -Filter ... command , or maybe filtering $users by piping to a where-object filtering by group membership or by an OU. but this is something for you as the end-user to research how.

@Odakolo
Copy link

Odakolo commented Feb 28, 2023

Hi, How can I modify this code to send out notification to a security group?

@meoso
Copy link
Author

meoso commented Feb 28, 2023

Hi, How can I modify this code to send out notification to a security group?

maybe useful: https://activedirectoryfaq.com/2021/02/sending-e-mail-to-members-of-an-ad-group/

@Odakolo
Copy link

Odakolo commented Feb 28, 2023

Hi, How can I modify this code to send out notification to a security group?

maybe useful: https://activedirectoryfaq.com/2021/02/sending-e-mail-to-members-of-an-ad-group/

Thanks meoso, I know I have to edit this part of the code
Import-Module ActiveDirectory
**$users = get-aduser -filter *** -properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress |where {$.Enabled -eq "True"} | where { $.PasswordNeverExpires -eq $false } | where { $_.passwordexpired -eq $false }

But I am not sure of how to.
I have tried other things like "Get-ADGroupMember".
I want the new code to look in a particular security group and email the users in that security at the times I want them to be notified.

@meoso
Copy link
Author

meoso commented Feb 28, 2023

Hi, How can I modify this code to send out notification to a security group?

maybe useful: activedirectoryfaq.com/2021/02/sending-e-mail-to-members-of-an-ad-group

Thanks meoso, I know I have to edit this part of the code Import-Module ActiveDirectory **$users = get-aduser -filter *** -properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress |where {$.Enabled -eq "True"} | where { $.PasswordNeverExpires -eq $false } | where { $_.passwordexpired -eq $false }

But I am not sure of how to. I have tried other things like "Get-ADGroupMember". I want the new code to look in a particular security group and email the users in that security at the times I want them to be notified.

Try some variation of (Get-ADGroup -Identity $GroupName -properties members).Members | Get-ADUser,
where this format of (Get-ADGroup -Identity $GroupName -properties members).Members is a workaround for a 5000 user-count limitation existing in Get-ADGroupMember -Identity $GroupName

@Odakolo
Copy link

Odakolo commented Feb 28, 2023

**Try some variation of [...]

I tried it but it didnt work.

@meoso
Copy link
Author

meoso commented Feb 28, 2023

I tried it but it didnt work.

so

$users = (Get-ADGroup -Identity $GroupName -properties members).Members | get-aduser -filter * -properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress |where {$_.Enabled -eq "True"} | where { $_.PasswordNeverExpires -eq $false } | where { $_.passwordexpired -eq $false }

where $GroupName is set for your group, still fails?

EDIT: do not use -filter *

@Odakolo
Copy link

Odakolo commented Feb 28, 2023

$users = (Get-ADGroup -Identity $GroupName -properties members).Members | get-aduser -filter * -properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress |where {$_.Enabled -eq "True"} | where { $_.PasswordNeverExpires -eq $false } | where { $_.passwordexpired -eq $false }

Thanks, my bad. I missed something. After fixing it, it runs but it gets notification for everyone not the people in the specific security group I specified.

@meoso
Copy link
Author

meoso commented Mar 1, 2023

[...]] it runs but it gets notification for everyone not the people in the specific security group I specified.

try without the -filter *. This is an interesting use-case. maybe i should do some coding/testing myself.

@Odakolo
Copy link

Odakolo commented Mar 1, 2023

try without the -filter *. This is an interesting use-case. maybe i should do some coding/testing myself.

Thanks! I will try that and let you know.

@meoso
Copy link
Author

meoso commented Mar 1, 2023

@Odakolo , i just tested without -filter *; It works.

$users = (Get-ADGroup -Identity $GroupName -properties members).Members | get-aduser -properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress |where {$_.Enabled -eq "True"} | where { $_.PasswordNeverExpires -eq $false } | where { $_.passwordexpired -eq $false }

image

where my group had 4 users. 1 of which with a soon to expire password.

seems like there would be a way to simplify this as well, rather than piping, but i'm happy enough with that line.

@Odakolo
Copy link

Odakolo commented Mar 2, 2023

@Odakolo , i just tested without -filter *; It works.

THANKS! it works!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment