Converting Hashtable in Powershell - 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

Related

How to send emails based on my data. csv file

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)
"#
[...]

PowerShell Send-MailMessage format column in message body

Warning - I'm new to PowerShell. There are two outcomes I would like to achieve with this script. The first is to include the output in an email and format the columns in the message body so they align with the headers similar to Out-Host. Second is when out-csv, out-gridview or export-excel, how do I order the columns?
$VolArray = #();
$Volumes = Get-Ncvol | Where-Object {$_.VolumeMirrorAttributes.IsDataProtectionMirror -match 'False' -and $_.VolumeStateAttributes.IsVserverRoot -match 'False' -and -not $_.VolumeCloneAttributes.VolumeCloneParentAttributes}
ForEach ($Volume in $Volumes){
#get properties
$vol = Get-Ncvol $Volume
#create object with values
$volobj = New-Object -TypeName PSObject -Property #{
'Controller' = $vol.NcController
'Vserver' = $vol.Vserver
'Aggregate' = $vol.VolumeIdAttributes.ContainingAggregateName
'Name' = $vol.VolumeIdAttributes.Name
'Type' = $vol.VolumeIdAttributes.Type
'TotSizeGB'= $vol.VolumeSpaceAttributes.Size/1gb
'Used' = $vol.VolumeSpaceAttributes.SizeUsed/1gb
'%Used' = $vol.VolumeSpaceAttributes.PercentageSizeUsed
'AvailableGB' = $vol.VolumeSpaceAttributes.SizeAvailable/1gb
'SSResSizeGB' = $vol.VolumeSpaceAttributes.SnapshotReserveSize/1GB
'IsDPMirror' = $vol.VolumeMirrorAttributes.IsDataProtectionMirror
'IsReplicaVol' = $vol.VolumeMirrorAttributes.IsReplicaVolume
'IsDPSource' = $vol.VolumeMirrorAttributes.IsSnapmirrorSource
'DPInProgress' = $vol.VolumeMirrorAttributes.MirrorTransferInProgress
'SSPolicy' = $vol.VolumeSnapshotAttributes.SnapshotPolicy
'AutoSSEnabled' = $vol.VolumeSnapshotAttributes.AutoSnapshotsEnabled
'SSCount' = $vol.VolumeSnapshotAttributes.SnapshotCount
'%SSReserve' = $vol.VolumeSpaceAttributes.PercentageSnapshotReserve
'%SSResUsed' = $vol.VolumeSpaceAttributes.PercentageSnapshotReserveUsed
'SSSpaceUsed' = $vol.VolumeSpaceAttributes.SizeUsedBySnapshots/1GB;
}
#add to array outside opf for-loop
$VolArray += $volobj
}
#$VolArray | Export-Csv -Path c:\temp\file.csv
#$VolArray | Export-Excel -Path c:\temp\exceldump.xlsx
$VolArray | Out-String
#Send-MailMessage -To $mailto -Subject $subject -Body (-join $message) -Port $port -SmtpServer $smtp -from $emailfrom
Send-MailMessage -To $mailto -Subject $subject -Port $port -SmtpServer $smtp -from $emailfrom -Attachments c:\temp\file.csv
Message body:
Column ordering
In PowerShell, for performance reasons, there is no guarantee of order for properties of common hashtables. Thankfully you can use the [ordered] keyword to create ordered dictionaries (a hashtable is a form of dictionary) since version 3.
[PSCustomObject][ordered]#{
FirstColumn = 1
SecondColumn = 2
ThirdColumn = 3
}
This will ensure the order of the properties in subsequent operations like Export-Csv. Note that I also used the [PSCustomObject] accelerator, which is more efficient and than New-Object -TypeName PSObject.
Getting the data efficiently
In your code there are unnecessary calls to Get-Ncvol in the foreach loop. You already have the data you need form earlier:
$Volumes = Get-Ncvol |
Where-Object {
$_.VolumeMirrorAttributes.IsDataProtectionMirror -match 'False' -and
$_.VolumeStateAttributes.IsVserverRoot -match 'False' -and
-not $_.VolumeCloneAttributes.VolumeCloneParentAttributes
}
# Store results in a variable to use later
$reportData = foreach ($Volume in $Volumes) {
# Create object with values
[PSCustomObject][ordered]#{
'Controller' = $Volume.NcController
'Vserver' = $Volume.Vserver
'Aggregate' = $Volume.VolumeIdAttributes.ContainingAggregateName
'Name' = $Volume.VolumeIdAttributes.Name
'Type' = $Volume.VolumeIdAttributes.Type
'TotSizeGB' = $Volume.VolumeSpaceAttributes.Size / 1gb
'Used' = $Volume.VolumeSpaceAttributes.SizeUsed / 1gb
'%Used' = $Volume.VolumeSpaceAttributes.PercentageSizeUsed
'AvailableGB' = $Volume.VolumeSpaceAttributes.SizeAvailable / 1gb
'SSResSizeGB' = $Volume.VolumeSpaceAttributes.SnapshotReserveSize / 1GB
'IsDPMirror' = $Volume.VolumeMirrorAttributes.IsDataProtectionMirror
'IsReplicaVol' = $Volume.VolumeMirrorAttributes.IsReplicaVolume
'IsDPSource' = $Volume.VolumeMirrorAttributes.IsSnapmirrorSource
'DPInProgress' = $Volume.VolumeMirrorAttributes.MirrorTransferInProgress
'SSPolicy' = $Volume.VolumeSnapshotAttributes.SnapshotPolicy
'AutoSSEnabled' = $Volume.VolumeSnapshotAttributes.AutoSnapshotsEnabled
'SSCount' = $Volume.VolumeSnapshotAttributes.SnapshotCount
'%SSReserve' = $Volume.VolumeSpaceAttributes.PercentageSnapshotReserve
'%SSResUsed' = $Volume.VolumeSpaceAttributes.PercentageSnapshotReserveUsed
'SSSpaceUsed' = $Volume.VolumeSpaceAttributes.SizeUsedBySnapshots / 1GB;
}
}
Exporting & emailing
Since we already took care of the column ordering you just need to use $reportData | Export-Csv c:\temp\file.csv -NoTypeInformation or the Export-Excel equivalent.
Emailing is going to be a bit more difficult. Your best bet to convert the data to an HTML table and include it as the body of the email.
# The CSS is neccesary to make the table look nicer, adjust as needed
$css = #'
<style>
body {background-color: powderblue;}
h1 {color: red;}
p {color: blue;}
th, td {
padding: 15px;
text-align: left;
}
</style>
'#
$emailBody = $reportData | ConvertTo-Html -Head $css
# Use parameter splatting for redability
$emailParameters = #{
To = "jdoe#company.com"
Subject = "NetApp report for $(Get-Date -Format 'd')"
Body = $emailBody
BodyAsHtml = $true
SmtpServer = "smtp.company.com"
Credential = Get-Credential
}
Send-MailMessage #emailParameters
[Ordered] worked perfectly.
I modified the message body parameter:
Body = ($emailBody | Out-String)
Because of this error:
Send-MailMessage : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'Body'. Specified method is not supported.
Would you have any recommendations for setting the number of decimal places? ...124.994548797607421875

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

How to get carriage return in my email body in powershell script?

I have created a powershell script to gather some system stats and email them, I would like to get carriage returns in the body so that everything is not jumbled up.
Anyone know how I would archieve this with the script below?
Process{
$strComputerName = (Get-WmiObject -Class Win32_ComputerSystem | Select-Object Name).Name
$strComputerModel = (Get-WmiObject -Class Win32_ComputerSystem | Select-Object Model).Model
$strSerialNumber = (Get-WmiObject -Class Win32_BIOS | Select-Object SerialNumber).SerialNumber
$strDate = Get-Date
$erroractionpreference = "SilentlyContinue"
$strSMTP = $server
$strSubject = $subject
$strBody = "Hostname: $strComputerName Model: $strComputerModel Serial: $strSerialNumber Date: $strDate"
$MailMessage = New-Object System.Net.Mail.MailMessage
$MailMessage.IsBodyHtml = $true
$SMTPClient = New-Object System.Net.Mail.smtpClient
$SMTPClient.host = $strSMTP
$Sender = New-Object System.Net.Mail.MailAddress($from, "ConfigMgr")
$Recipient = New-Object System.Net.Mail.MailAddress($to)
$MailMessage.Sender = $Sender
$MailMessage.From = $Sender
$MailMessage.Subject = $strSubject
$MailMessage.To.add($Recipient)
$MailMessage.Body = $strBody
$SMTPClient.Send($MailMessage)
}
Trying to get CR for the $strBody output.. anyone?
Thanks :)
There are several ways to achieve this.
If you just want to separate items in the assignment to $strBody as showed in your code i would change the line to.
$strBody = "Hostname: $strComputerName`r`n Model: $strComputerModel`r`n Serial: $strSerialNumber`r`n Date: $strDate"
backtick+r and backtick+n is the powershell escape sequence for Carriage Return and Newline.
You could also use the constant from the Environment class like this.
$strBody = "Hostname: $strComputerName{0} Model: $strComputerModel{0} Serial: $strSerialNumber{0} Date: $strDate" -f [Environment]::NewLine
Let me know if i misunderstood your question or if it does not work for you.
Neither (backtick)n nor (backtick)r(backtick)n work when using Send-MailMessage. I had to switch to HTML.
Send-MailMessage -From foo#bar.com -To "me#my.com"
-SmtpServer foo.bar.com -Subject "my subject"
-Body ($fileText -join "<br/>") -BodyAsHtml
Where $fileText was an array of strings.

enumerate and email with powershell

ok fellas, I've been reading, researching, learning and testing about powershell. Within the last 20 days, here is what I've been able to come up with.
get-mailbox -identity $_.name | select name,userprincipalname,#{label="Size";expression={$size = Get-Mailboxstatistics $_.identity;$size.TotalItemSize.Value.ToMB()}},#{label="Items";expression={$items = Get-Mailboxstatistics $_.name;$item.ItemCount}}
I stored this in a script called accountsizes.ps1. It works exactly as I expected by outputting all the email accounts with the sizes, but in order for me to get only the mailboxes over 2048MB, I have to call it like this:
PS C:\accountsizes.ps1 | where size -gt "2048" | select userprincipalname,size
And this works by return the email addresses and mailbox sizes in MBs. But now my dilemma is; how do I enumerate through the results and extract each email address and send an email to that user and myself, warning them that their mailbox is too large and they need to archive. From what I been reading and learning, I would have to use a ForEach loop and the send-mailmessage cmdlet. I cannot figure out how to use the ForEach and incorporate it with the script: Here is I go brain dead with the ForEach:
PS C:\accountsizes.ps1 | where size -gt "2048" | select userprincipalname,size | ForEach($user in userprincipalname){$_.userprincipalname}
I do not know the right way to go about doing this (so, don't ask me why I'm doing this way :)), I have no previous knowledge about scripting and coding.
Here is my email part:
$smtpserver = "domain.com"
$smtpFrom = "me#domain.com"
$smtpTo = "you#domain.com"
$messageSubject = "Warning Email Mailbox Too Large"
$body = "blah blah blah blah"
send-mailmessage -from $smtpFrom -to $smtpTo -subject $messageSubject -body $body
Thanks in advance for your helpful advice.
The foreach keyword and the ForEach-Object cmdlet are two different things.
If you use the foreach keyword, you give a name to the iteration variable, and you iterate on a collection. For example:
$collection = #("one", "two")
foreach ($item in $collection) {
Write-Host $item
}
Instead, if you pipe commands outputs, you have to use the ForEach-Object cmdlet with a script block. Inside the script block, you refer to the iteration variable with the special variable $_. Example:
$collection = #("one", "two")
$collection | ForEach-Object {
Write-Host $_
}
You can shorten ForEach-Object with %:
$collection | % {
Write-Host $_
}
So, in your case you should probably do this:
C:\accountsizes.ps1 | where size -gt "2048" | select userprincipalname,size | % { $_.userprincipalname }
Paolo provided excellent information and deserves upvotes if nothing else. As to your question of how to enumerate and send email you probable need something like:
C:\accountsizes.ps1 | where size -gt "2048" | %{
$smtpServer = "smtp.domain.com"
#Creating SMTP server object
$SMTP = new-object Net.Mail.SmtpClient($smtpServer)
#Creating a Mail object
$EMail = new-object Net.Mail.MailMessage
#Construct Email
$EMail.From = "me#domain.com"
$EMail.ReplyTo = "me#domain.com"
$EMail.To.Add($_.userprincipalname)
$EMail.subject = "Warning Email Mailbox Too Large"
$EMail.body = "blah blah blah blah"
$SMTP.Send($EMail)
}
You could get a lot more fancy, go by size and send different emails depending on how large their mailbox is, or get content from files for subject and body depending on size, but that is just going to make things complicated. You could also use Send-MailMessage, and that works just fine, I just like this way because it makes it easier to work with in my opinion than one really long line with a ton of switches. If the message and subject are going to be generic you may want to do something more like:
$smtpServer = "smtp.domain.com"
#Creating SMTP server object
$SMTP = new-object Net.Mail.SmtpClient($smtpServer)
#Creating a Mail object
$EMail = new-object Net.Mail.MailMessage
#Construct Email
$EMail.From = "me#domain.com"
$EMail.ReplyTo = "me#domain.com"
C:\accountsizes.ps1 | where size -gt "2048" | %{$EMail.BCC.Add($_.userprincipalname)}
$EMail.subject = "Warning Email Mailbox Too Large"
$EMail.body = "blah blah blah blah"
$SMTP.Send($EMail)
That would make one email and BCC everybody on it. Then you could do another email to yourself stating who warnings got sent to.