How can I format my output to PowerShell's script
My output looks like
Hello #{SamAccountName=user1} is locked out
#{SamAccountName=user2} is locked out
My PowerShell code is
if ($users)
{
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
}
Thank you
I would like my output to look like
Hello Administrator,
The following accounts are locked as of 10:31 AM on the 13th of April,
2015.
User1 User2
Thank You Automated System
As you can see in my current output, I can't figure out how to get a new line and it outputs #{SamAccountName=user1} instead of user1
Thank you
Here, try this approach instead. If you use a here-string (which is depicted like so:
$message = #"
So
this
keeps
track of spaces?
"#
You can put any amount of text inside and preserve the spacing of the message, while still having the ease of using this in your script by putting any variables inside that you might need.
As you'll see in the finished answer below, the line spacing is preserved. I'm using the $($variableName) format to allow me to pluck out one value of an object from within a bigger string. If I didn't use that format, the whole object would be listed, including all AD properties, which is not what we want.
if ($users)
{
$message = #"
Hello Administrator,
The following accounts are locked as of $((get-date).DateTime).
$($users | select -expand SamAccountName)
Thank You,
Automated System
"#
Send-MailMessage -To $to -Subject "Locked Accounts" -BodyAsHtml $message -From $from -Credential $cred -SmtpServer $server -Debug
}
The message will look like this:
Hello Administrator,
The following accounts are locked as of Monday, April 13, 2015 10:22:30 AM.
localadmin Guest Stephen RDV GRAPHICS SERVICE Jim SCVMM81221tqYYJ stephen.owen krbtgt _svc_sccm azure_adfs
Thank You,
Automated System
You need to directly access the sAMAccountName property on the $user object:
if ($users)
{
foreach($user in $users)
{
$message = $message + " " + $user.SamAccountName + " is locked out" + "`r`n"
}
Send-MailMessage -To $to -Subject "Locked Accounts" -BodyAsHtml $message -From $from -Credential $cred -SmtpServer $server -Debug
}
If you wanted to make something pretty, save the user names to an array first:
$lockedUsers = #()
foreach($user in $users)
{
$lockedUsers += ,$user.SamAccountName
}
Then you can construct your message like:
$message = #"
Hi Admin,
The following accounts are locked as of $(get-date)
$($lockedUsers -join ", ")
Thank you, Automated System
"#
I figured it out. Since the Body is HTML, I used standard HTML tags when forming the strings
<br> = new line
<strong></strong> = bold
<font color='red'></font> = red font
For the account names, I did
$message = $message + " " + $user.SamAccountName.ToLower() + " is locked out" + "<br>"
Related
I'm creating a PS script to automate email blast to 17k users. Our exchange security baseline is set to only accept 60 requests per minute. Because I'm looping through the email list (CSV) line by line (sleep 1 sec), it took hours for my script to complete. What I'm trying to achieve now is to send the email to 100 users per request. I'm figuring out how to store the emails in an array of 100 & send the mail before going for the next 100. Any suggestion?
$recipients = Get-Content "mailinglist.csv"
foreach($rcpt in $recipients)
{
Write-Host "Attempt sending email to $rcpt ..."
Send-MailMessage -ErrorAction SilentlyContinue -ErrorVariable SendError -From $From -to $rcpt -Subject $Subject -SmtpServer $SMTPServer -port $SMTPPort -UseSsl -Credential $Cred -BodyAsHtml ($Body -f $Subject, $Date, $Venue, $Description, $Image)
$ErrorMessage = $SendError.exception.message
If($ErrorMessage)
{
Write-Host "Failure - $ErrorMessage" -ForegroundColor Red
Start-Sleep -Seconds 60
Send-MailMessage -ErrorAction SilentlyContinue -ErrorVariable SendError -From $From -to $rcpt -Subject $Subject -SmtpServer $SMTPServer -port $SMTPPort -UseSsl -Credential $Cred -BodyAsHtml ($Body -f $Subject, $Date, $Venue, $Description, $Image)
}
ElseIf($SendError.exception.message -eq $null)
{
Write-Host "Email has been sent to $rcpt" -ForegroundColor Green
Start-Sleep -Seconds 1
$n++
}
}
Write-Host "Total sent = $n"
You could use a traditional for loop and access your array elements by index.
$recipients = Get-Content "mailinglist.csv"
$To = <SomeValidEmailAddress>
$LastIndex = $recipients.GetUpperBound(0)
for ($i = 0; $i -le $LastIndex; $i+=100) {
$upperRange = [Math]::Min(($i+99),$LastIndex)
$Params = #{
ErrorAction = 'SilentlyContinue'
ErrorVariable = 'SendError'
Bcc = $recipients[$i..$upperRange]
To = $To
From = $From
Subject = $Subject
SmtpServer = $SMTPServer
Port = $SMTPPort
Credential $Cred
Body = $Body -f $Subject, $Date, $Venue, $Description, $Image
BodyAsHTML = $true
UseSsl = $true
}
"Attempt sending email to $($recipients[$i..$upperRange]) ..." # You may want to alter this to be more readable
Send-MailMessage #Params
# Other code
}
Explanation:
I've opted to use Splatting here for readability and manageability with the $Params hash table. It is entirely optional.
The -bcc parameter of Send-MailMessage supports a string array (string[]). Using this over the -To parameter will preserve privacy of the recipients. You can then easily send an email to multiple recipients provided you pass it an array. However, -To is required for Send-Mailmessage to work. It is recommended to make the email address passed into -To something that can be spammed or has a way of handling these types of emails. I have set up the $To variable for you to provide that email address. If privacy is of no concern whatsoever, -Bcc can just be replaced with -To.
Since $recipients is an array, you can access its elements by index, which supports the range operator ... $recipients[0..99] would be the first 100 items in the list.
$LastIndex stores the last index of the list, which is the value returned by the Array.GetUpperBound(Int32) method with dimension 0. Since the array is one-dimensional, 0 is the only dimension.
$upperRange is the beginning index ($i) plus 99. Should $upperRange ever be larger than $LastIndex, it will be set to $LastIndex. Depending on your PowerShell version, the $i+99 and $LastIndex comparison may not be necessary. Accessing an upperbound range beyond the size of the array, will just return all of the remaining elements of the array without throwing an error. This is likely just for completeness.
I am new to powershell and I am trying to write a script that will email me whenever someone moves or deletes a file or folder from a shared drive. I already set up the task scheduler to execute the script when one of those events happen. The problem that I have is that I execute script from the shell it provides me with all the information regarding the last entry in the event logs. However, when it runs from the task scheduler it does not execute. I believe that it has to do with the body line. Any suggestions or insights will be greatly appreciated.
This is my code:
$events = Get-EventLog -Logname Security -Newest 1
$From = $env:COMPUTERNAME + "#company.com"
$To = "me#company.com"
$Subject = $env:COMPUTERNAME + " " + $env:UserName + " " + $_.eventID
$Body = $events | format-list -property * | out-string
$SMTPServer = "exch"
Send-MailMessage -From $From -to $To -Subject $Subject -Body $Body -SMTPServer $SMTPServer
I am working on a PowerShell script which sends emails multiple times with different subject and body each time.
I am trying to move Send-MailMessage into a function or something that I could use to reduce the code lines.
$Sender = 'jones#example.com'
$text = "<html><body>"
$text += "<p>Welcome</p>"
### A cmdlet that would give recipient email address
$Recipient = (Get-Details -user $user).email
$smtp = "server.example.com"
$subject = "welcome email"
Send-MailMessage -BodyAsHtml $text -from $Sender -SmtpServer $smtp -Priority high -to $Recipient -Subject $subject
Write-Output "executing commands to capture results"
Write-Output ""
### Few Commands executed in this step
Write-Output "Analyzing results"
### Few commands executed in this step
$newtext = "<html><body>"
$newtext += "Congrats, you are selected"
$newsubject = "results email"
Send-MailMessage -BodyAsHtml $newtext -from $Sender -SmtpServer $smtp -Priority high -to $Recipient -Subject $subject
You could create a function like this:
Function Send-Email($text,$subject,$recipient)
{
Send-MailMessage -BodyAsHtml $text -From "jones#example.com"
-SmtpServer "server.example.com" -Priority High -To $recipient -Subject $subject
}
You can call it like:
Send-Email -text "Hello" -subject "Test" -recipient "test#example.com"
You can add or remove arguments depending on what will change though. Assuming the smtp server won't change for example, this isn't needed as a parameter.
I am trying to move Send-MailMessage into a function or something that I could use to reduce the code lines.
Writing a function for one line can be useful if the options are many and never change. However you do have one that changes. There is another PowerShell feature that would work here just as well. Splatting!
$emailParameters = #{
From = $Sender
SmtpServer = $smtp
Priority = "high"
To = $Recipient
Subject = $subject
}
Send-MailMessage -BodyAsHtml $text #emailParameters
# ... other code and stuff
Send-MailMessage -BodyAsHtml $newtext #emailParameters
Now you still only have to make changes in one place and the code is arguably more terse.
Another point is that when you are making multi-line strings all at once, as supposed to building over the course of the script you can always use here strings. You only have two lines but if you code evolves over time it is a good tactic to start early instead of many $object += "string" lines
$text = #"
<html><body>
<p>Welcome</p>
"#
Note that indentation is preserved in the resulting here-string. The "# has to appear on its own line with no leading whitespace. Using double quotes means you can still expand variables as well in there.
I have a script that sends an email when a file exceeds a maximum set size.
All of my files come from a XML-file that concludes the path and max size.
The foreachthat I do to check every size (and to see if it exceeded the max size) looks like:
foreach ($item in $list)
{
if($item.Size -gt $item.TriggerSize -And (Test-Path $item.Path))
{
$body =
"The file " + $item.Name + " is reaching his max size!
<br /><br /> Current size: "+$item.Size.ToString(".00") + " " + $byteSize.Substring(1) +
"<br /> Maximum size: " + $item.MaxSize + " " + $byteSize.Substring(1)
Send-MailMessage -port 587 -From $from -BodyAsHtml -Encoding $enc -To $to -Subject $subject -Body $body -UseSsl -Credential $credentials
}
}
The code is working perfectly, I get an email that looks like:
The file editix2017.exe is reaching his max size!
Current size:84.93 Mb
Maximum size:100 Mb
The problem I have however is that when I work with more than 1 file, it sends an email for every file that exceeded its max limit.
This means that if I have 2 files that exceeded their max size, it sends 2 seperate emails.
How could I encapsulate every one of the exceeded files in a list and send just one email after?
Use $body += to add text to $body string each time, then move Send-MailMessage outside the loop so it only sends an email after each file has been evaluated:
foreach ($item in $list)
{
if($item.Size -gt $item.TriggerSize -And (Test-Path $item.Path))
{
$body += "The file $($item.Name) is reaching his max size!<br />
Current size: $($item.Size.ToString(".00")) $($byteSize.Substring(1))<br />
Maximum size: $($item.MaxSize) $($byteSize.Substring(1))<br /><br />"
}
}
Send-MailMessage -port 587 -From $from -BodyAsHtml -Encoding $enc -To $to -Subject $subject -Body $body -UseSsl -Credential $credentials
edit: I've updated the way $body is constructed to use subexpressions $() as these return only the properties of the object making your string construction much simpler.
This might do it (untested). It defines $body as an empty string, which we then increment in the loop with the messages about the files and then I have moved the send-mailmessage outside of the loop so that we do it at the end, but only if $body has contents:
$body = ""
foreach ($item in $list)
{
if($item.Size -gt $item.TriggerSize -And (Test-Path $item.Path))
{
$body +=
"The file " + $item.Name + " is reaching his max size!
<br /><br /> Current size: "+$item.Size.ToString(".00") + " " + $byteSize.Substring(1) +
"<br /> Maximum size: " + $item.MaxSize + " " + $byteSize.Substring(1) + "<br /><br />"
}
}
If ($body) { Send-MailMessage -port 587 -From $from -BodyAsHtml -Encoding $enc -To $to -Subject $subject -Body $body -UseSsl -Credential $credentials }
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