How to send emails based on my data. csv file - powershell

I do a monthly report on inactive accounts and would like to send an email to the managers who are responsible for the service accounts to notify them that the accounts have not been used for more than 30 days based on my data. csv file. When I run my script it only shows one user in the email for each entry in the csv file.
$Users =
#"
Manager;inactiveADAccount
manager_1#domain.com;test_1;test_2;test_3
manager_2#domain.com.ca;test_4
"# | ConvertFrom-Csv -Delimiter ";"
ForEach($Manager in $Users) {
$bodytxt = #"
Hello,
Please verify users below
$($Manager.inactiveADAccount)
"#
$Message = New-Object System.Net.Mail.MailMessage
$Message.From = New-Object System.Net.Mail.MailAddress 'me#domain.com'
$To = $Manager.Manager
$Object = "Inactive Accounts"
$body = $bodytxt
$SMTPServer = 'mail.domain.com'
Send-MailMessage -From $Message.From -To $To -Subject $Object -Body $body -BodyAsHtml -SmtpServer $SMTPServer -Priority High -Encoding UTF8
}

Your CSV file is not constructed correctly.
See the results of your CSV File:
Manager inactiveADAccount
------- -----------------
manager_1#domain.com test_1
manager_2#domain.com.ca test_4
As you can see test_2 and test_3 users are omitted from the csv...
Use one row for each user and manager, this should be:
$Users =
#"
Manager;inactiveADAccount
manager_1#domain.com;test_1
manager_1#domain.com;test_2
manager_1#domain.com;test_3
manager_2#domain.com.ca;test_4
"# | ConvertFrom-Csv -Delimiter ";"
Which now includes all the users:
Manager inactiveADAccount
------- -----------------
manager_1#domain.com test_1
manager_1#domain.com test_2
manager_1#domain.com test_3
manager_2#domain.com.ca test_4
Then Group The results and send the mail:
foreach ($Manager in $Users | group Manager) {
$bodytxt = #"
Hello,
Please verify users below
$($Manager.Group.inactiveADAccount | Out-String)
"#
[...]

Related

a PowerShell script should use the template txt file to read info and dynamically generate the email based on that info

The PowerShell script should use the template and dynamically generate the email
The ps scripts should be able to replace the variables with the values passed during the script execution
Some of the values should be read from the control file and some from the parameters passed while executing the script
Sample Control file:
Email: 1
From: user1
To: several users
Subject: Going to start patching - Start Time
Content: c:\folder\email_template1.txt
Variable1: sysdate
Variable2: hostname
here is my code but does not send the emails to multiple email addresses and multiple lines in body content:
$Subject = "Going to Start Patching " + $patchName + " Start Time:" + $startTime
$Content = "C:\Users\Documents\participants2.txt"
$From = (Select-String -Path $Content -Pattern "From:(.*)").Matches.Groups[1].Value
$To = (Select-String -Path $Content -Pattern "To:(.*)").Matches.Groups[1].Value
$Body = (Select-String -Path $Content -Pattern "Body:(.*)").Matches.Groups[1].Value|
ForEach-Object {$_ -Replace 'patchname', $patchName}
$patchName = [System.NET.DNS]::GetHostByName('')
$startTime = Get-Date -Format "dddd MM/dd/yyyy HH:mm K"
$SMTPServer = ""
Send-MailMessage -From $From -To $To -Cc $Cc -SmtpServer $SMTPServer -Subject $Subject -Body $Body

How to send unique individual emails?

I need to send one unique email to different email addresses exporting
them from a .csv file. I have a code but it just sends the message to all
of the emails in one email.
#Import the file that store username and emails
$data = import-csv "C:.csv"
#Declare email content
$email = $data.Email | select -unique
ForEach ($email in $data)
{
$From = "***#gmail.com"
$To = $data.Email
$Subject = "Test"
$Body = "Test"
$SMTPServer = "smtp.gmail.com"
$SMTPPort = "587"
}
#Sending email
Send-MailMessage -From $From -to $To -Subject $Subject `
-Body $Body -SmtpServer $SMTPServer -port $SMTPPort -UseSsl `
-Credential (Get-Credential -Message "Please input valid credentials")
The code above works but as mentioned it sends just one email to all the email addresses in the file. I need it to send one for each email.
The main problem I see in your code is that you first create an array of unique email addresses from the csv file in a variable $email, but later on you overwrite that same value by using it in the foreach loop.
There, in every iteration, the $email variable will become a complete row from the CSV file which is obviously not what you expect it to be.
Below a slightly adjusted version of your code. Note that I also used Splatting to create a hashtable with all properties for the Send-MailMessage cmdlet, to avoid having to use the easy to overlook backtick.
#Import the file that store username and emails
$data = import-csv "D:\mail.csv"
# Get a unique array of email addresses
$addresses = $data.Email | Select-Object -Unique
# Declare Credentials
$creds = (Get-Credential -Message "Please input valid credentials")
# loop through the email addresses array and send a mail to each of them
foreach ($email in $addresses) {
$splat = #{
From = "***#gmail.com"
To = $email
Subject = "Test"
Body = "Test"
SmtpServer = "smtp.gmail.com"
Port = 587
Credential = $creds
UseSsl = $true
}
#Sending email
Send-MailMessage #splat
}
Note that the -Port parameter is of type Int32, so you should not quote that
Update
As requested in your comment, if you want to use more fields from the CSV file, then the code would change.
Let's assume your CSV looks anything like:
"User","Email","ManagerEmail"
"Tom","t.somebody#yourcompany.com","el.jeffe#yourcompany.com"
"Dick","d.somebody#yourcompany.com","el.jeffe#yourcompany.com"
"Harry","h.somebody#yourcompany.com","di.rector#yourcompany.com"
"Dick","d.somebody#yourcompany.com","el.jeffe#yourcompany.com"
(note, user Dick is duplicated)
Then the following will read the csv, deduplicate it on the Email property and send emails to each user:
# Import the file that store username and emails
# and uniquify the objectson property Email
$data = Import-Csv "D:\mail.csv" | Sort-Object -Property Email -Unique
# Declare Credentials
$creds = (Get-Credential -Message "Please input valid credentials")
# loop through the csv objects array and send a mail to each of them
foreach ($item in $data) {
# every item is an object with properties .User, .Email and .ManagerEmail
$splat = #{
From = "***#gmail.com"
To = $item.Email
Cc = $item.ManagerEmail
Subject = "Hi there {0}" -f $item.User
Body = "Test"
SmtpServer = "smtp.gmail.com"
Port = 587
Credential = $creds
UseSsl = $true
}
#Sending email
Send-MailMessage #splat
}
Hope that helps
As per comments, please find the adjusted script and reasoning.
You were looping through the CSV but only sending the email once. It looks like it would have originally sent the email to ONE person, that being the last in your CSV.
This version will loop through and send an email for each line in the CSV.
#Import the file that store username and emails
$data = import-csv "C:.csv"
#Declare email content
$email = $data.Email | select -unique
# Declare Credentials
$creds = (Get-Credential -Message "Please input valid credentials")
ForEach ($email in $data) {
$From = "***#gmail.com"
$To = $email.Email
$Subject = "Test"
$Body = "Test"
$SMTPServer = "smtp.gmail.com"
$SMTPPort = "587"
#Sending email
Send-MailMessage -From $From -to $To -Subject $Subject `
-Body $Body -SmtpServer $SMTPServer -port $SMTPPort -UseSsl `
-Credential $creds
}
Most important error you made is
$To = $data.Email
where you add all the emails to To: field. Drew's suggestion is almost what you want to follow but with one remark. Your $To= assignment should use $email which represents single object from array, instead of the whole array (which is $data).
$To = $email.Email
Edit: As #Theo suggested in the comments, it's worth mentioning that the line
$email = $data.Email | select -unique
won't work as you use the same variable name later in foreach loop. What I'd suggest is to save unique email addresses to other variable
$uniqueEmails = $data.Email | select -unique
then iterate that one
# Change this
ForEach ($email in $data) {
# To this
ForEach ($email in $uniqueEmails ) {
And of course as you already saved Email propert to $uniqueEmails:
# This line
$To = $email.Email
# Should be changed to
$To = $email

EventID 4740 report using powershell and suppress email delivery if empty

I have a script that gets all the information from the security log and has the event ID 4740. It then creates an html report from that and emails it. What I want to do is to exit the script if there is no data returned from $event.
# Created by Brad Tostenson 1/13/17
# This script will gather all the events with event ID 4740 (Account Locked Out)
# creates a report in HTML and emails it to the System Admins as the body
# of the email.
# Varaible the sets the reports temporary location
$LockedOut= "c:\temp\LockedOut.html"
# Setup date
$Date = Get-Date
# Sets up the report
$HTML=#"
<title>Account locked out Report</title>
<!--mce:0-->
"#
# Setup variables for the information to go under the headers in the report
$Account_Name = #{n='Account Name';e={$_.ReplacementStrings[-1]}}
$Account_domain = #{n='Account Domain';e={$_.ReplacementStrings[-2]}}
$Caller_Computer_Name = #{n='Caller Computer Name';e={$_.ReplacementStrings[-1]}}
# Pulls the information from the log
$event = Get-EventLog -LogName Security -InstanceId 4740 -after $date.AddHours(-24) |
Select TimeGenerated,ReplacementStrings,"Account Name","Account Domain","Caller Computer Name" |
% {
New-Object PSObject -Property #{
"Account Name" = $_.ReplacementStrings[-7]
"Account Domain" = $_.ReplacementStrings[5]
"Caller Computer Name" = $_.ReplacementStrings[1]
Date = $_.TimeGenerated
}
}
$event | ConvertTo-Html -Property "Account Name","Account Domain","Caller Computer Name",Date -head $HTML -body "<H2> The Following User Accounts Were Locked In Active Directory</H2>"|
Out-File $LockedOut -Append
# Takes the report and adds the information to the body of the email and sends it to the System Admins
$MailBody= Get-Content $LockedOut
$MailSubject= "Account Lock Report"
$SmtpClient = New-Object system.net.mail.smtpClient
$SmtpClient.host = "smtp.ourdomain.com"
$MailMessage = New-Object system.net.mail.mailmessage
$MailMessage.from = "LockedOut#ourdomain.com"
$MailMessage.To.add("email#ourdomain.com")
$MailMessage.Subject = $MailSubject
$MailMessage.IsBodyHtml = 1
$MailMessage.Body = $MailBody
$SmtpClient.Send($MailMessage)
del c:\temp\LockedOut.html
I don't see why $event wouldn't be null if Get-EventLog returns no data, and null has a Boolean value of false. That means you can just do this:
if (!$event) { exit }
Or, if it makes more sense, you can do this:
if ($event) {
$event | ConvertTo-Html -Property "Account Name","Account Domain","Caller Computer Name",Date -head $HTML -body "<H2> The Following User Accounts Were Locked In Active Directory</H2>"|
Out-File $LockedOut -Append
# Takes the report and adds the information to the body of the email and sends it to the System Admins
$MailBody= Get-Content $LockedOut
$MailSubject= "Account Lock Report"
$SmtpClient = New-Object system.net.mail.smtpClient
$SmtpClient.host = "smtp.ourdomain.com"
$MailMessage = New-Object system.net.mail.mailmessage
$MailMessage.from = "LockedOut#ourdomain.com"
$MailMessage.To.add("email#ourdomain.com")
$MailMessage.Subject = $MailSubject
$MailMessage.IsBodyHtml = 1
$MailMessage.Body = $MailBody
$SmtpClient.Send($MailMessage)
del c:\temp\LockedOut.html
}
Also, you might want to look at using the Send-MailMessage cmdlet. It's much simpler that what you're doing.
You also don't technically need the $LockedOut file at all, but I'm guessing you're using that in case the network is down or the email fails for some reason.

Converting Hashtable in Powershell

I need some help outputting a a hash table to the body of an email message.
$computer = "adwte998"
$err = "This is the error"
$td = Get-Date
$table = #{
Workstation = $computer
Error = $err
Time = $td
}
send-mailmessage -to "email#email.com" -from "Automated Reboot<no-reply#email.com>" -subject "E-Mail HashTable Test" -body ($table | Out-String) -smtpserver smtpserver.email.com
The Message body looks like it should if i just returned the $table
I would ideally like to convert the table to a format that looks like a CSV with the proper columns and headers but I don't want to email it as an attachment.
Does anyone know how i can go about doing this? I would even considering converting it to HTML so it looks nice in the email.
Using V3:
$computer = "adwte998"
$err = "This is the error"
$td = Get-Date
$table = [ordered]#{
Workstation = $computer
Error = $err
Time = $td
}
[PSCustomObject]$table | ft -auto | out-string
Workstation Error Time
----------- ----- ----
adwte998 This is the error 10/18/2013 1:26:08 PM
for HTML:
[PSCustomObject]$table | convertto-html

How can I concatenate a string with a link that has spaces in powershell

Here is my code that checks when users passwords will expire and emails them if it will expire in less than 14 days. Since the link in the body of the email has spaces in it, the link does not encompass the whole file name.
#Add the Quest PowerShell snapin
Add-PsSnapIn Quest.ActiveRoles.ADManagement -ErrorAction SilentlyContinue
#Clear the placeholder log file
Clear-Content O:\logs\network\passwordchangeemails.txt
#Set Email Variables
$today = Get-Date
$logdate = Get-Date -format yyyyMMdd
$emailFrom = "my.name#mycompany.com"
$body = "Good Morning, `n`n"
$body += "As a courtesy reminder, your Company password will be expiring soon. `n`n"
$body += "If you allow your password to expire, you will be unable to access our network, mail or QAD until you recieve assistance from the Helpdesk. `n`n"
**$body += "To avoid this, please change your network password as soon as possible. (Remote users, please follow the password change procedure for remote users. Click Here -> " + "\\mydfsshare.net\share\helpdesk\guides\Passwords - Change procedure for remote users.pdf ) `n`n"**
$body += "Feel free to contact the Help Desk by phone at 555-555-5555 or by email at Helpdesk#mycompany.com `n"
$body += "Thanks!"
#Get Active Directory Information
Get-QADUser -SizeLimit 0 | Select-Object samAccountName,mail,PasswordStatus |
Where-Object {$_.PasswordStatus -ne "Password never expires" -and $_.PasswordStatus -ne "Expired" -and $_.PasswordStatus -ne "User must change password at next logon." -and $_.mail -ne $null} |
#For each user, get variables
ForEach-Object {
$samaccountname = $_.samAccountName
$mail = $_.mail
$passwordstatus = $_.PasswordStatus
$passwordexpiry = $passwordstatus.Replace("Expires at: ","")
$passwordexpirydate = Get-Date $passwordexpiry
$daystoexpiry = ($passwordexpirydate - $today).Days
#If days to expire is lessthan 14 days, send email to user
if ($daystoexpiry -lt 14) {
$emailTo = "$mail"
$subject = "Company IT Notification: Your Network password will expire in $daystoexpiry day(s). "
Send-MailMessage -To $emailTo -From $emailFrom -Subject $subject -Body $body -SmtpServer 192.168.1.191
Write-Host "Email was sent to $mail on $today"
Add-Content O:\logs\network\passwordchangeemails.txt "Email was sent to $mail on $today"
}
}
$recipients = "my.name#mycompany.com"
#Copy contents of log file to email and send to IT Department
$content = [IO.File]::ReadAllText("O:\logs\network\passwordchangeemails.txt")
Send-MailMessage -To $recipients -From "my.name#mycompany.com" -Subject "Password change log for $today" -Body "This is the log from $today, $content" -SmtpServer 111.111.111.111
Sorry I can't test this right now but if HTML is OK to use, try wrapping the link as follows...
"Text you want the users to see goes here<br>`n
Then set -BodyAsHtml on Send-Mailmessage:
Send-MailMessage -To $emailTo -From $emailFrom -Subject $subject -Body $body -SmtpServer 192.168.1.191 -BodyAsHtml