How to check whether the email has been sent with Powershell - powershell

I am currently writing a Powershell script, which pings a server and if a connection cannot be established, sends an email to the IT department to restart the server manually.
Right now, the script can successfully send the email when the ping is unsuccessful and the object has a property value, which is supposed to evaluate to "True", but I can't verify that, because upon sending the email, the variable deletes itself and therefore the property doesn't exist anymore.
$Outlook = New-Object -ComObject Outlook.Application
$Mail = $Outlook.CreateItem(0)
$Mail.To = "<recipient>"
$Mail.Cc = "<some_cc>"
$Mail.Send()
# After this line, the variable is deleted!
if ($Mail.Sent())
# The line which is supposed to work, but evaluates to False everytime
How do I make sure that this variable survives until I can verify the exchange?

The variable won't survive after Send method, and won't return a value either.
You are delegating the actual sending action to Outlook so you'll need to check Outlook Application for errors (i.e. a bounce).
If sending via Outlook is not necessary, you can have more options with
try {
Send-MailMessage -From 'monitoring#example.com' -To 'ITdesk#example.com' -Subject 'test' -Body 'whatever' -Priority High -DeliveryNotificationOption OnSuccess, OnFailure -SmtpServer 'mail.example.com' -ErrorAction stop -Port 25
}
catch{
write-warning "error in sending. $_"
}
Note that if you need authenticated send you can specify saved credentials via the -credential parameter.
References here:
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/send-mailmessage?view=powershell-6

You could put the mail.send() inside a try/catch block. That way it would output if there were any errors or if it succeeded. Plus, there's no need for another variable.
try{
mail.send()
write-host "success"
}catch{
write-host "fail"
}

Related

Powershell - Send email when number of days reaches a set interval

I'm at a blocker and need some help!
(New-Timespan -Start (Get-Date) -End (Get-AdfsCertificate -CertificateType Token-Signing | where-object { $_.IsPrimary -eq $true }).Certificate.NotAfter).Days
This returns a result of days until it is no longer a primary certificate. E.G simply "141"
How can I implement a way of looking at this result and sending an email when it reaches a certain day? E.G 20 days until it is no longer primary.
I can use a mail relay, so no credentials are required!
Many thanks in advance!
Put your code in a Task Schedule to run on a date and time, use an if/then code block to check the state, and send as needed when it meets your metric.
For example:
If ((New-Timespan -Start (Get-Date) -End (Get-AdfsCertificate -CertificateType Token-Signing |
where-object { $PSItem.IsPrimary -eq $true }).Certificate.NotAfter).Days -eq 20)
{
# Do something here
}
As for email, this is why the Send-MailMessage cmdlet exists.
Get-Help -Name Send-MailMessage -Full
Get-Help -Name Send-MailMessage -Examples
# Results
<#
NAME
Send-MailMessage
SYNOPSIS
Sends an email message.
# -- Example 1: Send an email from one person to another person --
Send-MailMessage -From 'User01 <user01#fabrikam.com>' -To 'User02 <user02#fabrikam.com>' -Subject 'Test mail'
The `Send-MailMessage` cmdlet uses the From parameter to specify the message's sender. The To parameter specifies the message's recipient. The Subject
parameter uses the text string Test mail as the message because the optional Body parameter is not included.
# ---------------- Example 2: Send an attachment ----------------
Send-MailMessage -From 'User01 <user01#fabrikam.com>' -To 'User02 <user02#fabrikam.com>', 'User03 <user03#fabrikam.com>' -Subject 'Sending the
Attachment' -Body "Forgot to send the attachment. Sending now." -Attachments .\data.csv -Priority High -DeliveryNotificationOption OnSuccess,
OnFailure -SmtpServer 'smtp.fabrikam.com'
The `Send-MailMessage` cmdlet uses the From parameter to specify the message's sender. The To parameter specifies the message's recipients. The
Subject parameter describes the content of the message. The Body parameter is the content of the message.
The Attachments parameter specifies the file in the current directory that is attached to the email message. The Priority parameter sets the message
to High priority. The -DeliveryNotificationOption parameter specifies two values, OnSuccess and OnFailure . The sender will receive email
notifications to confirm the success or failure of the message delivery. The SmtpServer parameter sets the SMTP server to smtp.fabrikam.com .
# ----------- Example 3: Send email to a mailing list -----------
Send-MailMessage -From 'User01 <user01#fabrikam.com>' -To 'ITGroup <itdept#fabrikam.com>' -Cc 'User02 <user02#fabrikam.com>' -Bcc 'ITMgr
<itmgr#fabrikam.com>' -Subject "Don't forget today's meeting!" -Credential domain01\admin01 -UseSsl
The `Send-MailMessage` cmdlet uses the From parameter to specify the message's sender. The To parameter specifies the message's recipients. The Cc
parameter sends a copy of the message to the specified recipient. The Bcc parameter sends a blind copy of the message. A blind copy is an email
address that is hidden from the other recipients. The Subject parameter is the message because the optional Body parameter is not included.
The Credential parameter specifies a domain administrator's credentials are used to send the message. The UseSsl parameter specifies that Secure
Socket Layer (SSL) creates a secure connection.
#>

Is it possible to use Send-MailMessage with default credentials?

I've been trying to write a powershell script that sends an automated email as part of it's process using the logged in user's credentials. There are two primary methods that I've found to do this: Send-MailMessage and using a Net.Mail.SmtpClient object.
I've been able to get it up and running using the Net.Mail.SmtpClient object without issue, however that API is marked obsolete and so I'd prefer to not use it if possible, however I haven't been able to get Send-MailMessage to work using default credentials.
Without UseDefaultCredentials both of the scripts below fail with the same general message.
$smtpServer = "server.com"
$smtpFrom = "user#server.com"
$smtpTo = "user#server.com"
$messageSubject = "THIS IS WHERE YOUR SUBJECT GOES"
$messageBody = "This is a test of automated email at $([datetime]::Now.ToString('dd.MM.yyyy - HH.MM.SS'))"
$smtp = New-Object Net.Mail.SmtpClient($smtpServer)
$smtp.EnableSsl = $true
$smtp.Port = 587
#$smtp.UseDefaultCredentials = $true
$smtp.Send($smtpFrom,$smtpTo,$messagesubject,$messagebody)
and
$mailsplat = #{
SmtpServer = 'server.com'
From = 'user#server.com'
To = 'user#server.com'
Subject = 'THIS IS WHERE YOUR SUBJECT GOES'
Body = "This is a test of automated email at $([datetime]::Now.ToString('dd.MM.yyyy - HH.MM.SS'))"
Port = 587
UseSSL = $true
#UseDefaultCredentials = $true
}
Send-MailMessage #mailsplat
"The SMTP server requires a secure connection or the client was not authenticated. The server
response was: 5.7.1 Client was not authenticated"
Fixing the first script is easy. If I uncomment out the UseDefaultCredentials line it works perfectly as expected. There's no UseDefaultCredentials or similar parameter available to Send-MailMessage however. I'd prefer to use the Send-MailMessage as it's the recommended method and the other is marked as obsolete, but I can't actually find a way to make it work with my requirements (despite a plethora of posts saying Send-MailMessage will use default credentials if none are provided and the documentation indicating it should)

Send email with Powershell from different mailbox

I have created a small PS script to create an email for my pipeline to send out whenever there is a deployment. the problem is i dont want the email to be sent from my personal email but from the company outlook email. i searched and saw different SMTP server names and using mail.from but i cant get it to work. can someone help me out?
param(
[Parameter(Mandatory=$true,Position=0)]
[string]$Address1,
[Parameter(Mandatory=$true,Position=1)]
[string]$Address2,
[switch]$Recurse,
[switch]$Force
)
$ol = New-Object -comObject Outlook.Application
$mail = $ol.CreateItem(0)
$Mail.Recipients.Add($Address1)
$Mail.Recipients.Add($Address2)
$Mail.Subject = "DSC Deployment in Progress"
$Mail.Body = "There is a DSC install beginning. . ."
$Mail.Send()
You need to assign a value to the SendUsingAccount property. The account can be found in the (outlook).Session.Accounts collection.
$sendSmtpAddress = "some.name#somedomain.com"
$account = $ol.session.acounts | ? { $_.smtpAddress -eq $sendSmtpAddress }
then, assign to the SendUsingAccount property before sending
$mail.SendUsingAccount = $account
$mail.Send()
Full example
$sendSmtpAddress = "some.name#somedomain.com"
$ol = new-object -comobject "outlook.application"
$account = $ol.session.accounts | ? { $_.smtpAddress -eq $sendSmtpAddress }
$mail = $ol.CreateItem(0)
$mail.recipients.add("target.user#somedomain.com") | out-null
$mail.subject = "test email"
$mail.body = "test email"
$mail.SendUsingAccount = $account
$mail.Send()
For what it's worth, I gave up trying to send email via Outlook a long time ago, it's much easier to use plain SMTP. Depending on the security policy on your local SMTP server (Exchange?), you may be able to 'send as' any user on your local domain. Ask your IT people for the name/IP of an internal SMTP server that you can use to send email, and then it's as easy as:
send-mailmessage -smtpServer (servername or IP) -from sender.name#domain.com -to #(recipient1#domain.com, recipient2#domain.com) -subject "Email Subject" -body "Email Body"
If using send-mailmessage, it's possible to set a Display Name for the sender by using the form "Display Name <sender.name#domain.com>" e.g.
-from "Deployment Alerts <sender.name#domain.com>"
Recipients will see the Display Name in their email client, rather than the SMTP address.
A couple of points that I consider to be good practice:
Depending on the config of the SMTP server, there may be little, or no verification of the 'sender' address. It is worth using a genuine account that you have access to, so that you have sight of any bounce / non-delivery reports.
Consider including something in the mail body (perhaps a footer) that mentions where the alert came from and what process generated it. This can help your successor or colleague track down the script in the future.
The assignment like
$mail.SendUsingAccount = $account
worked for me since Windows Server 2000 till Windows Server 2008 and from Outlook 2007 till Outlook 2016. Since Windows Server 2016 I've got an exception (The server threw an exception. (Exception from HRESULT: 0x80010105 (RPC_E_SERVERFAULT))).
But there is an alternative way to assign this property.
[Void] $mail.GetType().InvokeMember("SendUsingAccount","SetProperty",$NULL,$mail,$account)

Send mail from powershell as anonymous user

I've always used the cmdlet Send-MailMessage without specifying any -Credential. Now I need to send mail using the anonymous user. The workaround I've found is this piece of code
$pass = ConvertTo-SecureString "anyString"-AsPlainText -Force
$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "NT AUTHORITY\ANONYMOUS LOGON",$pass
Send-MailMessage -Credential $cred #...
It works, but is this the correct method to get the anonymous is and send anonymous mail?
You can send mail from any address, it just depends on whether the receiving mail server cares about whether it can verify the sending user or not.
For example if you send from anon#microsoft.com and microsoft does not have that account, OR they have SPF records to indicate whether the sending mail server is valid or not, then the receiving mail server might(not always) reject it.
Just make sure your email user actually exists and has a valid domain... and you can send from anonymous# or noreply# or whatever you want to.
Okay, from the comments we know we're dealing with an Exchange server. Whether or not it will do an anonymous relay depends on the configuration of the Recieve Connectors. But those restrictions only apply to the network connections. If you run Send-MailMessage on the Exchange server and use 'LocalHost' as your SMTPServer it didn't go through a receive connector so those restrictions don't apply.
If you have remoting enabled on the Exchange server you can use that to do a local invocation to send email without having to modify the Receive Connector configurations:
$EmailParams =
#{
To = '<Email To>'
From = '<Email From>'
Subject = '<Email Subject>'
Body = '<Email Body>'
SMTPServer = 'localhost'
}
$Scriptblock = [Scriptblock]::Create(
"Send-MailMessage $(&{$args} #EmailParams) ")
Invoke-Command -ScriptBlock $Scriptblock -ComputerName ExchangeServer

Send email via Powershell and Outlook

I have a .msg file on my filesystem. With powershell I can open a Outlook window with the message simply like this:
Invoke-Item "MY MAIL.msg"
How to change the subject and forward it to a given address via Powershell?
Thanks in advance
We had a problem that required the email to be forwarded from Outlook, there was 3000~ emails to do.
The answer Iain had given led me down the path to success, so thank you.
However it did not work for me as given, it failed. I noticed that you need to save the method of the forward to a variable and then execute the code from that, below is my complete script for looping through each msg file in a folder and forwarding it to a person.
I also left the subject as it was and gave no body as this was not needed.
#Open Outlook and get a list of emails to forward
$Outlook = New-Object -comObject Outlook.Application
$Emails = Get-ChildItem -Path C:\Users\APerson\Documents -Filter *.msg
#Loop through each email and open it up
Foreach($Email IN $Emails){
$Message = $Outlook.Session.OpenSharedItem($($Email.FullName))
$Forward = $Message.Forward()
$Forward.Recipients.Add('a.person#gmail.com')
$Forward.Send()
#Sleep is optional :D
Start-Sleep -Seconds 1
}
#Close Outlook
$Outlook.Quit()
Also noticed if you have a security policy applied to Outlook that is stopping you from running this script, for example it will remove the Add() on recipients, just import these registry settings (can be saved as a reg file):
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Policies\Microsoft\office\14.0\outlook\security]
"PromptOOMSend"=dword:00000002
"PromptOOMAddressBookAccess"=dword:00000002
"PromptOOMAddressInformationAccess"=dword:00000002
"PromptOOMMeetingTaskRequestResponse"=dword:00000002
"PromptOOMSaveAs"=dword:00000002
"PromptOOMFormulaAccess"=dword:00000002
"PromptSimpleMAPISend"=dword:00000002
"PromptSimpleMAPINameResolve"=dword:00000002
"PromptSimpleMAPIOpenMessage"=dword:00000002
You could try something like this, works with outlook 2010
$ol = New-Object -comObject Outlook.Application
gm -InputObject $ol
$mail = $ol.Session.OpenSharedItem("C:\Users\fred\Desktop\Test Email Subject.msg")
$mail.Forward()
$Mail.Recipients.Add("fred#bloggs.com")
$Mail.Subject = "Test Mail"
$Mail.Body = " Test Mail 22222 "
$Mail.Send()
In PowerShell 2.0 there is a Send-MailMessage cmdlet that allows you to attach files, specify a subject and a recipient e.g.:
Send-MailMessage -smtpServer smtp.doe.com -from 'joe#doe.com' `
-to 'jane#doe.com' -subject 'Testing' -attachment foo.txt
Not sure how that plays with .msg files but you might give it a try.