# Create an Opt-Out Tool for Exchange Online

•
•
•
•
•
•

Earlier this week, a question popped up on a distribution list for managing an opt-out process with Exchange Online.  That wasn’t the first request I’ve seen for such a tool (in fact, I had my own customer asking for something similar).  Of course, it’s much more fun to solve someone else’s problems than your own, so I dove right in.

Let’s imagine the scenario: we have a mailing list that we’re going to populate with email addresses, and it’s going to periodically send out from an Exchange Online mailbox. From time to time, recipients may choose to stop receiving messages and you want an automated way to process those.  While we’re not really fans of using Exchange Online to perform bulk mailing, I’m not going to be the one to tell you how to use your subscription.

The pieces that we’re going to configure:

1. An Exchange Online mailbox to receive the notifications.  Let’s call it unsubscribe@contoso.com.
2. A distribution group called do-not-send@contoso.com.
3. A service account. This can be the unsubscribe mailbox. It will need certain permissions in order to function correctly.
4. An Exchange Online Transport Rule (ETR).
5. A script similar to the one I provide.

## Exchange Online Mailbox

Honestly, I hope this part is pretty self-explanatory.  Create a mailbox that will be the recipient of the opt-out messages.  For purposes of the script, it can’t be a shared mailbox, since we will need to log on to it directly. In a future post, I will revisit this topic for processing a mailbox using impersonation.

1. From Exchange Management Shell:
New-RemoteMailbox -Name ‘Unsubscribe’ -UserPrincipalName ‘unsubscribe@contoso.com’

## Distribution Group

1. From Exchange Management Shell:
New-DistributionGroup -Name ‘Do Not Send’ -PrimarySmtpAddress ‘do-not-send@contoso.com’

## Service Account

The script will need to run as a user account.  It can be a user account that already exists in your organization or you can use the unsubscribe user account.  It will need to have “Account Operator” permissions in Active Directory or be delegated permissions to manage the distribution group.

## Exchange Online Transport Rule

Now that you have a mailbox that you can send to, a distribution group of people who have chosen to opt out, and an account to run it all, you need to rule to handle the processing.  Something like this should do the trick:

1. From the Exchange Management Shell:
New-TransportRule -Name “Delete messages sent to do-not-send list” -DeleteMessage:$True -SentToMemberOf do-not-send@contoso.com ## Script In order to achieve the “unsubscribe” effect, we’re going to run a script to add users to a group (that gets synchronized to Exchange Online via AADConnect) that has an ETR that drops messages matching the recipients of said group. The script will connect to the “unsubscribe” mailbox, download the messages, and then loop through them, checking for your key unsubscribe words, log the matches, and then delete the original message. The -ArgumentList 10 parameter will just grab the firs 10 messages. You may want to adjust this based on how active your mailbox is or any other number of factors. <# .SYNOPSIS Sample Office 365 Opt-Out mailbox processing .DESCRIPTION Read an Office 365 mailbox and exectute a script based on an email #> # Locating EWS Managed API and loading Write-Host -Fore Yellow "Locating EWS installation ..." If (Test-Path 'C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll') { Write-Host -ForegroundColor Green "Found Exchange Web Services DLL."$WebServicesDLL = "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll"
Import-Module $WebServicesDLL } ElseIf ($filename = Get-ChildItem 'C:\Program Files' -Recurse -ea silentlycontinue | where { $_.name -eq 'Microsoft.Exchange.WebServices.dll' }) { Write-Host -ForegroundColor Green "Found Exchange Web Services DLL at$($filename.FullName)."$WebServicesDLL = $filename.FullName Import-Module$WebServicesDLL
}
ElseIf
(!(Test-Path 'C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll'))
{
Write-Host -ForegroundColor Yellow "This requires the Exchange Web Services Managed API. Attempting to download and install."
msiexec /i EwsManagedApi.msi /qb
Sleep 60
If (Test-Path 'C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll')
{
Write-Host -ForegroundColor Green "Found Exchange Web Services DLL."
$WebServicesDLL = "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll" Import-Module$WebServicesDLL
}
Else
{
Break
}
}
Function ConnectToExchangeOnPrem()
{
$ExOnPremSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://$ExchangeServer/PowerShell/ -Authentication Kerberos
Import-PSSession $ExOnPremSession -AllowClobber -ErrorAction SilentlyContinue } # End ConnectToExchangeOnPrem Function$user = "unsubscribe@contoso.com"
$userPass = "Password123"$SuccessLogFile = "Success.txt"
$ExchangeServer = "myonpremExchangeServer"$ErrorLogFile = "Error.txt"
$OptOutGroup = "testgroup1"$securePassword = ConvertTo-SecureString $userPass -AsPlainText -Force$Credential = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist $user,$securePassword
$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2007_SP1$Service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)$creds = New-Object System.Net.NetworkCredential($user,$userPass)
$Service.Credentials =$creds
$Service.AutodiscoverUrl($user, {$true})$results = $Service.FindItems("Inbox",( New-Object Microsoft.Exchange.WebServices.Data.ItemView -ArgumentList 10 )) ConnectToExchangeOnPrem$DistributionGroupMembers = Get-DistributionGroupMembers $OptOutGroup$results.Items | ForEach-Object {
$Subject =$_.Subject
$SenderAddress = ($_.From).Address
$SenderName = ($_.From).Name
Write-Host -NoNewline "Subject   : ";Write-Host -Fore Green $Subject Write-Host -NoNewline "Sender : ";Write-Host -Fore Green$SenderAddress
Write-Host -NoNewLine "SenderName: ";Write-host -fore Cyan $SenderName If ($Subject -like "*remove*")
{
If (Get-MailContact $SenderAdddress -ea SilentlyContinue) { If ($DistributionGroupMembers.EmailAddresses.AddressString -match $SenderAddress) { "$(Sender) already member of group."
}
Else
{
Set-MailContact -Identity $SenderAddress -HiddenFromAddressListsEnabled$true
Add-DistributionGroupMember -Identity $OptOutGroup -Member$SenderAddress
$Data = """" +$SenderAddress + """" + "," + """" +"Added to $($OptOutGroup)" + """"
$Data | Out-File$SuccessLogFile -Append
$Data =$null
}
}
Else
{
New-MailContact -Name $SenderName -ExternalEmailAddress$SenderAddress
Set-MailContact -Identity $SenderAddress -HiddenFromAddressListsEnabled$true
Add-DistributionGroupMember -Identity $OptOutGroup -Member$SenderAddress
$Data = """" +$SenderAddress + """" + "," + """" +"Added to $($OptOutGroup)" + """"
$Data | Out-File$SuccessLogFile -Append
$Data =$null
}
Write-Host -Fore Green "Deleting message $($Subject) from user $($SenderAddress)."
$_.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::HardDelete) } Else {$Data = """" + $SenderAddress + """" + "," + """" +"did not match criteria to be added to$($OptOutGroup)" + """"$Data | Out-File $ErrorLogFile -Appen Write-Host -Fore Green "Deleting non-matching message$($Subject) from user$($SenderAddress)."$_.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::HardDelete)
}
}

Helping companies conquer inferior technology since 1997. I spend my time developing and implementing technology solutions so people can spend less time with technology. Specialties: Active Directory and Exchange consulting and deployment, Virtualization, Disaster Recovery, Office 365, datacenter migration/consolidation, cheese.

1. Jesse says:

I am looking at doing something similar at our organization, but wit on-premise Exchange 2016. All the steps would be the same, save for the script. Obviously, it would need some modification and I’m assuming the paths would just need to be changed pointing to the relevant Exchange resources. Am I thinking correctly on this?

Thanks!

1. Aaron Guilmette says:

Yes. Just specify the correct on-prem endpoint. You’ll probably need to change the auth type to Kerb instead of Basic.

2. Clandis Smith says:

I am trying to get this to work. I have it where the script will change groups but eveyone in the group still gets the messages except for me.

1. Aaron Guilmette says:

Are the users you are attempting to add to the group internal / tenant Exchange users or people external to your organization?

2. Joe Jarosz says:

Hi Clandis, will this work with external users?

This site uses Akismet to reduce spam. Learn how your comment data is processed.