PasswordChangeNotification.ps1


Description

@GitHub Copilot - Welcome @BanterBoy, I’m your Copilot and I’m here to help you get things done faster. I can identify issues, explain and even improve code.

You can ask generic questions, but what I’m really good at is helping you with your code. For example:

Generate unit tests for my code Explain the selected code Propose a fix for the bugs in my code If you want to learn more about my capabilities and limitations, check out the Copilot documentation.

I’m powered by AI, so surprises and mistakes are possible. Make sure to verify any generated code or suggestions, and share feedback so that we can learn and improve.

@BanterBoy: - Explain what the script does and why it is useful.

@GitHub Copilot -


Script

<#
.Synopsis
   Script to Automated Email Reminders when Users Passwords due to Expire.
.DESCRIPTION
   Script to Automated Email Reminders when Users Passwords due to Expire.
   Robert Pearman / WindowsServerEssentials.com
   Version 2.9 August 2018
   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

   Alternativley visit my youtube channel, https://www.youtube.com/robtitlerequired

   Videos are available to cover most questions, some videos are based on the earlier version which used static variables, however most of the code
   can still be applied to this version, for example for targeting groups, or email design.

   Please take a look at the existing Q&A as many questions are simply repeating earlier ones, with the same answers!


.EXAMPLE
  PasswordChangeNotification.ps1 -smtpServer mail.domain.com -expireInDays 21 -from "IT Support <[email protected]>" -Logging -LogPath "c:\logFiles" -testing -testRecipient [email protected]

  This example will use mail.domain.com as an smtp server, notify users whose password expires in less than 21 days, send mail from [email protected]
  Logging is enabled, log path is c:\logfiles
  Testing is enabled, and test recipient is [email protected]

.EXAMPLE
  PasswordChangeNotification.ps1 -smtpServer mail.domain.com -expireInDays 21 -from "IT Support <[email protected]>" -reportTo [email protected] -interval 1,2,5,10,15

  This example will use mail.domain.com as an smtp server, notify users whose password expires in less than 21 days, send mail from [email protected]
  Report is enabled, reports sent to [email protected]
  Interval is used, and emails will be sent to people whose password expires in less than 21 days if the script is run, with 15, 10, 5, 2 or 1 days remaining untill password expires.

#>
param(
    # $smtpServer Enter Your SMTP Server Hostname or IP Address
    [Parameter(Mandatory = $True, Position = 0)]
    [ValidateNotNull()]
    [string]$smtpServer,
    # Notify Users if Expiry Less than X Days
    [Parameter(Mandatory = $True, Position = 1)]
    [ValidateNotNull()]
    [int]$expireInDays,
    # From Address, eg "IT Support <[email protected]>"
    [Parameter(Mandatory = $True, Position = 2)]
    [ValidateNotNull()]
    [string]$from,
    [Parameter(Position = 3)]
    [switch]$logging,
    # Log File Path
    [Parameter(Position = 4)]
    [string]$logPath,
    # Testing Enabled
    [Parameter(Position = 5)]
    [switch]$testing,
    # Test Recipient, eg [email protected]
    [Parameter(Position = 6)]
    [string]$testRecipient,
    # Output more detailed status to console
    [Parameter(Position = 7)]
    [switch]$status,
    # Log file recipient
    [Parameter(Position = 8)]
    [string]$reportto,
    # Notification Interval
    [Parameter(Position = 9)]
    [array]$interval
)
###################################################################################################################
# Time / Date Info
$start = [datetime]::Now
$midnight = $start.Date.AddDays(1)
$timeToMidnight = New-TimeSpan -Start $start -end $midnight.Date
$midnight2 = $start.Date.AddDays(2)
$timeToMidnight2 = New-TimeSpan -Start $start -end $midnight2.Date
# System Settings
$textEncoding = [System.Text.Encoding]::UTF8
$today = $start
# End System Settings

# Load AD Module
try {
    Import-Module ActiveDirectory -ErrorAction Stop
}
catch {
    Write-Warning "Unable to load Active Directory PowerShell Module"
}
# Set Output Formatting - Padding characters
$padVal = "20"
Write-Output "Script Loaded"
Write-Output "*** Settings Summary ***"
$smtpServerLabel = "SMTP Server".PadRight($padVal, " ")
$expireInDaysLabel = "Expire in Days".PadRight($padVal, " ")
$fromLabel = "From".PadRight($padVal, " ")
$testLabel = "Testing".PadRight($padVal, " ")
$testRecipientLabel = "Test Recipient".PadRight($padVal, " ")
$logLabel = "Logging".PadRight($padVal, " ")
$logPathLabel = "Log Path".PadRight($padVal, " ")
$reportToLabel = "Report Recipient".PadRight($padVal, " ")
$interValLabel = "Intervals".PadRight($padval, " ")
# Testing Values
if ($testing) {
    if ($null -eq ($testRecipient)) {
        Write-Output "No Test Recipient Specified"
        Exit
    }
}
# Logging Values
if ($logging) {
    if ($null -eq ($logPath)) {
        $logPath = $PSScriptRoot
    }
}
# Output Summary Information
Write-Output "$smtpServerLabel : $smtpServer"
Write-Output "$expireInDaysLabel : $expireInDays"
Write-Output "$fromLabel : $from"
Write-Output "$logLabel : $logging"
Write-Output "$logPathLabel : $logPath"
Write-Output "$testLabel : $testing"
Write-Output "$testRecipientLabel : $testRecipient"
Write-Output "$reportToLabel : $reportto"
Write-Output "$interValLabel : $interval"
Write-Output "*".PadRight(25, "*")
# Get Users From AD who are Enabled, Passwords Expire and are Not Currently Expired
# To target a specific OU - use the -searchBase Parameter -https://docs.microsoft.com/en-us/powershell/module/addsadministration/get-aduser
# You can target specific group members using Get-AdGroupMember, explained here https://www.youtube.com/watch?v=4CX9qMcECVQ
# based on earlier version but method still works here.
$users = get-aduser -filter { (Enabled -eq $true) -and (PasswordNeverExpires -eq $false) } -properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress | Where-Object { $_.passwordexpired -eq $false }
# Count Users
$usersCount = ($users | Measure-Object).Count
Write-Output "Found $usersCount User Objects"
# Collect Domain Password Policy Information
$defaultMaxPasswordAge = (Get-ADDefaultDomainPasswordPolicy -ErrorAction Stop).MaxPasswordAge.Days
Write-Output "Domain Default Password Age: $defaultMaxPasswordAge"
# Collect Users
$colUsers = @()
# Process Each User for Password Expiry
Write-Output "Process User Objects"
foreach ($user in $users) {
    # Store User information
    $Name = $user.Name
    $emailaddress = $user.emailaddress
    $passwordSetDate = $user.PasswordLastSet
    $samAccountName = $user.SamAccountName
    $pwdLastSet = $user.PasswordLastSet
    # Check for Fine Grained Password
    $maxPasswordAge = $defaultMaxPasswordAge
    $PasswordPol = (Get-AduserResultantPasswordPolicy $user)
    if ($null -ne ($PasswordPol)) {
        $maxPasswordAge = ($PasswordPol).MaxPasswordAge.Days
    }
    # Create User Object
    $userObj = New-Object System.Object
    $expireson = $pwdLastSet.AddDays($maxPasswordAge)
    $daysToExpire = New-TimeSpan -Start $today -End $Expireson
    # Round Expiry Date Up or Down
    if (($daysToExpire.Days -eq "0") -and ($daysToExpire.TotalHours -le $timeToMidnight.TotalHours)) {
        $userObj | Add-Member -Type NoteProperty -Name UserMessage -Value "today."
    }
    if (($daysToExpire.Days -eq "0") -and ($daysToExpire.TotalHours -gt $timeToMidnight.TotalHours) -or ($daysToExpire.Days -eq "1") -and ($daysToExpire.TotalHours -le $timeToMidnight2.TotalHours)) {
        $userObj | Add-Member -Type NoteProperty -Name UserMessage -Value "tomorrow."
    }
    if (($daysToExpire.Days -ge "1") -and ($daysToExpire.TotalHours -gt $timeToMidnight2.TotalHours)) {
        $days = $daysToExpire.TotalDays
        $days = [math]::Round($days)
        $userObj | Add-Member -Type NoteProperty -Name UserMessage -Value "in $days days."
    }
    $daysToExpire = [math]::Round($daysToExpire.TotalDays)
    $userObj | Add-Member -Type NoteProperty -Name UserName -Value $samAccountName
    $userObj | Add-Member -Type NoteProperty -Name Name -Value $Name
    $userObj | Add-Member -Type NoteProperty -Name EmailAddress -Value $emailAddress
    $userObj | Add-Member -Type NoteProperty -Name PasswordSet -Value $pwdLastSet
    $userObj | Add-Member -Type NoteProperty -Name DaysToExpire -Value $daysToExpire
    $userObj | Add-Member -Type NoteProperty -Name ExpiresOn -Value $expiresOn
    # Add userObj to colusers array
    $colUsers += $userObj
}
# Count Users
$colUsersCount = ($colUsers | Measure-Object).Count
Write-Output "$colusersCount Users processed"
# Select Users to Notify
$notifyUsers = $colUsers | Where-Object { $_.DaysToExpire -le $expireInDays }
$notifiedUsers = @()
$notifyCount = ($notifyUsers | Measure-Object).Count
Write-Output "$notifyCount Users with expiring passwords within $expireInDays Days"
# Process notifyusers
foreach ($user in $notifyUsers) {
    # Email Address
    $samAccountName = $user.UserName
    $emailAddress = $user.EmailAddress
    # Set Greeting Message
    $name = $user.Name
    $messageDays = $user.UserMessage
    # Subject Setting
    $subject = "Your password will expire $messageDays"
    # Email Body Set Here, Note You can use HTML, including Images.
    # examples here https://youtu.be/iwvQ5tPqgW0
    $body = "
    <font face=""verdana"">
    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> If you are using a MAC you can now change your password via Web Mail. <br>
    Login to <a href=""https://mail.domain.com/owa"">Web Mail</a> click on Options, then Change Password.
    <p> Don't forget to Update the password on your Mobile Devices as well!
    <p>Thanks, <br>
    </P>
    IT Support
    <a href=""mailto:[email protected]""?Subject=Password Expiry Assistance"">[email protected]</a> | 0123 456 78910
    </font>"
    # If Testing Is Enabled - Email Administrator
    if ($testing) {
        $emailaddress = $testRecipient
    } # End Testing
    # If a user has no email address listed
    if ($null -eq ($emailaddress)) {
        $emailaddress = $testRecipient
    }# End No Valid Email
    $samLabel = $samAccountName.PadRight($padVal, " ")
    try {
        # If using interval paramter - follow this section
        if ($interval) {
            $daysToExpire = [int]$user.DaysToExpire
            # check interval array for expiry days
            if (($interval) -Contains ($daysToExpire)) {
                # if using status - output information to console
                if ($status) {
                    Write-Output "Sending Email : $samLabel : $emailAddress"
                }
                # Send message - if you need to use SMTP authentication watch this video https://youtu.be/_-JHzG_LNvw
                Send-Mailmessage -smtpServer $smtpServer -from $from -to $emailaddress -subject $subject -body $body -bodyasHTML -priority High -Encoding $textEncoding -ErrorAction Stop
                $user | Add-Member -MemberType NoteProperty -Name SendMail -Value "OK"
            }
            else {
                # if using status - output information to console
                # No Message sent
                if ($status) {
                    Write-Output "Sending Email : $samLabel : $emailAddress : Skipped - Interval"
                }
                $user | Add-Member -MemberType NoteProperty -Name SendMail -Value "Skipped - Interval"
            }
        }
        else {
            # if not using interval paramter - follow this section
            # if using status - output information to console
            if ($status) {
                Write-Output "Sending Email : $samLabel : $emailAddress"
            }
            Send-Mailmessage -smtpServer $smtpServer -from $from -to $emailaddress -subject $subject -body $body -bodyasHTML -priority High -Encoding $textEncoding -ErrorAction Stop
            $user | Add-Member -MemberType NoteProperty -Name SendMail -Value "OK"
        }
    }
    catch {
        # error section
        $errorMessage = $_.exception.Message
        # if using status - output information to console
        if ($status) {
            $errorMessage
        }
        $user | Add-Member -MemberType NoteProperty -Name SendMail -Value $errorMessage
    }
    $notifiedUsers += $user
}
if ($logging) {
    # Create Log File
    Write-Output "Creating Log File"
    $day = $today.Day
    $month = $today.Month
    $year = $today.Year
    $date = "$day-$month-$year"
    $logFileName = "$date-PasswordLog.csv"
    if (($logPath.EndsWith("\"))) {
        $logPath = $logPath -Replace ".$"
    }
    $logFile = $logPath, $logFileName -join "\"
    Write-Output "Log Output: $logfile"
    $notifiedUsers | Export-CSV $logFile
    if ($reportTo) {
        $reportSubject = "Password Expiry Report"
        $reportBody = "Password Expiry Report Attached"
        try {
            Send-Mailmessage -smtpServer $smtpServer -from $from -to $reportTo -subject $reportSubject -body $reportbody -bodyasHTML -priority High -Encoding $textEncoding -Attachments $logFile -ErrorAction Stop
        }
        catch {
            $errorMessage = $_.Exception.Message
            Write-Output $errorMessage
        }
    }
}
$notifiedUsers | Select-Object UserName, Name, EmailAddress, PasswordSet, DaysToExpire, ExpiresOn | Sort-Object DaystoExpire | Format-Table -autoSize

$stop = [datetime]::Now
$runTime = New-TimeSpan $start $stop
Write-Output "Script Runtime: $runtime"
# End

Back to Top


Download

Please feel free to copy parts of the script or if you would like to download the entire script, simple click the download button. You can download the complete repository in a zip file by clicking the Download link in the menu bar on the left hand side of the page.


Report Issues

You can report an issue or contribute to this site on GitHub. Simply click the button below and add any relevant notes. I will attempt to respond to all issues as soon as possible.

Issue


Back to Top