Powershell popup to list of usernames - powershell

I wish to have a powershell script that provides a popup message to a specific list of users. I need to use a list of relevant users in a spreadsheet with their network username and not use Active Directory. I have the powershell script to display a suitable message with a Warning icon and found to format the popup in a certain way I had to use a "FORM" and not a default popup messagebox. Although the following two lines for the Warning icon would not work in the form so had to use a picture box
$WarningIcon = New-Object ([System.Windows.MessageBoxImage]::Warning)
$Form.Controls.Add($WarningIcon)
The script for the form is as follows. There is probably a cleaner way of creating a form like this but I am quite new to Powershell!
Add-Type -AssemblyName System.Windows.Forms
$Form = New-Object system.Windows.Forms.Form
$Form.Width = 700
$Form.Height = 400
$Form.BackColor = "#DCDCDC"
$Form.Text = "System Restart Alert"
$Font = New-Object System.Drawing.Font("Ariel",30,
[System.Drawing.FontStyle]::Bold::Underline)
$FontB = New-Object System.Drawing.Font("Ariel",14,
[System.Drawing.FontStyle]::Bold)
$Picture = (get-item ("C:\FA_Files\Windows_Warning_Exclamation9.jpg"))
$img = [System.Drawing.Image]::Fromfile($Picture)
$pictureBox = new-object Windows.Forms.PictureBox
$pictureBox.Location = New-object System.Drawing.Size(160,30)
$pictureBox.Height = "100"
$pictureBox.Image = $img
$Form.controls.add($pictureBox)
$Label = New-Object System.Windows.Forms.Label
$Label.Location = "260,30"
$Label.Font = $Font
$Label.ForeColor = "Red"
$Label.Text = "WARNING!"
$Label.AutoSize = $True
$Form.Controls.Add($Label)
$LabelB = New-Object System.Windows.Forms.Label
$LabelB.Location = "100,130"
$LabelB.Font = $FontB
$LabelB.Text = "Due to essential maintenance system requires rebooting"
$LabelB.AutoSize = $True
$Form.Controls.Add($LabelB)
$LabelC = New-Object System.Windows.Forms.Label
$LabelC.Location = "100,160"
$LabelC.Font = $FontB
$LabelC.Text = "Please save all work immediately"
$LabelC.AutoSize = $True
$Form.Controls.Add($LabelC)
$okButton = New-Object System.Windows.Forms.Button
$okButton.Location = "300,280"
$okButton.Font = "$FontB"
$okButton.Size = "85,28"
$okButton.Text = "Okay"
$Form.Controls.Add($okButton)
$okButton.Add_Click = ({$Form.Close()})
$Form.ShowDialog()
I can retrieve a list of network usernames from the spreadsheet okay. Below is an image of the csv content
But I have googled a lot and cannot find a way of sending the popup to the list of usernames retrieved from the spreadsheet. So far I have tried the following where I have the form above set to the $msg varaiable and also tried having the form in another file and referencing that file in the $msg variable but it doesn't work
$csv = Import-csv "C:\FA_Files\NetNames.csv"
foreach($line in $csv)
{
$name = $line.("Name")
$netName = $line.("NetworkName")
#Echo "Name is $name and Network Name is $netName"
msg $netName $msg
}
It also has to be on the username and not the machine name.
How do I correct this please?

FWIW, msg.exe has replaced net /send in Windows, and is meant for sending messages to users, so I would begin by looking at the syntax for message.exe here. .
It has a /server: switch you can use to send a message to a remote host. So, take your user and computer name list and put them in a CSV file like this:
//MyInputFile.csv
ComputerName,UserName
Laptop01,BillG
Laptop02,StephenO
PC03,WayneH
Desktop04,JimA
You could use a short script like this to achieve your goal (or get you mostly there, at least 😁)
$Msg = "Put your message here"
$SpreadSheet = Import-CSV .\PathTo\MyInputFile.csv
ForEach ($row in $SpreadSheet){
"Sending message to \\$($row.ComputerName)\$($row.UserName) : $msg"
msg /server:$($row.ComputerName) $($row.UserName) $msg
}
You'll see an output like this in the console:
Sending message to \\Laptop01\BillG : Put your message here
Sending message to \\Laptop02\StephenO : Put your message here
Sending message to \\PC03\WayneH : Put your message here
Sending message to \\Desktop04\JimA : Put your message here
Sending message to \\localhost\* : Put your message here
Then your lucky user will see the following on their workstation.
If a computer can't be reached, the process will time out after five seconds or so and move on to the next one in the list.

All users that are a member of Active Directory by default receive read rights, this is by design as Active Directory is exactly the kind of system that should be relied on for the kind of functionality you desire... not sure why your ICT manager is so against the idea.
You cannot send a message to a user (with only the username), you must know the machine the user is logged into as well.
The only way I can think to achieve this is to iterate through every computer and query who is logged on and if the user matches your list then send a message to that computer. This would require reading a list of computers from AD or iterating through every available IP address on your local network(s).

Related

How to update datatable and commit back to sql server source

Alright so I have a table with some data in it. Here is a screenshot of the table def in SQL server:
And there is an app which populates some of the data in the table. Here is a screenshot of the sample data. All columns except for [emailSentCount] are populated by an external app.
Now my question is with with a Powershell script that I'm trying to build to consume this data and send email Notifications. I read all the content of the table in a DataTable. I go through each Row and decide if I have to send an email for that row. If so, then I send the email and I update the [emailSentCount] column by adding + 1 to it.
At the end of the script I'm trying to send these changes I made to the DataTable back to the table on SQL server. However I get an error:
Here is the script I'm working with.
param(
[string]$SQLServerName="SQLServerName\InstanceName"
,[string]$SQLDatabaseName="DBName"
,[string]$SQLTableName = "UserList"
,[string]$SQLSelectQuery="SELECT * FROM $SQLTableName"
)
cls
Function SendEmail
{
Param(
[string]$ToMailAddress,
[int]$MessageTemplate
)
[string]$MessageBody=$null
switch ($MessageTemplate)
{
1 {$MessageBody = Test new certificate issued. Please ignore!}
2 {$MessageBody = Test existing certificate renewed. Please ignore!}
}
$from = "EmailApplicationAccount#example.com"
$to = $ToMailAddress
$smtp = "smtp.example.net"
Send-MailMessage
-From $from
-To $to
-Subject $MessageBody
-SmtpServer $smtp
}
$sqlConn = New-Object System.Data.SqlClient.SqlConnection
$sqlConn.ConnectionString = “Server=$SQLServerName;Integrated Security=true;Initial Catalog=$SQLDatabaseName”
$sqlConn.Open()
$sqlCommand = $sqlConn.CreateCommand()
$sqlCommand.CommandText = $SQLSelectQuery
$dataAdapter = New-Object System.Data.SqlClient.SqlDataAdapter $sqlCommand
$dataTable = New-Object System.Data.DataTable
$dataAdapter.Fill($dataTable) | Out-Null
foreach($dataRow in $dataTable.Rows)
{
write-host $dataRow["shouldSendEmail"]
if($dataRow["shouldSendEmail"] -eq $true)
{
# First send email depending on whether its a first time new cert or a cert renewal.
if($dataRow["certRenewal"] -eq $true)
{
SendEmail -ToMailAddress $dataRow["email"] -MessageTemplate 2
}
else
{
SendEmail -ToMailAddress $dataRow["email"] -MessageTemplate 1
}
# After you have sent the email, increase the emailSentCount value in the datatable.
$dataRow["emailSentCount"] = $dataRow["emailSentCount"] + 1
#Also reset the shouldSendEmail column to $false/0
$dataRow["shouldSendEmail"] = $false
}
}
$dataAdapter.Update($dataTable)
$sqlConn.Close()
It seems I need to include some Update command. But what will it look like in this context and wwhere does it need to be included?
Actually, I found the solution. We have to build an update command before we start messing with the data in the rows. Here is the bit I added just before the Foreach loop.
# command builder
$commandBuilder = new-object system.data.sqlclient.sqlcommandbuilder($dataAdapter)
$dataAdapter.UpdateCommand = $commandBuilder.GetUpdateCommand()
And that was it! No errors and I can see the data on columns [shouldSendEmail] and [emailSentCount] change on the source table in SQL server as intended in the script.

How to filter e-mails by Flag status in EWS?

I'm using EWS and trying to find e-mail messages that are flagged for action (i.e. their Flag.FlagStatus property is "Flagged"). I have successfully filtered e-mails based on their subject and other properties, but I just can't wrap my head around how to filter them based on FlagStatus.
The problem arises when defining $searchFilter in the code below.
The line returns error
"Exception calling "FindItems" with "2" argument(s): "Validation
failed. Parameter name: searchFilter""
I've tried using other variants of SearchFilter, e.g. SearchFilter+IsEqualTo, but all return the same error.
Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll"
$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP3
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
$service.Credentials = New-Object System.Net.NetworkCredential("username","password")
$service.Url = "https://mail.server.net/EWS/Exchange.asmx"
$inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox)
$propertySet = New-Object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
$propertySet.RequestedBodyType = [Microsoft.Exchange.WebServices.Data.BodyType]::Text
$searchFilter = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring([Microsoft.Exchange.WebServices.Data.ItemSchema]::Flag.FlagStatus, "Flagged")
$view = New-Object Microsoft.Exchange.WebServices.Data.ItemView(1,0)
$messages = $inbox.FindItems($searchFilter, $view)
foreach ($item in $messages.Items) {
$item.Load($propertySet)
write-host $item.Flag.FlagStatus
write-host $item.Body.Text
}
This one works:
$searchFilter = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring([Microsoft.Exchange.WebServices.Data.ItemSchema]::Categories, "Blue Category")
This one doesn't:
$searchFilter = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring([Microsoft.Exchange.WebServices.Data.ItemSchema]::Flag.FlagStatus, "Flagged")
I'd suggest you use the Extendedproperty instead of the strongly typed one (which is derived from the ep anyway) eg
ExtendedPropertyDefinition PidTagFlagStatus = new ExtendedPropertyDefinition(0x1090, MapiPropertyType.Integer);
SearchFilter.Exists sfExits = new SearchFilter.Exists(PidTagFlagStatus);
should work to give you any messages that are flagged (eg the property exists) or you could be more specific to get followupFlagged
SearchFilter.IsEqualTo sfEqualTo = new SearchFilter.IsEqualTo(PidTagFlagStatus, 0x00000002);
in Powershell you need
$PidTagFlagStatus = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x1090, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);
$searchFilter = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+Exists($PidTagFlagStatus)
or
$searchFilter = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo($PidTagFlagStatus, 0x00000002)

Show a list when a choice is made

I'm trying to work around a script Under Windows.Form and I'm a little bit stuck.
I'd like to be able a specific list appears depending on the choice made from the first list, which means that at the start of the script, only one list has to appears and many other available depending of the choice made.
Here's the full script for reference
#Open a Window.
[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$form = New-Object Windows.Forms.Form
$form.text = "Contrôles"
$form.Size = New-Object System.Drawing.Size(1000,700)
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Point(75,150)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = 'OK'
$OKButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$form.AcceptButton = $OKButton
$form.Controls.Add($OKButton)
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Point(150,150)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = 'Cancel'
$CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$form.CancelButton = $CancelButton
$form.Controls.Add($CancelButton)
#Create the Data table (DataTable).
$table1 = New-Object system.Data.DataTable
$table2 = New-Object system.Data.DataTable
#Define the 2 column (Name, Type).
$colonne1 = New-Object system.Data.DataColumn Choice,([string])
$colonne2 = New-Object system.Data.DataColumn Choice,([string])
#Create columns in the data table.
$table1.columns.add($colonne1)
$table2.columns.add($colonne2)
#Add the data line by line in the data table.
$ligne = $table1.NewRow() #Creation of the new row.
$ligne.Choice = "Service" #In the column Choice we put the value we want.
$table1.Rows.Add($ligne) #Add a line in the data table.
$ligne = $table1.NewRow()
$ligne.Choice = "Software"
$table1.Rows.Add($ligne)
$ligne = $table1.NewRow()
$ligne.Choice = "Other"
$table1.Rows.Add($ligne)
#Add the data line by line in the data table.
$ligne = $table2.NewRow() #Creation of the new row.
$ligne.Choice = "Service Enable" #In the column Choice we put the value we want.
$table2.Rows.Add($ligne) #Add a line in the data table.
$ligne = $table2.NewRow()
$ligne.Choice = "Service Disable"
$table2.Rows.Add($ligne)
$ligne = $table2.NewRow()
$ligne.Choice = "Other"
$table2.Rows.Add($ligne)
#Create the View.
$vu1 = New-Object System.Data.DataView($table1)
$vu1.Sort="Choice ASC" #Tri la colonne "Extension" par ordre croissant.
$vu2 = New-Object System.Data.DataView($table2)
$vu2.Sort="Choice ASC"
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(650,50)
$label.Size = New-Object System.Drawing.Size(280,35)
$label.Text = 'Please enter the information in the space below:'
$form.Controls.Add($label)
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Point(650,100)
$textBox.Size = New-Object System.Drawing.Size(260,20)
$form.Controls.Add($textBox)
#Create the Drop-down list (ComboBox).
$liste1 = New-Object System.Windows.Forms.Combobox
$liste1.Location = New-Object Drawing.Point 20,50
$liste1.Size = New-Object System.Drawing.Size(150, 50)
$liste1.DropDownStyle = "DropDownList"
$liste2 = New-Object System.Windows.Forms.Combobox
$liste2.Location = New-Object Drawing.Point 350,50
$liste2.Size = New-Object System.Drawing.Size(150, 50)
$liste2.DropDownStyle = "DropDownList"
#Associate the Data to the Drop-down list
#To do so, we create a "Binding Context".
$liste1.BindingContext = New-Object System.Windows.Forms.BindingContext
$liste1.DataSource = $vu1 #Assigne the view that contains the sorted Data.
$liste1.DisplayMember = "Choice" #Column that will be displayed (Choice).
$liste2.BindingContext = New-Object System.Windows.Forms.BindingContext
$liste2.DataSource = $vu2 #Assigne the view that contains the sorted Data.
$liste2.DisplayMember = "Choice" #Column that will be displayed (Choice).
#Attach the control to the window.
$form.controls.add($liste1)
$form.controls.add($liste2)
#Show everything.
$form.Add_Shown({$textBox.Select()})
$result = $form.ShowDialog()
#Work the code arround.
if ($liste1.DisplayMember= "Service Enable")
{set-service -name RemoteRegistry -ComputerName $textBox.Text -StartupType Automatic}
if ($liste1.DisplayMember = "Service Disable")
{set-service -name RemoteRegistry -ComputerName $textBox.Text -StartupType Automatic}
Write-Host "ComboBox = " $liste1.DisplayMember
Write-Host "ComboBox = " $liste2.selectedvalue
#Fin.
If anybody have an idea where I could look, it would be great.
Thanks you
Nad
1. You have no form / trigger events in your code.
2. You don't have the correct GUI objects in your code to hold a list /
record result.
A form is just a container to hold elements until you add the code behind to make it do something. You have to have a proper GUI object to send that result to.
I am not sure if you are doing this all by hand in the ISE or VSCode or Notepad or whatever, but this is a good first effort. However, what you show, seems to indicate you are not really up to speed on GUI development / general app dev work, as what you are doing is not really unique to PowerShell, but something required for any app development client or web.
So, really, spend some time studying / reviewing general WPF/Winforms development and that form event stuff will be covered.
As for your use case, you need:
Define the list GUI object (multiline, ListBox, ListView, datagrid) to hold the results (synch'ing combox boxes mean adding and removing elements on event actions)
Define what that list is (text files, db read etc)
On the click, change or other form event, read from that list and populate
the GUI list object
There are many examples of this on this site and all over the web.
Here a good video on GUI development with PowerShell:
powershell populate combobox basing on the selected item on another combobox
From the above discussion (not something to just add to your code without understanding the what's and the why's):
Use a ComboBox.SelectionChangeCommitted Event:
"Occurs when the user changes the selected item and that change is displayed in the ComboBox"
$combobox2_SelectionChangeCommitted={
$Mailboxes = Get-Mailbox -OrganizationalUnit $ClientSelected
foreach ($mailbox in $Mailboxes)
{
$CurrentMailbox = "{0} ({1})" -f $mailbox.Name, $mailbox.Alias
Load-ComboBox $combobox2 $CurrentMailbox -Append
}
}
Use a button:
$button1_Click={
$Mailboxes = Get-Mailbox -OrganizationalUnit $ClientSelected
foreach ($mailbox in $Mailboxes)
{
$CurrentMailbox = "{0} ({1})" -f $mailbox.Name, $mailbox.Alias
Load-ComboBox $combobox2 $CurrentMailbox -Append
}
}
Lastly, using this …
Write-Host "ComboBox = " $liste1.DisplayMember
Write-Host "ComboBox = " $liste2.selectedvalue
… is not something one would do, because the console is not opened to see these results and Write-Host should be avoided except for when using console only text colorizations of other console only formatting scenarios, it also empties the display buffer, so it cannot be sent to anything else. Also, you don't have a GUI object called 'ComboBox' anywhere on the form, so it's not serving any purpose for your use case.
After some times and research, I managed to find what I needed exactly.
This might help people who stumble upon the post so here a small part of what I found
function Service()
{if ($ListBox1.SelectedItem -eq 'Enable Services')
{
$form.Controls.Add($Label3)
$form.Controls.add($ListBox2)
$form.Controls.Add($Label4)
$form.Controls.Add($textBox)
$form.Controls.Add($Button2)
$form.Controls.Add($Button3)
}
I create firstly a Function with a name, in which will countain the condition of what I'd like to happen when a choice is made in my listbox "ComboBox"
$button1.add_Click({ Service })
Then I call that function from a button I Added, in which other Boxes will appear upon click on that button.
It is not very different from #Postanote's answer but that was the solution I'm more at ease with.

Remote execute powershell script that displays a form

I am trying to run a powershell script remotely that displays a pop-up alert. We are attempting to create a sort of "Emergency Notification System". On the admin machine there is a script to choose which emergency alert to send to everyone, and then it should run/appear on everyone's screen.
I've used the /msg command to achieve this, but the message is so plain that it doesn't catch the user's attention and the text can't be customized (unless it can be, which in that case PLEASE enlighten me).
I am receiving the error below when attempting to do this. I've also attempted to do it via PsExec but receive the same error.
Error:
Exception calling "ShowDialog" with "0" argument(s): "Showing a modal dialog box or form when the application is not running in UserInteractive mode is not a valid operation. Specify the ServiceNotification or DefaultDesktopOnly style to display a notification from a service application."
Below is the PowerShell script. It's nothing fancy, just wanting something that works for our purpose.
Add-Type -AssemblyName System.Windows.Forms
$Form = New-Object system.Windows.Forms.Form
$Form.Text = "Disaster Alert!"
$Form.AutoScroll = $True
$Form.AutoSize = $True
$Form.AutoSizeMode = "GrowAndShrink"
$Form.WindowState = "Normal"
$Form.StartPosition = "CenterScreen"
$Form.Font = New-Object System.Drawing.Font("Calibri",60,[System.Drawing.FontStyle]::Bold)
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(500,200)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "Close"
$OKButton.Font = New-Object System.Drawing.Font("Calibri",11)
$OKButton.UseVisualStyleBackColor = $True
$OKButton.Add_Click({$Form.Close()})
$Form.Controls.Add($OKButton)
$Label = New-Object System.Windows.Forms.Label
$Label.Text = "Please Exit the Building`nBuilding Fire Alarm Sounding!!"
$Label.ForeColor = "Red"
$Label.TextAlign = "MiddleCenter"
$Label.AutoSize = $True
$Form.Controls.Add($Label)
$Form.ShowDialog()
Is it possible to have a pop-up via PowerShell? If not, does anyone have any recommendations?
Thank you very much in advance.

Create Outlook email draft using PowerShell

I'm creating a PowerShell script to automate a process at work. This process requires an email to be filled in and sent to someone else. The email will always roughly follow the same sort of template however it will probably never be the same every time so I want to create an email draft in Outlook and open the email window so the extra details can be filled in before sending.
I've done a bit of searching online but all I can find is some code to send email silently. The code is as follows:
$ol = New-Object -comObject Outlook.Application
$mail = $ol.CreateItem(0)
$Mail.Recipients.Add("XXX#YYY.ZZZ")
$Mail.Subject = "PS1 Script TestMail"
$Mail.Body = "
Test Mail
"
$Mail.Send()
In short, does anyone have any idea how to create and save a new Outlook email draft and immediately open that draft for editing?
Based on the other answers, I have trimmed down the code a bit and use
$ol = New-Object -comObject Outlook.Application
$mail = $ol.CreateItem(0)
$mail.Subject = "<subject>"
$mail.Body = "<body>"
$mail.save()
$inspector = $mail.GetInspector
$inspector.Display()
This removes the unnecessary step of retrieving the mail from the drafts folder. Incidentally, it also removes an error that occurred in Shay Levy's code when two draft emails had the same subject.
$olFolderDrafts = 16
$ol = New-Object -comObject Outlook.Application
$ns = $ol.GetNameSpace("MAPI")
# call the save method yo dave the email in the drafts folder
$mail = $ol.CreateItem(0)
$null = $Mail.Recipients.Add("XXX#YYY.ZZZ")
$Mail.Subject = "PS1 Script TestMail"
$Mail.Body = " Test Mail "
$Mail.save()
# get it back from drafts and update the body
$drafts = $ns.GetDefaultFolder($olFolderDrafts)
$draft = $drafts.Items | where {$_.subject -eq 'PS1 Script TestMail'}
$draft.body += "`n foo bar"
$draft.save()
# send the message
#$draft.Send()
I think Shay Levy's answer is almost there: the only bit missing is the display of the item.
To do this, all you need is to get the relevant inspector object and tell it to display itself, thus:
$inspector = $draft.GetInspector
$inspector.Display()
See the MSDN help on GetInspector for fancier behaviour.
if you want to use HTML template please use HTMLbody instead of Body , please find sample code below:
$ol = New-Object -comObject Outlook.Application
$mail = $ol.CreateItem(0)
$mail.Subject = "Top demand apps-SOURCE CLARIFICATION"
$mail.HTMLBody="<html><head></head><body><b>Joseph</b></body></Html>"
$mail.save()
$inspector = $mail.GetInspector
$inspector.Display()
Thought I would add in to this as well. There are a few steps you can save yourself if you know a lot of the basics (subject, recipients, or other aspects). First create the template of the email and save that, e.g. somewhere maybe with the code?
As to the code itself, it follows much the same that others have posted.
Borrowing from Jason:
$ol = New-Object -comObject Outlook.Application
$msg = $ol.CreateItemFromTemplate(<<Path to template file>>)
Modify as needed. Append fields or modify body. The message can still be viewed prior to sending the same way $msg.GetInspector.Display(). Then call $msg.send() to send away!