Powershell- Scripting an email from Exchange - email

I have looked all over the web and cannot find exactly what I am looking for. I am trying to write a Powershell (V2) script that emails a file using our internal Exchange server, but doesn't require Outlook. I have a user account to use for this, but I don't have Outlook available for the server it runs on. Can someone either provide a script (Or even a method) that allows me to send an email with a specified attachment, using an Exchange mailbox?
Thanks!

You can use the Send-MailMessage cmdlet. Type this at your console for more help:
Get-Help Send-MailMessage -Full
Check the second code example at the examples section:
Get-Help Send-MailMessage -Examples

This should do the trick but is missing the attachment. That shouldn't be hard to add.
function submit_report_smtp{
param($report)
trap{return 1}
$smtp_client = New-Object system.Net.Mail.SmtpClient
$smtp_client.Host = $smtp_host
$credentials = New-Object system.Net.NetworkCredential
$credentials.UserName = $smtp_user
$credentials.Password = $smtp_pass
$smtp_client.Credentials = $credentials
$smtp_client.send($smtp_from, $smtp_to, $title,$report)
return 0
}

Related

How to Fix PowerShell Script from Basic Auth to Modern Auth

I have a PowerShell script that, after pinging a server address, uses Basic Auth to send an automated email via Task Scheduler. Microsoft has deprecated Basic Auth in Exchange Online in favor of Modern Auth, but I do not see clear directions for updating a PowerShell script to use Modern Auth.
This is an example of the Basic Auth that I need to convert.
$secpasswd = ConvertTo-SecureString “password” -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential (“user#place.com”, $secpasswd)
Send-MailMessage -SmtpServer smtp.office365.com -Port 587 -From user#place.com -To otheruser#place.com -Subject test -Body test -Credential $mycreds -UseSsl"
Can someone point me to an example of Modern Auth being used in a similar script or share what I need to do to update and run the above script?
Many Thanks!
As per the resources in my original comment.
Send-MailMessage is obsolete and no longer supported. Microsoft says this cmdlet does not guarantee a secure connection to SMTP servers. As per:
https://aka.ms/SendMailMessage
Therefore use the below:
Send-MgUserMail - MS Docs
Note: Send-MgUserMail requires a more complex parameter structure.
$EmailMessageContent=#'
<Strong> This is a Test Message</Strong><br>
Modern auth testing
'#
$params = #{
Message = #{
Subject = "Using MSGraph"
Body = #{
ContentType = "html"
Content = $EmailMessageContent
}
ToRecipients = #(
#{
EmailAddress = #{
Address = "SomeRecipientEmaiAddress"
}
}
)
}
}
Import-Module Microsoft.Graph.Users.Actions
Connect-MgGraph -Scopes Mail.Read
Send-MgUserMail -UserId 'SomeSenderEmailAddress' -BodyParameter $params
Point of note:
SMTP AUTH will still be available when Basic authentication is permanently disabled on October 1, 2022. The reason SMTP will still be available is that many multi-function devices such as printers and scanners can't be updated to use modern authentication.
See also the below for full details of the why and so on... (and more sample code):
Authenticate an IMAP, POP or SMTP connection using OAuth
Moving on from Send-MailMessage: Sending Email from PowerShell using
the Graph API
The Send-MailMessage Conundrum
Largely because of history, Exchange Online supports a wide variety of
connectivity protocols. Microsoft is making some progress to convince
customers to disable basic authentication for protocols they never
use, and has upgraded older protocols like POP3 and IMAP4 to use OAuth
2.0 for modern authentication. As discussed in this blog, tenants will need to find PowerShell scripts which call the Send-MailMessage cmdlet
and eventually upgrade the code with a more modern method to send
email.
The Send-MailMessage cmdlet depends on the SMTP AUTH protocol to send
email using basic authentication. Microsoft announced OAuth 2.0
support for SMTP AUTH in April 2020, but this doesn’t mean that an
off-the-shelf replacement cmdlet is available. Microsoft says that the
announcement “is for interactive applications to enable OAuth for IMAP
and SMTP [AUTH].” In effect, this means mail clients or other
applications which send, read, or otherwise process email. A quick
trip to the referenced page leaves no doubt that this means more than
replacing a few lines of code in a PowerShell script.
Thanks #postanote for your suggestions, the fix eventually came down to this:
from user#place.com I had to drop the 'place.com' and just keep the alias
with -SmtpServer smtp.office365.com I had to drop 'office365.com' and replace with '[uni].edu'

What is the best way to store account credentials (especially password) for an automated email script?

I am writing a simple script (windows powershell) to send automated emails. A computer will be running at all times collecting data and then sending emails at regular intervals. Part of sending an email obviously is collecting credentials, and since its automated we cant have somebody there everytime to enter the user and password. The obvious solution is to just store info in $user and $pass vars in the script but this seems horribly unsafe to me, and prone to attacks. Is there any better way to do this? Maybe setup a secure email connection once with the user address and not have to enter it in again? I am new to powershell so im not really clear on the best ways to do this. Currently what I am doing is:
$from = 'UserEmail#anotherEmail.com'
$to = 'someEmail#SomeMail.com'
$smtpServer = 'smtp-mail.outlook.com'
$smtpPort = '587'
$mailSubject = 'PowerShell Script Email Test'
$password = 'p#ssword'
$mailBody = 'body text'
$credentials = New-Object System.Management.Automation.PSCredential -ArgumentList $from, $($password | ConvertTo-SecureString -AsPlainText -Force)
Send-MailMessage -To "$to" -From "$from" -Subject $mailSubject -SmtpServer $smtpServer -UseSsl -Credential $credentials -BodyAsHtml -Body $mailBody
Any advice or documentation to read would be much appreciated
You may want to investigate the Protect-CMSMessage cmdlet, which allows you to encrypt/decrypt data using public key cryptography so that only users with the correct certificate would be able to decrypt the password.
If that seems like overkill, another, easier but possibly less secure, option is to export the credentials to XML and read them back when required.
To create the file, do this:
Log on as the user the script will be running as
Execute this command: Get-Credential | Export-CliXml <path>\cred.xml
When prompted enter the username/password to be used in the script
The resulting XML file will have the username and password securely stored and can be read back like this:
$cred = Import-CliXml <path>\cred.xml
You can then pass $cred to any cmdlet that has a -Credential parameter.
The password is encrypted in such a way that it can only be opened by the same user on the same computer, so if someone else opens it they won't be able to access the details. Obviously, if they can log on as the user who encrypted it (or convince that user to run a 'bad' script), then they will have access to the details, but otherwise this is pretty secure.
A third option is to use the built-in Credential Manager in Windows. This needs some complicated .NET interop for older systems, but luckily some nice person has already done the hard work for you:
PowerShell Credentials Manager
This is a bit easier in Windows 10:
PasswordVault Class

Finding mailbox server

I'm trying to write down a powershell script that will automatically finds the mailbox server and connect to it (using the URI https://mailboxserver/powershell).
Problem is I haven't found a way to automatically detect a mailbox server for a given exchange organization. I found a way how to find the CAS server because someone posted how the outlook finds this manually.
I tried to query AD but I do not know which attribute is unique to exchange mailbox server.
I also tried DNS records but found none which helps.
Does anybody know about a unique value of mailbox server which could be queried from AD or GC? Or a DNS record or something else I have not thought of?
Exchange 2010
I could post forest and domain functional level if necessary but I am on the way.
Thanks in advance
Your AD user attributes have this information, albeit you have to parse the mailbox server name from them.
HomeMTA
msExchHomeServerName
So if you have access to the AD cmdlets you might be able to get your mailbox server this way.
$adUser = get-aduser someuser -Properties msExchHomeServerName
$mailboxServerName = ($aduser.msExchHomeServerName -split "cn=")[-1]
Those attributes help you find your current mailbox is hosted. The mailbox server in my case was the last "item" in msExchHomeServerName so I split the string on "cn=" and then the last element of that array would be my mailbox server name.
Then you can use that to connect to an Exchange session!
$Credentials = Get-Credential
$exchangePath = "http://$mailboxServerName/PowerShell/?SerializationLevel=Full"
$ExSession = New-PSSession –ConfigurationName Microsoft.Exchange –ConnectionUri $exchangePath -Credential $Credentials –Authentication Kerberos
Import-PSSession $ExSession
Does the code below get you what you need? It uses EWS - see my SO post for further details on EWS.
# load the assembly
[void][Reflection.Assembly]::LoadFile("D:\Temp\Microsoft.Exchange.WebServices.2.2\lib\40\Microsoft.Exchange.WebServices.dll")
# set ref to exchange - may need to change the version
$s = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2)
# replace with your email address
$email = "your.email#domain.com"
# grab your own credentials
$s.UseDefaultCredentials = $true
# discover the url from your email address
$s.AutodiscoverUrl($email)
$s.Url.AbsoluteUri

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

PowerShell, Exchange 2010 Addressbook

It is possible to access Exchange 2010 Addressbook from a PowerShell Script which is running on a client?
I want to access the addressbook, search by properties and work with the results.
I have not found any tutorial for the EWS and PowerShell.
[Reflection.Assembly]::LoadFrom("path to ews.dll")
$ExchangeService = new-object ExchangeServiceBinding
$paramName = New-Object UserConfigurationNameType
$paramName.Item = New-Object FolderIdType
$paramName.Name = "CategoryList"
$params = New-Object GetUserConfigurationType
$params.UserConfigurationName = $paramName
$params.UserConfigurationProperties = [UserConfigurationPropertyType]::ALL
$ExchangeService.UseDefaultCredentials
$ExchangeService.Url = "https://path.to.exchange/EWS/Exchange.asmx"
$ExchangeService.GetUserConfiguration($params)
I don't know about PowerShell, but you can accomplish this in Exchange Management Shell (EMC). PowerShell v2.0+ can run remote sessions, so the EMC commands can be used from your clients. Of course they'll need some Exchange rights to do this. Conveniently in Exchange 2010, RBAC allows you to give miniscule Exchange rights to your users. If this is not an option, you could do an LDAP query (that's what Outlook does) but I'm not sure of the exact procedure.
However, if it is an option:
1. Initiate your remote PowerShell session.
1a. $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionURI http://FQDNofCAS/PowerShell/ -Authentication Kerberos
1b. Import-PSSession $session
After that, try one of the following:
1. Get-GlobalAddressList
1b. Note the GAL you'll be using
2. $GAL = (Get-GlobalAddressList "Default Global Address List").DistinguishedName
2b. Replace _Default GAL_ with the output of step one.
3. Get-GlobalAddressList $GAL | Update-GlobalAddressList
4. Get-Recipinet -Filter {Addresslistmembership -eq $GAL}
4b. -Filter may require some tweaking to your specifics.
Note: See http://www.msexchange.org/articles_tutorials/exchange-server-2007/management-administration/address-lists-exchange-2007-part1.html for a better explanation of this.
--OR--
1. Get-User | where($_.RecipientType -like "*Mail*"}
Note: This will show all Mail-Enabled users, so it might not be exactly what you're looking for.
You need the Exchange EWS Managed API:
http://msdn.microsoft.com/en-us/library/dd637749.aspx