AD Password expiry notification from technet gallery - powershell

I try edit powershell password email notification from
- https://gallery.technet.microsoft.com/Password-Expiry-Email-177c3e27
or
- https://blogs.technet.microsoft.com/askpfeplat/2015/05/04/how-to-setup-a-password-expiration-notification-email-solution/
I like to send log in html file and I try add 2 new column $sent, $expireson.
I add 2 columns to get-aduser query but results in this columns is bad.
I don't know what can I do to make the code work properly.
My code below:
$smtpServer="mail.company.com"
$expireindays = 52
$from = "Company Administrator <support#mycompany.com>"
$logging = "Enabled" # Set to Disabled to Disable Logging
$logFile = "C:\scripts\test.html" # ie. c:\mylog.csv
$testing = "Enabled" # Set to Disabled to Email Users
$testRecipient = "testuser#company.com"
$filecontent = (Get-Content $logfile | Out-String)
# 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} |
#{N="column1";E= {$expireson}}, #{N="column1";E= {$sent}} |
Convertto-html | out-file $logfile
# 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
#Send-Mailmessage -smtpServer $smtpServer -from $from -to $emailaddress -subject $subject -body $body -bodyasHTML -priority High -Encoding $textEncoding
# End

I fix my issue converting CSV to HTML and add some CSS. Now i have log in html table sending it to email.
$info = Import-Csv $logFile | ConvertTo-Html -head $style
$mailBody =
"
$info
"
Send-Mailmessage -smtpServer $smtpServer -from $from -to $emailaddress -subject test -body $mailBody -bodyasHTML -priority High -Encoding $textEncoding

Related

AD password update via Powershell

Too all Powershell Experts: I need some help to figure out why this is working incorrectly. What I mean incorrectly, the accounts in CSV password get updated with a random password and email is sent to the users but i don't under why they are getting the same password, and everyaccount in the AD is addressed. See email output:
Firstuser email:
Hello Guest DefaultAccount user1, user2, user3, user4 test John Doe,
Your AD Account user1, password has been updated to e,wQlsGfBE;J'4
Second useremail
Hello Guest DefaultAccount user1, user2, user3, user4 test John Doe,
Your AD Account jdoe, password has been updated to e,wQlsGfBE;J'4
what I'm trying to accomplish are as follows:
List item
Import one column cvs with just "samAccountName"
reset password of the accounts listed in the csv with a random generated 20 character long password
Email the AD account associated email address with the new password. (I'm doing this because their email address if different from their AD Account), so they are able to receive their email.
Import-Module ActiveDirectory
# Import AD user objects and store in users variables
$users = Get-ADUser -Filter * -Properties Name, EmailAddress,SamAccountName | Select SamAccountName,Name,EmailAddress
$Name = $users.Name
$SamAccountName = $users.SamAccountName
$EmailAddress = $users.EmailAddress
#Date
$date = (Get-Date -Format F)
#Generate a random password and store in newpwd
$newpwd = -join (33..126|%{[char]$_}|Get-Random -Count 20)
$newPassword = ConvertTo-SecureString -AsPlainText "$newpwd" -Force
# Import users from CSV
Import-Csv "C:\Userlist.csv" | ForEach-Object {
$samAccountName= $_."samAccountName"
#Proceed with password reset
Set-ADAccountPassword -Identity $samAccountName -NewPassword $newPassword -Reset
Set-AdUser -Identity $samAccountName -ChangePasswordAtLogon $true
#Server configuration
$smtpServer =""
$SmtpPort = ""
# sending option
$from = "it-operation#example.com"
$to = $EmailAddress
$subject ="AD Password updated - " + $date
$priority = "Normal"
$from = ""
$body = #"
Hello $name, <br>
Your AD Account $samAccountName, password has been updated to $newpwd
"#
try {
# Send the report email
Send-MailMessage -To $to -Subject $subject -BodyAsHtml -body $body -SmtpServer $smtpServer -Port $SmtpPort -From $From -Priority $priority
}
catch{
write-warning "error in sending. $_"
} # End User Processing
}
The Account password is getting updated, but just can't send the emai
When you do $Name = $users.Name, the $Name variable gets an array containing ALL usernames in the CSV.
Remove that line and instead set the variable inside your ForEach-Object loop:
# Import users from CSV
$UserList = Import-Csv "C:\Userlist.csv" | ForEach-Object {
$samAccountName= $_.samAccountName
$name = $_.Name
# then the rest of your code
}
Note:
SmtpPort takes an integer, not a string
From cannot be an empty string
P.S. Try to do some proper code indentation, so it is much easier to see where a code block (like ForEach-Object) starts and ends)
Your code revised:
Import-Module ActiveDirectory
# Import AD user objects and store in users variables
$users = Get-ADUser -Filter * -Properties Name, EmailAddress,SamAccountName | Select SamAccountName,Name,EmailAddress
#Date
$date = (Get-Date -Format F)
# Import users from CSV
Import-Csv "C:\Userlist.csv" | ForEach-Object {
# read this from the CSV
$samAccountName= $_.samAccountName
# get the user object from the $users array
$user = $users | Where-Object { $_.SamAccountName -eq $samAccountName }
$name = $user.Name
$emailAddress = $user.EmailAddress
#Generate a random password and store in newpwd
$newpwd = -join (33..126|%{[char]$_}|Get-Random -Count 14)
$newPassword = ConvertTo-SecureString -AsPlainText "$newpwd" -Force
$body = #"
Hello $name, <br>
Your AD Account $samAccountName, password has been updated to $newpwd
"#
#Proceed with password reset
Set-ADAccountPassword -Identity $samAccountName -NewPassword $newPassword -Reset
Set-AdUser -Identity $samAccountName -ChangePasswordAtLogon $true
#Server configuration
$mailParams = #{
SmtpServer = "YourSMTPServer"
Port = 25 # <-- an integer value
# sending option
From = "it-operation#example.com"
To = $user.EmailAddress
Subject ="AD Password updated - " + $date
Priority = "Normal"
Body = $body
BodyAsHtml = $true
}
try {
# Send the report email
Send-MailMessage #mailParams -ErrorAction Stop
}
catch{
write-warning "error in sending. $_"
} # End User Processing
}
Don't accept my answer. I'm just building off what #Theo did. (I'd love an up-vote though)
Import-Module ActiveDirectory
# Import AD user objects and store in users variables
$Users = #{}
Get-ADUser -Filter * -Properties Name,EmailAddress,SamAccountName | Select-Object SamAccountName,Name,EmailAddress |
ForEach-Object{ $Users.Add( $_.samAccountName, $_ ) }
#Date
$Date = (Get-Date -Format F)
# Generate a random password and store in newPassword
$newPassword = -join ( 33..126 | ForEach-Object{ [char]$_ }| Get-Random -Count 20 )
$newPassword = ConvertTo-SecureString -AsPlainText "$newPassword" -Force
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# Move to the loop if you want each user's pwd to be different!!!
# Put this here so you can indent the loop easier...
$BaseBody =
#"
Hello %NAME%, <br>
Your AD Account %SAMACCOUNTNAME%, password has been updated to %NEWPASSWORD%
"#
# Server configuration & recipien configurations...:
$MailParams = #{
$smtpServer = "YorServer.example.com" #MAKE SURE YOU CHANGE THIS
$SmtpPort = 25
$from = "it-operation#example.com"
$To = ""
$subject ="AD Password updated - " + $date
$Body = ""
}
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# Removed -Priority if it's alwasy going to be normal you don't need it.
# Import users from CSV
Import-Csv "C:\Userlist.csv" |
ForEach-Object {
$samAccountName = $_."samAccountName"
# Proceed with password reset
Set-ADAccountPassword -Identity $samAccountName -NewPassword $newPassword -Reset
Set-ADUser -Identity $samAccountName -ChangePasswordAtLogon $true
# Get the Name & Email address from the $Users Hash...:
$EmailAddress = $Users[$samAccountName].EmailAddress
$Name = $Users[$samAccountName].Name
# Reset the To & Body values
$MailParams['To'] = $EmailAddress
$MailParams['Body'] = $BaseBody.Replace('%NAME%', $Name).Replace('%SAMACCOUNTNAME%', $samAccountName).Replace('%NEWPASSWORD%', $newPassword)
try { # Send the report email
Send-MailMessage #MailParams -ErrorAction Stop
}
catch{
write-warning "Error in sending : $_"
}
} # End User Processing
Used a hash table to store the AD user objects so you don't have to re-execute a Where{} on every loop iteration. Depending on the number of users this could be quite a bit faster. When relating data between 2 sources I gravitate toward a hash tables.
Moved the $Body var out of the loop. Here strings don't like to be indented, by moving it out we don't have to interfere with the loop indentation; improving readability. Then inside the loop we can get the actual body by doing some string replacement.
I also placed the $MailParams parameter hash outside the loop. Then inside the loop I reset the 2 keys that vary. Nitpicking, but I'd rather not declare the hash on every loop iteration either.
$Reused the $newPassword variable rather than have the extra $newpdw variable just laying around.
Expanded the aliases, just because PSScriptAnalyzer complains about it...
Obviously this is untested! But, I hope it's helpful...

Can't send powershell email to managers

I'm trying to create a PS script to email managers about employees with expiring accounts(NOT PASSWORDS) within the next 10 days.
I know this has been discussed already a few times but I was unable to find a suitable solution to this issue.
This is what I have so far:
$MyEmail = "email#domain.com"
$SMTP= "domain.com"
$To = "email#domain.com"
$Subject = "Account expiring"
$Body = "Hi_,
the following account is due to expire, please reply to this email if
you wish to extend the account for a further 3 months
Kind regards,
Tech Team"
$Users = Get-ADUser -filter * -SearchBase "OU=Powershell Test
OU,DC=domain,DC=com" -Properties emailaddress,
Manager,accountexpirationdate
$Users | Select
Name,emailaddress,accountexpirationdate,#{label="Manager";expression= .
{(Get-ADUser $_.Manager -Properties emailaddress).emailaddress}}
Send-MailMessage -To $to -From $MyEmail -Subject $Subject -Body $Body
-SmtpServer $SMTP
I believe I am missing a few pieces.
Can anyone help?
It would be much appreciated.
Thanks
So I've edited my previous comment, because I couldn't stand that is was still not very good:
$MyEmail = "email#domain.com"
$secpasswd = ConvertTo-SecureString "PlainTextPassword" -AsPlainText -Force
$SMTP= "domain.com"
$SMTPPort = "587"
$cred = New-Object System.Management.Automation.PSCredential ($MyEmail, $secpasswd)
$NeverExpires = 9223372036854775807;
$ExpireMin = (Get-Date).AddDays(0);
$ExpireMax = (Get-Date).AddDays(90);
$today = (Get-Date)
try{
$expiringsoon = Get-ADUser -Filter * -SearchBase "OU=Powershell Test OU,DC=domain,DC=com" -Properties accountExpires | Where-Object {$_.accountExpires -ne $NeverExpires -and [datetime]::FromFileTime([int64]::Parse($_.accountExpires)) -lt $ExpireMax -and [datetime]::FromFileTime([int64]::Parse($_.accountExpires)) -gt $ExpireMin }
}catch{
throw $_
}
foreach($user in $expiringsoon){
try{
$userDetails = Get-ADUser $user -Properties AccountExpirationDate,accountExpires,manager
}catch{
Write-Host "Error while searching for user '$user'" -ForegroundColor Red
}
$ExpiryDate = $userDetails.AccountExpirationDate
$DaysLeft = ($ExpiryDate-$today).days
$DateStr = $ExpiryDate.AddDays(-1).ToString("dd/MM/yyyy")
$name = $user.Name
$manager = $userDetails.manager
try{
$managerEmail = (Get-ADUser $manager -Properties emailAddress).emailAddress
if(!$managerEmail){
$managerEmail = $MyEmail
}
}catch{
$managerEmail = $MyEmail
}
# Set Greeting based on Number of Days to Expiry.
# Check Number of Days to Expiry
$messageDays = $DaysLeft
if (($DaysLeft) -gt "1")
{
$messageDays = "in " + "$DaysLeft" + " days"
}
ElseIf (($DaysLeft) -eq "1")
{
$messageDays = "in 1 day"
}
else
{
$messageDays = "today"
}
# Email Subject Set Here
$subject="The account of $name will expire $messageDays "
# Email Body Set Here, Note You can use HTML, including Images.
$body ="
Hi,
<p>the following account '$name' is due to expire, please reply to this email if you wish to extend the account for a further 3 months<br>
<p>Kind regards, <br>
Tech Team
</P>"
#----- Show Details of sent mail -----#
try{
Send-MailMessage -to $managerEmail -from $MyEmail -SmtpServer $SMTP -subject $subject -body $body -bodyasHTML -priority High –Credential $cred -port $SMTPPort -UseSsl
Write-Host "An e-mail has been send to $managerEmail about $name" -ForegroundColor Green
}catch{
Write-Host "Email to $managerEmail for $name failed with error: $($_.exception.message)" -ForegroundColor Red
}
}
Okay, because I couldn't help but wanting to get the script right. Here my present to you. A perfectly working script that sends emails to managers of whom the user expires within 90 days. I also included error handling. Please take this script as a guide to create your own and if you have any questions, feel free.

Powershell search for longfile path names and send the owner of a file a mail to do something about it

I am trying to set up a PowerShell script which reads out a UNC path on Windows and searches for longfiles/paths. After it has found the file it needs to send a mail to the owner of the file.
I have found already a script and tweaked it a bit but it doesn't seems to work yet. The following script can find the long file path now but the mail is not working properly.
$limit = 90
$testpath = "C:\test"
$resultpath = "c:\test"
$admins = "moh#test.com"
$from = "moh#test.com"
$smtpserver = "smtp.office365.com"
Get-ChildItem -Path $testpath -Recurse | ?{$_.fullname.length -gt $limit} |
Select-Object fullname,
#{n="owner";e={
$_.GetAccessControl().GetOwner('System.Security.Principal.NTAccount')}},
#{n="namelength"; e={$_.fullname.length}} |
%{
Out-File -FilePath "$resultpath\Longfiles of $($_.owner -replace "\\","-").txt" -Append -InputObject "$($_.namelength) - $($_.fullname)"
}
Get-ChildItem $resultpath -Filter "longfiles of *" | % {
if($_.name -match "Longfiles\sof\s(.+)\.txt"){
$user = $matches[1] -replace "-","\"
$ntacc = New-Object System.Security.Principal.NTAccount($user)
$sid = $ntacc.Translate([System.Security.Principal.SecurityIdentifier])
$aduser = [ADSI]"LDAP://<SID=$sid>"
$email = $aduser.Properties.mail
if($email) {Send-MailMessage -Attachments $_.fullname -Body "Please change the filenames of the files listed in the attached file to shorter!"
-From $from -SmtpServer $smtpserver -Subject "System notice" -To
$email -cc $admins
}
else {
Send-MailMessage -Attachments $_.fullname -Body "email coudn't be sent to owner" `
-From $from -SmtpServer $smtpserver -Subject "System notice" -To $admins
}
}
else {Write-Host "Some error with file $_"}
}
EDIT: This is what i see, after running the script... it is asking me to fill in the fields, while the fields are already filled in in the script such as (From: moh#test.com to moh#test.com)
[]
You issue was because of line breaks in the middle of a command. In some lines, you had a backtick character which escapes the end of the line. But as you found these are really easy to break and that's why it's best practice to use splatting on commands with many parameters.
I also changed your Select-Object calculated properties into a more readable [pscustomobject] since they are hard to format in a readable way, but this does require PS3+.
$limit = 90
$testpath = "C:\test"
$resultpath = "c:\test"
$admins = "moh#test.com"
$from = "moh#test.com"
$smtpserver = "smtp.office365.com"
Get-ChildItem -Path $testpath -Recurse |
Where-Object {$_.fullname.length -gt $limit} |
ForEach-Object {
[PSCustomObject]#{
'fullname' = $_.fullname
'owner' = $_.GetAccessControl().GetOwner('System.Security.Principal.NTAccount')
'namelength' = $_.fullname.length
}
} |
ForEach-Object {
Out-File -FilePath "$resultpath\Longfiles of $($_.owner -replace "\\","-").txt" -Append -InputObject "$($_.namelength) - $($_.fullname)"
}
Get-ChildItem $resultpath -Filter "longfiles of *" | ForEach-Object {
if ($_.name -match "Longfiles\sof\s(.+)\.txt") {
$user = $matches[1] -replace "-", "\"
$ntacc = New-Object System.Security.Principal.NTAccount($user)
$sid = $ntacc.Translate([System.Security.Principal.SecurityIdentifier])
$aduser = [ADSI]"LDAP://<SID=$sid>"
$email = $aduser.Properties.mail
if ($email) {
$mailparams = #{
'Attachments' = $_.fullname
'Body' = "Please change the filenames of the files listed in the attached file to shorter!"
'From' = $from
'SmtpServer' = $smtpserver
'Subject' = "System notice"
'To' = $email
'cc' = $admins
}
Send-MailMessage #mailparams
} else {
$mailparams = #{
'Attachments' = $_.fullname
'Body' = "email coudn't be sent to owner"
'From' = $from
'SmtpServer' = $smtpserver
'Subject' = "System notice"
'To' = $admins
}
Send-MailMessage #mailparams
}
} else {
Write-Host "Some error with file $_"
}
}
Remove the line breaks or escape them with a backtick: `. Your script must actually look like this:
if ($email) {
Send-MailMessage -Attachments $_.fullname -Body "Please change the filenames of the files listed in the attached file to shorter!"
-From $from -SmtpServer $smtpserver -Subject "System notice" -To
$email -cc $admins
}
else {
Send-MailMessage -Attachments $_.fullname -Body "email coudn't be sent to owner" `
-From $from -SmtpServer $smtpserver -Subject "System notice" -To $admins
}
And Powershell doesn't know that the line beginning with -From is part of Send-MailMessage.

I am trying to create a script in powershell that will send a email based off of results of a command and have no idea where to start

This is all in powershell 4.0
We currently have this script that we manually run once a week that tells us all the users on the network who's password will expire in <=7 days.
#get max password age policy
$maxPwdAge=(Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge.Days
#expiring in 7 days
$7days=(get-date).AddDays(7-$maxPwdAge).ToShortDateString()
Get-ADUser -filter {Enabled -eq $True -and PasswordNeverExpires -eq $False - and PasswordLastSet -gt 0} –Properties * | where {($_.PasswordLastSet).ToShortDateString() -le $7days} | Select-Object -Property "Displayname"
This displays a list of names when run. I then want to take the list of names it generates, add the string "#example.com" to have a string "user.name#example.com"
I thien want to send an email to that email address
$EmailFrom = "noreply#domain.com"
$EmailTo = "destinyemail#domain.com"
The EmailTo needs to change for each person. It could even be emailed to all the users the above outputs in one email.
$EmailSubject = "PasswordExpiring"
$emailbody = " body message "
$SMTPServer = "smtpserver.company.com"
Send-MailMessage -Port 587 -SmtpServer $SMTPServer -From $EmailFrom -To $EmailTo -Attachments $emailattachment -Subject $EmailSubject -Body $emailbody -Bodyashtml;
Assuming that Displayname format is "FirstName LastName" (e.g. "Jon Doe") and email address format is "FirstName.LastName#example.com":
$displayNames = Get-ADUser ...
foreach ($sdisplayName in $displayNames ) {
$Parts = $sdisplayName -split ' '
$EmailTo = $Parts[0] + '.' + $Parts[-1]
Send-MailMessage -To $EmailTo ...
}

PowerShell to send email

I am running a Task Scheduler to execute my PowerShell every 30 minutes.
So
$users = Search-ADAccount -LockedOut -SearchBase
"OU=People,DC=Example,DC=com" -SearchScope Subtree | Select
SamAccountName
This returns a list of users. Now I want to create an emailbody with the users
$EmailBody = ForEach($user in $users) {"`r`n",$user}
Send-MailMessage -To $to -Subject "Locked Accounts" -BodyAsHtml $EmailBody -From $from -Credential $cred -SmtpServer $server -Debug
However my current code will send the email even if NO ONE is locked. Is there anyway I can check to see if the $users has at least 1 person?
(I haven't tested the $EmailBody but would like to know if this is acceptable for BodyAsHtml)
================
Updated Code
if ($users) {
if ($users.count -gt 0) {#even if there are users, this line always is false.
# send the email here
foreach($user in $users)
{
$message = $message + " " + $user + " is locked out" + "`r`n"
Write-Host $user
}
Send-MailMessage -To $to -Subject "Locked Accounts" -BodyAsHtml $message -From $from -Credential $cred -SmtpServer $server -Debug
} }
if ($users -ne $null) # lists can be empty as well, in which case the count fails
{
if ($users.count -gt 0) {
# send the email here
}
}