Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
This script syncs SendAs permissions from Exchange on-Prem to Exchange Online to avoid a misconfigured hybrid environment
Uses Azure Automation for scheduling and safely storing the on-Prem credentials as well as the authentication certificate for Exchange Online
* Azure Automation Account
* Hybrid Worker
* Setup App-only authentication (
* Install private certificate as exportable to Azure Automation Account as 'Exchange Hybrid Automation'
* Store OnPrem Exchange credentials in Azure Automation Account as 'Exchange onPrem'
param (
# Change to your App Id
[Parameter(Mandatory = $false)]
$AppID = "3d907615-5f82-41da-9d8a-e3c3713497b5",
# Change to your tenant name
[Parameter(Mandatory = $false)]
$Organization = "",
# Change to your on Prem Exchange server
[Parameter(Mandatory = $false)]
$ExchangeServer = ""
# Basic settings
$ConfirmPreference = "None"
$ErrorActionPreference = "Stop"
# Retrieve private certificate and onPrem credentials
$EXOCertificate = Get-AutomationCertificate -Name 'Exchange Hybrid Automation'
$OnPremCred = Get-AutomationPSCredential -Name 'Exchange onPrem'
# Get a list of all On-Prem Shared Mailboxen (msExchRecipientTypeDetails = 4) which where modified in the last two days
$NewSharedMaiboxes = Get-ADUser -LDAPFilter "(msExchRecipientTypeDetails:1.2.840.113556.1.4.803:=4)" -Properties whenChanged, EmailAddress | Where-Object { $_.whenChanged -ge (Get-Date).AddDays(-2) }
if ( -not $NewSharedMaiboxes) {
Write-Output "[SHAREDMBX]`t Found no new shared mailboxes..."
} else {
#region Export PFX certificate
# Generate password
$Password = [System.Web.Security.Membership]::GeneratePassword(25, 5)
# Export certificate as pfx file
try {
# Define temporary file
$CertificatePath = Join-Path $env:temp "Exchange Hybrid Automation.pfx"
Write-Output "[PFXCERTIFICATE]`t Certificate is exported to: $CertificatePath"
# Export PFX to variable
$PFXCertificate = $EXOCertificate.Export("pfx", $Password)
# Write content of pfx cert to file
Set-Content -Value $PFXCertificate -Path $CertificatePath -Force -Encoding Byte
Write-Output "[PFXCERTIFICATE]`t Certificate exported successfully"
} catch {
throw "Could not export certificate - ($($_.Exception.Message))"
#region Connect to Exchange on-Prem
try {
Write-Output "[EXOnPrem]`t Connecting to Exchange On-Prem ($ExchangeServer)..."
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "http://$($ExchangeServer)/powershell" -Credential $OnPremCred
Write-Output "[EXOnPrem]`t Connection established, import module..."
# Import module while using the prefix OnPremExchange to distinguish between onPrem and Online
Import-Module ( Import-PSSession -Session $session -AllowClobber -DisableNameChecking -Prefix OnPremExchange ) -Global
# Thanks to Joshua Bines who posted the way of importing the module here:
Write-Output "[EXOnPrem]`t Module successfully imported"
} catch {
throw "Exchange On-Prem connection could no be established - ($($_.Exception.Message))"
#region Verbindung zu Exchange Online herstellen
try {
Write-Output "[EXO]`t Connecting to Exchange Online..."
Connect-ExchangeOnline -LogLevel All -LogDirectoryPath C:\Service\exolog -CertificateFilePath $CertificatePath -CertificatePassword (ConvertTo-SecureString -String $Password -AsPlainText -Force) -AppID $AppID -Organization $Organization
Write-Output "[EXO]`t Connection established and module successfully imported"
} catch {
throw "Exchange Online connection could no be established - ($($_.Exception.Message))"
#region Synchronize permissions
foreach ($User in $NewSharedMaiboxes) {
# Get on-Prem mailbox for the AD User
$Mailbox = Get-OnPremExchangeMailbox $User.EmailAddress
# Retrieve every Send-As permission
# Exclude SELF and Deny
$SendAsPermissions = $Mailbox | Get-OnPremExchangeADPermission | Where-Object { $_.IsInherited -eq $false -and $_.ExtendedRights -match "Send-As" -and $_.Deny -eq $false -and "$($_.User)" -ne "NT AUTHORITY\SELF" }
# Loop through every entry and set the permission in Exchange Online
$SendAsPermissions | ForEach-Object {
try {
Write-Output "[$($User.UserPrincipalName)]`t Add recipient permission for $($_.User)"
Add-RecipientPermission -Identity $User.UserPrincipalName -Trustee $_.User -AccessRights SendAs -Confirm:$false | Out-Null
} catch {
Write-Error "[$($User.UserPrincipalName)]`t Permissions could not be added - ($($_.Exception.Message))"
# Close connections
Disconnect-ExchangeOnline -Confirm:$false
$session | Remove-PSSession -ErrorAction SilentlyContinue -WhatIf:$false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment