首页 > 解决方案 > 在密码即将到期时向 AD 用户发送电子邮件

问题描述

我一直在研究一个 PowerShell 脚本,该脚本会在 AD 用户的密码即将到期时向其发送自动电子邮件。我的脚本中有一个 ForEach 语句,但其中没有任何实际运行。我已经对其进行了设置,以便将所有活动记录在一个 .txt 文件中,这样我就可以看到每个步骤的执行时间。它在 Windows 2016 Essentials 上运行。

下面的脚本:

# VAR
$SMTPHost = "smtp.office365.com"
$FromEmail = "***"
$expireindays = 3
$Date = Get-Date




# Set DIR
$DirPath = "C:\TEMP"


# Check is DIR is present
$DirPathCheck = Test-Path -Path $DirPath
if (!($DirPathCheck)) {
    try {
        #Create DIR if not present
        New-Item -ItemType Directory $DirPath -Force
    }
    catch {
        $_ | Out-File ($DirPath + "\" + "Log.txt") -Append     
    }
}




# CredObj
$CredObj = ($DirPath + "\" + "EmailExpiry.cred")
# Check if CredObj is Present
$CredObjCheck = Test-Path -Path $CredObj
If (!($CredObjCheck))
{
    "$Date - INFO: creating cred object" | Out-File ($DirPath + "\" + "Log.txt") -Append
    #If not present get O365 cred and store
    $Credential = Get-Credential -Message "Please enter your Office 365 credentials."
    #Export CredObj
    $Credential | Export-Clixml -Path $CredObj
}

Write-Host "INFO | Importing Cred Object" -ForegroundColor Yellow
$Cred = (Import-Clixml -Path $CredObj)




"$Date - INFO: Importing AD Module" | Out-File ($DirPath + "\" + "Log.txt") -Append
Import-Module ActiveDirectory
"$Date - INFO: Getting Users" | Out-File ($DirPath + "\" + "Log.txt") -Append
Write-Host "INFO | Getting Users" -ForegroundColor Yellow
$users = Get-ADUser -properties Name, PasswordExpired, PasswordLastSet, EmailAddress -filter { (enabled -eq 'True') } | Where-Object { $_.PasswordExpired -eq 'False'}




# Process Each User for Password Expiry
ForEach ($User in $Users) {
    $Name = (Get-ADUser $user | Get-ADUser -Property Name)
    Write-Host "Working on $Name..." -ForegroundColor White
    Write-Host "Getting email address for $Name..." -ForegroundColor Yellow
    $emailaddress = $user.EmailAddress
    if (!($emailaddress)) {
        Write-Host "$Name has no E-Mail address listed, looking at their proxy address attributes..."
        if (!($emailaddress)) {
            Write-Host "$Name has no email address to send an e-mail to!" -ForegroundColor Red
            "$Date - WARNING: No email found for $Name" | Out-File ($DirPath + "\" + "Log.txt") -Append
        }
    }


    #Get password last set
    $passwordSetDate = (Get-AAUser $user -properties * | ForEach-Object { $_.PasswordLastSet})


    #Get the count on how many days until the password expires and stores it in the $daystoexpire VAR
    $daystoexpire = (New-TimeSpan -Start $today -End $Expireson).Days
    if (($daystoexpire -ge "0") -and ($daystoexpire -lt $expireindays)) {
        "$Date - INFO: Sending expiry notice email to $Name" | Out-File ($DirPath + "\" + "Log.txt")
        Write-Host "Sending Password expiry email to $Name" -ForegroundColor Yellow


        $SmtpClient = New-Object system.net.mail.smtpclient
        $MailMessage = New-Object system.net.mail.mailmessage


        #Email Sender
        $MailMessage.From = $FromEmail

        #SMTP Server
        $SmtpClient.Host = $SMTPHost

        #SMTP SSL
        $SmtpClient.EnableSsl = $true

        #SMTP Credentials
        $SmtpClient.Credentials = $Cred

        #Email Recipients
        $MailMessage.To.add($emailaddress)

        #Subject
        $MailMessage.Subject = "Your password will expire $daystoexpire days"

        #Delivery Success
        $MailMessage.DeliveryNotificationOptions = ("onSuccess", "onFailure")

        #Set Priority
        $MailMessage.Priority = "High"

        #Body
        $MailMessage.Body = "Password will expire, better change it!"


        Write-Host "Sending email to $emailaddress..." -ForegroundColor Green
        try {
            $SmtpClient.Send($MailMessage)
        }
        catch {
            $_ | Out-File ($DirPath + "\" + "Log.txt") -Append
        }
        else {
            "$Date- INFO: Password for $Name not expiring for another $daystoexpire days" | Out-File ($DirPath + "\" + "Log.txt") -Append
            Write-Host "Password for $Name does not expire for $daystoexpire days" -ForegroundColor White
        }
    }
}

输出: 输出截图

标签: powershellforeachscripting

解决方案


我注意到你没有在$Expireson任何地方设置变量,所以你也不会得到正确的值$daystoexpire

事实上,我前段时间做了一个脚本来做同样的事情。我已经对其进行了一些编辑,供您试用。

这使用Send-Mailmessage而不是System.Net.Mail.SmtpClient使事情变得更容易。这样,我们也可以使用Splatting使代码更具可读性。

Import-Module ActiveDirectory

$smtpServer   = "smtp.office365.com"
$expireInDays = 3  # 3 is a bit close... better do something like 7
$from         = "YOUR EMAILADDRESS"
$logPath      = "C:\TEMP"
$logFile      = Join-Path -Path $logPath -ChildPath 'PasswordExpiryLog.txt'
$logDate      = '{0:dd-MM-yyyy}' -f (Get-Date)
$credPath     = Join-Path -Path $logPath -ChildPath 'EmailExpiry.cred'

# create the output path if it does not exist
if (!(Test-Path -Path $logPath -PathType Container)) {
    New-Item -Path $logPath -ItemType Directory | Out-Null
}

# Credentials
If (!(Test-Path -Path $credPath -PathType Leaf)) {
    # write to the log and screen
    $msg = "Creating credentials object"
    Add-Content -Path $logFile -Value "$logDate - INFO: $msg"
    Write-Host $msg -ForegroundColor Yellow

    # If not present get O365 cred and store
    $cred = Get-Credential -Message "Please enter your Office 365 credentials."
    # Export CredObj
    $cred | Export-Clixml -Path $credPath
}
else {
    # write to the log and screen
    $msg = "Importing credentials Object"
    Add-Content -Path $logFile -Value "$logDate - INFO: $msg"
    Write-Host $msg -ForegroundColor Yellow
    $cred = Import-Clixml -Path $credPath
}

# create a template for the emails
$emailTemplate = @" 
<html>
    <head>
    <title>Password Expire Notification</title>
    <meta name="generator" content="PowerShell" />
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <style type="text/css">
    body {
        font-family: verdana, arial, helvetica, sans-serif;
        font-size: 12px;
    }
    </style>
    </head>
    <body> 
    Dear _NAME_,
    <p>Your password will expire in _DAYS_ days.<br /><br />
    To change your password on a Windows pc in the office press CTRL-ALT-Delete and choose <strong>Change a password...</strong><br />
    </p>
    Regards
    </body>  
</html>
"@


# get all users that are enabled and that have a password expiry date
# test it out on dummy user(s) first of course !
$users = Get-ADUser -Filter * -Properties GivenName, Name, SamAccountName, PasswordNeverExpires, PasswordExpired, 
                                            PasswordLastSet, EmailAddress, AccountExpirationDate, accountExpires |
            Where-Object { $_.Enabled -eq $true -and $_.PasswordNeverExpires -eq $false}

# get the domains default max password age
$defaultMaxPasswordAge = (Get-ADDefaultDomainpasswordPolicy).MaxPasswordAge

$mailCount = 0
foreach ($user in $users) {
    if ([string]::IsNullOrWhiteSpace($emailAddress)) {
        # write to the log and screen
        $msg = "$userName has no email address to send an e-mail to!"
        Add-Content -Path $logFile -Value "$logDate - INFO: $msg"
        Write-Host $msg -ForegroundColor Yellow
        # skip this user because we cannot send mail..
        continue
    }

    # just for convenience, store some properties in variables
    $firstName        = $user.GivenName
    $userName         = $user.Name
    $accountName      = $user.SamAccountName
    $emailAddress     = $user.EmailAddress
    $passwordSetDate  = $user.PasswordLastSet
    $passwordPolicy   = (Get-AduserResultantpasswordPolicy $user)

    # check if there is a 'Fine Grained Password' policy for this user
    if ($null -ne $passwordPolicy) {
        $maxPasswordAge = ($passwordPolicy).MaxPasswordAge
    }
    else {
        # no 'Fine Grained Password' policy, so use the default domain password age
        $maxPasswordAge = $defaultMaxPasswordAge
    }

    # prevent errors when the 'User must change password at next logon' checkmark is set
    if (!$passwordSetDate -or !$maxPasswordAge) {
        # write to the log and screen
        $msg = "Please check if the 'User must change password at next logon' checkmark is off for user '$userName'"
        Add-Content -Path $logFile -Value "$logDate - INFO: $msg"
        Write-Host $msg -ForegroundColor Yellow
    }

    # calculate the expiry date for the password
    $passwordExpiresAt = $passwordSetDate + $maxPasswordAge

    # check if the account does not expire before the password does using the accountExpires property.
    # 0 means the expiration date has been removed. 9223372036854775807 means the account never had an expiration date
    if ($user.accountExpires -ne 0 -and $user.accountExpires -ne 9223372036854775807 -and $user.AccountExpirationDate -ne $null) {
        if ($user.AccountExpirationDate -le $passwordExpiresAt) {
            # skip this user if the account expires before the password needs changing
            $msg = "The account for user '$userName' expires before the password needs changing."
            Add-Content -Path $logFile -Value "$logDate - INFO: $msg"
            Write-Host $msg -ForegroundColor Yellow
            continue
        }
    }

    # calculate how many days are left
    $daysToExpire = [int](New-TimeSpan -Start (Get-Date) -End $passwordExpiresAt).Days

    if (($daysToExpire -ge 0) -and ($daysToExpire -lt $expireInDays)) {
        # if there are still days left to change the password, send an email
        # using Send-MailMessage rather than System.Net.Mail.SmtpClient

        $msg = "Sending expiry notice email to '$userName'"
        Add-Content -Path $logFile -Value "$logDate - INFO: $msg"
        Write-Host $msg -ForegroundColor Yellow

        # use splatting for cmdlets that take a lot of parameters
        $params = @{ 
            SmtpServer                 = $smtpServer 
            From                       = $from 
            To                         = $emailAddress 
            Subject                    = "Your password will expire in $daysToExpire days."
            Body                       = $emailTemplate -replace "_NAME_", $firstName -replace "_DAYS_", $daysToExpire
            BodyAsHtml                 = $true
            Encoding                   = [System.Text.Encoding]::UTF8
            Credential                 = $cred
            UseSsl                     = $true 
            Priority                   = 'High'
            DeliveryNotificationOption = 'OnSuccess', 'OnFailure'
            # Port = 587 
        } 

        Send-Mailmessage @params
        # update the counter for the users that were sent an email
        $mailCount++
    }
    elseif ($daysToExpire -le 0) { 
        $msg = "Password for user '$userName' is already expired!"
        Add-Content -Path $logFile -Value "$logDate - WARNING: $msg"
        Write-Host $msg -ForegroundColor Red
    }
}

$msg = "Password expiry notifications have been sent to $mailCount users"
Add-Content -Path $logFile -Value "$logDate - INFO: $msg"
Write-Host $msg -ForegroundColor Green

注意:与往常一样,先创建几个测试用户来尝试一下。您可以使用cmdlet-Filter上的参数Get-ADUser仅获取 testuser 或使用该-SearchBase参数并将您的测试帐户放在一个特殊的 OU 中。


推荐阅读