33<#
44. SYNOPSIS
55 Active Directory Password Expiration Notification Script
6-
6+
77. DESCRIPTION
88 Monitors Active Directory users and sends email notifications when passwords are approaching expiration.
99 Supports fine-grained password policies, multilingual messages, and comprehensive logging.
10-
11- . PARAMETER searchBase
12- Distinguished Name of the OU to search for users. If not specified, uses default configured base.
13-
14- . PARAMETER testMode
10+
11+ . PARAMETER SmtpServer
12+ SMTP server address for sending notification emails.
13+
14+ . PARAMETER FromAddress
15+ Email address used as the sender for notifications.
16+
17+ . PARAMETER SearchBase
18+ Distinguished Name of the OU to search for users.
19+
20+ . PARAMETER ExpireInDays
21+ Number of days before expiration to start sending notifications. Default is 10.
22+
23+ . PARAMETER TestMode
1524 Switch to enable test mode. All emails will be sent to the test recipient instead of actual users.
16-
17- . PARAMETER enableLogging
25+
26+ . PARAMETER TestRecipient
27+ Email address to receive all notifications when running in test mode. Default is "admin@company.com".
28+
29+ . PARAMETER EnableLogging
1830 Switch to enable detailed logging to CSV file.
19-
31+
32+ . PARAMETER LogDirectory
33+ Directory path for log files. Default is "C:\Logs\PasswordNotifications".
34+
35+ . PARAMETER Language
36+ Language for email templates. Supported values: EN, PT. Default is EN.
37+
2038. EXAMPLE
21- .\Send-PasswordExpiryNotification.ps1
22- Runs with default configuration
23-
39+ .\Send-PasswordExpiryNotification.ps1 -SmtpServer "mail.company.com" -FromAddress "noreply@company.com" -SearchBase "OU=Users,DC=domain,DC=com"
40+
41+ Runs with specified SMTP server and search base.
42+
2443. EXAMPLE
25- .\Send-PasswordExpiryNotification.ps1 -testMode -enableLogging
26- Runs in test mode with logging enabled
27-
44+ .\Send-PasswordExpiryNotification.ps1 -SmtpServer "mail.company.com" -FromAddress "noreply@company.com" -SearchBase "OU=Users,DC=domain,DC=com" -TestMode -EnableLogging
45+
46+ Runs in test mode with logging enabled.
47+
2848. NOTES
29- File Name : Send-PasswordExpiryNotification.ps1
30- Author : Leonardo Klein Rezende
31- Requires : PowerShell 5.1+, ActiveDirectory Module, SMTP Server Access
32-
49+ File Name : Send-PasswordExpiryNotification.ps1
50+ Author : Leonardo Klein Rezende
51+ Prerequisite : PowerShell 5.1+, ActiveDirectory Module, SMTP Server Access
52+ Creation Date : 2025-09-04
53+
3354. LINK
3455 https://github.com/leonardokr/powershell-scripts
3556#>
3657
58+ [CmdletBinding (SupportsShouldProcess )]
3759param (
38- [string ]$searchBase ,
39- [switch ]$testMode ,
40- [switch ]$enableLogging
41- )
60+ [Parameter (Mandatory = $true )]
61+ [string ]$SmtpServer ,
62+
63+ [Parameter (Mandatory = $true )]
64+ [string ]$FromAddress ,
65+
66+ [Parameter (Mandatory = $true )]
67+ [string ]$SearchBase ,
4268
43- $smtpServer = " x.x.x.x"
44- $expireInDays = 10
45- $fromAddress = " noreply@company.com"
46- $testRecipient = " admin@company.com"
47- $language = " EN" # EN or PT
48- $logDirectory = " C:\Logs\PasswordNotifications"
49- $defaultSearchBase = " OU=Users,DC=contoso,DC=local"
69+ [Parameter (Mandatory = $false )]
70+ [ValidateRange (1 , 90 )]
71+ [int ]$ExpireInDays = 10 ,
5072
51- if ($searchBase ) { $defaultSearchBase = $searchBase }
52- if ($testMode ) { $testModeEnabled = $true } else { $testModeEnabled = $false }
53- if ($enableLogging ) { $loggingEnabled = $true } else { $loggingEnabled = $false }
73+ [Parameter (Mandatory = $false )]
74+ [switch ]$TestMode ,
75+
76+ [Parameter (Mandatory = $false )]
77+ [string ]$TestRecipient = " admin@company.com" ,
78+
79+ [Parameter (Mandatory = $false )]
80+ [switch ]$EnableLogging ,
81+
82+ [Parameter (Mandatory = $false )]
83+ [string ]$LogDirectory = " C:\Logs\PasswordNotifications" ,
84+
85+ [Parameter (Mandatory = $false )]
86+ [ValidateSet (" EN" , " PT" )]
87+ [string ]$Language = " EN"
88+ )
5489
5590$messages = @ {
5691 EN = @ {
@@ -107,7 +142,7 @@ $messages = @{
107142 }
108143}
109144
110- $msg = $messages [$language ]
145+ $msg = $messages [$Language ]
111146function Write-LogMessage {
112147 param (
113148 [string ]$Message ,
@@ -252,15 +287,17 @@ function Send-PasswordExpiryAlert {
252287 param (
253288 [Microsoft.ActiveDirectory.Management.ADUser ]$User ,
254289 [hashtable ]$ExpirationData ,
255- [string ]$RecipientEmail
290+ [string ]$RecipientEmail ,
291+ [string ]$Server ,
292+ [string ]$From
256293 )
257-
294+
258295 try {
259296 $emailBody = Get-PasswordExpiryEmailBody - UserName $User.Name - DaysToExpire $ExpirationData.DaysToExpire
260-
297+
261298 $mailParams = @ {
262- SmtpServer = $smtpServer
263- From = $fromAddress
299+ SmtpServer = $Server
300+ From = $From
264301 To = $RecipientEmail
265302 Subject = $msg.EmailSubject
266303 Body = $emailBody
@@ -309,27 +346,22 @@ function Write-LogEntry {
309346
310347try {
311348 Write-LogMessage " === PASSWORD EXPIRATION NOTIFICATION STARTED ===" - Level " INFO"
312- Write-LogMessage " Search Base: $defaultSearchBase " - Level " INFO"
313- Write-LogMessage " Language: $language " - Level " INFO"
314- Write-LogMessage " Test Mode: $testModeEnabled " - Level " INFO"
315- Write-LogMessage " Logging: $loggingEnabled " - Level " INFO"
316-
349+ Write-LogMessage " Search Base: $SearchBase " - Level " INFO"
350+ Write-LogMessage " Language: $Language " - Level " INFO"
351+ Write-LogMessage " Test Mode: $ ( $TestMode .IsPresent ) " - Level " INFO"
352+ Write-LogMessage " Logging: $ ( $EnableLogging .IsPresent ) " - Level " INFO"
353+
317354 $logFilePath = $null
318- if ($loggingEnabled ) {
355+ if ($EnableLogging ) {
319356 $logFileName = " PasswordNotification_$ ( Get-Date - Format ' yyyy-MM-dd' ) .csv"
320- $logFilePath = Join-Path - Path $logDirectory - ChildPath $logFileName
357+ $logFilePath = Join-Path - Path $LogDirectory - ChildPath $logFileName
321358
322359 if (-not (Initialize-LogFile - LogPath $logFilePath )) {
323360 Write-LogMessage " Continuing without logging..." - Level " WARN"
324- $loggingEnabled = $false
361+ $EnableLogging = $false
325362 }
326363 }
327-
328- if (-not (Get-Module - Name ActiveDirectory - ListAvailable)) {
329- throw " Active Directory module not available. Please install RSAT tools."
330- }
331-
332- Import-Module ActiveDirectory - ErrorAction Stop
364+
333365 Write-LogMessage " Active Directory module loaded successfully" - Level " SUCCESS"
334366
335367 $defaultPasswordPolicy = Get-ADDefaultDomainPasswordPolicy - ErrorAction Stop
@@ -342,9 +374,9 @@ try {
342374 $_.PasswordExpired -eq $false
343375 }
344376
345- Write-LogMessage " Searching for users in: $defaultSearchBase " - Level " INFO"
377+ Write-LogMessage " Searching for users in: $SearchBase " - Level " INFO"
346378
347- $adUsers = Get-ADUser - SearchBase $defaultSearchBase - Filter * - Properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress |
379+ $adUsers = Get-ADUser - SearchBase $SearchBase - Filter * - Properties Name, PasswordNeverExpires, PasswordExpired, PasswordLastSet, EmailAddress |
348380 Where-Object $userFilter
349381
350382 Write-LogMessage " Found $ ( $adUsers.Count ) enabled users to process" - Level " INFO"
@@ -363,22 +395,22 @@ try {
363395 try {
364396 $expirationData = Get-PasswordExpirationData - User $user - DefaultMaxAge $maxPasswordAge
365397
366- if ($expirationData.DaysToExpire -ge 0 -and $expirationData.DaysToExpire -lt $expireInDays ) {
367- $recipientEmail = if ($testModeEnabled ) { $testRecipient } else { $user.EmailAddress }
398+ if ($expirationData.DaysToExpire -ge 0 -and $expirationData.DaysToExpire -lt $ExpireInDays ) {
399+ $recipientEmail = if ($TestMode ) { $TestRecipient } else { $user.EmailAddress }
368400
369401 if ([string ]::IsNullOrWhiteSpace($recipientEmail )) {
370- $recipientEmail = $testRecipient
402+ $recipientEmail = $TestRecipient
371403 $stats.NoEmail ++
372404 Write-LogMessage " User without email address: $ ( $user.Name ) " - Level " WARN"
373405 }
374406
375- $emailSent = Send-PasswordExpiryAlert - User $user - ExpirationData $expirationData - RecipientEmail $recipientEmail
407+ $emailSent = Send-PasswordExpiryAlert - User $user - ExpirationData $expirationData - RecipientEmail $recipientEmail - Server $SmtpServer - From $FromAddress
376408
377409 if ($emailSent ) {
378410 $stats.EmailsSent ++
379411 }
380412
381- if ($loggingEnabled ) {
413+ if ($EnableLogging ) {
382414 Write-LogEntry - LogPath $logFilePath - User $user - ExpirationData $expirationData - Email $recipientEmail - EmailSent $emailSent
383415 }
384416
@@ -401,11 +433,11 @@ try {
401433 Write-LogMessage " Errors: $ ( $stats.Errors ) " - Level " INFO"
402434 Write-LogMessage " Skipped (not expiring): $ ( $stats.Skipped ) " - Level " INFO"
403435
404- if ($testModeEnabled ) {
405- Write-LogMessage " *** TEST MODE ACTIVE - All emails sent to: $testRecipient ***" - Level " WARN"
436+ if ($TestMode ) {
437+ Write-LogMessage " *** TEST MODE ACTIVE - All emails sent to: $TestRecipient ***" - Level " WARN"
406438 }
407439
408- if ($loggingEnabled ) {
440+ if ($EnableLogging ) {
409441 Write-LogMessage " Detailed log saved to: $logFilePath " - Level " INFO"
410442 }
411443
0 commit comments