I'm building a script that checks for user accounts with soon-expiring passwords and sends emails to these people reminding them to change their passwords. Yes, our domain settings already generate those prompts counting down from 14 days. Yes, people ignore these. Yes, I'm trying to save work due to people failing to change their passwords in time while working remotely.
In the script I go through an array of the OUs I am responsible for and get the user accounts with expiring passwords and store these in an array containing their DisplayName, Mail, and calculated values containing DaysLeft and ExpiryDate:
$diff = New-TimeSpan -Start ([datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")) -End (Get-Date)
$OusToSearch = @(
'OU=ORG1,DC=corp,DC=com'
'OU=ORG2,DC=corp,DC=com'
'OU=ORG3,DC=corp,DC=com'
)
ForEach ($OU in $OusToSearch) {
$PwdExpUsersInOU = Get-ADUser -SearchBase $OU -filter {Enabled -eq $True -and PasswordNeverExpires -eq $False -and Mail -like '*'} -Properties "DisplayName", "mail", "msDS-UserPasswordExpiryTimeComputed" | Where-Object {
$diff = New-TimeSpan -End ([datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")) -Start (Get-Date)
$diff.Days -le 14 -and $diff.Days -ge 0
} | Select-Object "DisplayName","Mail",@{Name="DaysLeft";Expression={$diff.Days}},@{Name="ExpiryDate";Expression={[datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")}}
$PwdExpUsers += $PwdExpUsersInOU
}
Thanks to other askers and answerers here for lots of that code.
This part works. At this point I have an array $PwdExpUsers containing the relevant information.
Next I break this array apart into several smaller arrays in order to send tailored email addressed to each person that query returns with messages appropriate to how soon their passwords are expiring:
Note 1: This is currently testing code and filters for and only creates emails for those with passwords expiring tomorrow, as should be obvious from the code below. Once I get the addressing issue figured out I'll expand this to handle the rest of the array.
Note 2: The message content is HTML I've already stored in variables ($MsgTomorrow) earlier in the script. It works fine.
$Outlook = New-Object -ComObject Outlook.Application
$ExpTomorrow = $PwdExpUsers | Where-Object -FilterScript {$PSItem.DaysLeft -eq '0'}
foreach ($Account in $ExpTomorrow) {
$Mail = $Outlook.CreateItem(0)
$Mail.Importance = 2
$Mail.To = $PSItem.Mail
$Mail.Subject = "IMPORTANT: Your computer login password expires tomorrow"
$Mail.HTMLBody = $MsgTomorrow
$Mail.Save()
}
At the end of this I have emails in my Drafts folder with the correct subject and body, but no email address in the To field.
I assume I am failing to grasp how the $PSItem.Mail value is being passed along, and what $Mail.To expects to receive.
A valid solution would also remove the ForEach bit entirely and allow me to create a single email addresses to all members of the array $ExpTomorrow.
$Mail.To = $PSItem.Mail-->$Mail.To = $Account.Mail. BTW why use Outlook for this and not Send-MailMessage ?