Exchange Online Custom RBAC Role for AWS

Exchange Online Custom RBAC Role for AWS

Hi All,

I’ve had an interesting Use Case some Weeks ago.

The collegues from the AWS Cloud Team wanted to create a Mailenabled Security List for each Application they create. So they can inform the right People and i guess also assign permissions to these People.

I expected, that this will be an easy task. Just a few Microsoft Graph Commands to create a Group and add Members. But i was very wrong. As soon as a Group has an Emailaddress, the Object needs to be managed via “ExchangeOnlineManagement” PowerShell.

Security groups are for controlling user access to resources. By checking whether a user is a member of a security group, your app can make authorization decisions when that user is trying to access some secure resources in your app. Security groups can have users, other security groups, devices, and service principals as members.

Mail-enabled security groups are used in the same way as security groups, but can be used to send emails to group members. Mail-enabled security groups can’t be created or updated through the API; instead, they’re read-only. Learn more in the Manage mail-enabled security groups Exchange article.

Solution

We came up with the following Solution:

  • Create an Entra ID App
  • Assign Exchange.ManageAsApp
  • Restrict the App with a specific Recipient Filter (Group needs to start with AAD-AWS)
  • Restrict the APP with a customized RBAC Role
    • Create/Modify/Delete Distribution Group
    • Add/Remove Group Members

This Blog Article is based on:

Create EntraID Application

First we need to create the Entra ID Application with the Microsoft Graph PowerShell Modules

###############################################################################
# Connect-MgGraph
# Requires EntraID "Application Adminstrator" or "Global Administrator" Role
###############################################################################
Connect-MgGraph -Scopes "Application.Read.All","Application.ReadWrite.All","User.Read.All"

###############################################################################
# Create EntraID Application
###############################################################################
$AppName =  "EXO-DistGroup-RBAC"
$App = New-MgApplication -DisplayName $AppName
$APPObjectID = $App.Id
$AppID = $App.AppId

Prepare the Self-Signed Certificate for Certificate Based Authentication (CBA) with Exchange Online

###############################################################################
# Create a Self Signed Certificate
###############################################################################
$Subject = "EXO-DistGroup-RBAC"
$NotAfter = (Get-Date).AddMonths(+24)
$Cert = New-SelfSignedCertificate -Subject $Subject -CertStoreLocation "Cert:\CurrentUser\My" -KeySpec Signature -NotAfter $Notafter -KeyExportPolicy Exportable
$ThumbPrint = $Cert.ThumbPrint

Add the Certificate to the Entra ID Application

###############################################################################
# Add Certificate to Entra ID Application
###############################################################################
# Get Certificate with Thumbprint from UserCertStore
$Cert = Get-ChildItem -Path cert:\CurrentUser\my\$ThumbPrint 

# Create a keyCredential (Certificate) for App
$keyCreds = @{ 
    Type = "AsymmetricX509Cert";
    Usage = "Verify";
    key = $cert.RawData
}
Update-MgApplication -ApplicationId $APPObjectID  -KeyCredentials $keyCreds

Add the Exchange.ManageAsApp Permission

###############################################################################
# Add Entra ID Application Permissions
###############################################################################
$params = @{
	RequiredResourceAccess = @(
		@{
			ResourceAppId = "00000002-0000-0ff1-ce00-000000000000"
			ResourceAccess = @(
				@{
					Id = "dc50a0fb-09a3-484d-be87-e023b12c6440"
					Type = "Role"
				}
			)
		}
	)
}
Update-MgApplication -ApplicationId $APPObjectID -BodyParameter $params

Grant Admin Consent with Browser Sign-In

###############################################################################
# Grant Admin Consent - Opens URL in Browser
###############################################################################
#https://login.microsoftonline.com/{tenant-id}/adminconsent?client_id={client-id}
$TenantID = $App.PublisherDomain
$URL = "https://login.microsoftonline.com/$TenantID/adminconsent?client_id=$AppID"
Start-Process $URL

It will open a Browser Window where you need to Sign-In

And Grant the Permissions

Application

This is the resulting Entra ID Application

Create Exchange RBAC Role and Management Scope

Now we need to Login to Exchange with the Exchange Administrator Role to prepare the Exchange Online RBAC Role and Management Scope.

###############################################################################
# Connect-ExchangeOnline
###############################################################################
Connect-ExchangeOnline

First we create the Management Scope. Only Objects with the DisplayName that Starts with “AAD-AWS” can be managed.

###############################################################################
# ManagementScope
###############################################################################
$RecipientFilter = "(DisplayName -like 'AAD-AWS*')"
Get-Recipient -RecipientPreviewFilter $RecipientFilter
New-ManagementScope -Name "AAD-AWS" -RecipientRestrictionFilter $RecipientFilter

We need to figure out the best built-in role to use as a parent for our new custom RBAC Role.

You will need both ManagementRoles “Security Group Creation and Membership” and “Distribution Groups” because of the Confirm Parameter.

###############################################################################
# Management Role
###############################################################################
Get-ManagementRole  | where {$_.Name -like "*group*"}
Get-ManagementRoleEntry -Identity "Security Group Creation and Membership\*"
Get-ManagementRoleEntry -Identity "Distribution Groups\*"

Let’s create the RBAC Role with the Parent.

###############################################################################
# New-ManagementRole
###############################################################################
$ManagementRoleName01 = "CUSTOM-AWS-SecurityGroup"
New-ManagementRole -Parent "Security Group Creation and Membership" -Name $ManagementRoleName01

List the Management Role Entrys

###############################################################################
# Get-ManagementRoleEntry
###############################################################################
Get-ManagementRoleEntry -Identity "$ManagementRoleName01\*"

Remove unneeded cmdlets from our new RBAC Role

###############################################################################
# Remove unneeded commands
###############################################################################
Remove-ManagementRoleEntry -Identity "$ManagementRoleName01\Get-EligibleDistributionGroupForMigration" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName01\Get-OrganizationalUnit" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName01\Get-UnifiedAuditSetting" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName01\Set-Group" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName01\Set-UnifiedAuditSetting" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName01\Write-AdminAuditLog" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName01\Start-AuditAssistant" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName01\Get-Mailbox" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName01\Get-SweepRule" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName01\Get-Recipient" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName01\Get-DynamicDistributionGroupMember" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName01\Test-DatabaseEvent" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName01\Get-ScopeEntities" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName01\Get-ScopeAdmins" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName01\Test-MailboxAssistant" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName01\Get-BookingMailbox" -Confirm:$false

Check the Resulting RBAC Role

###############################################################################
# Check-ManagementRoleEntry after removal
###############################################################################
Get-ManagementRoleEntry -Identity "$ManagementRoleName01\*"

###############################################################################
# New-ManagementRole 
###############################################################################
$ManagementRoleName02 = "CUSTOM-AWS-DistributionGroup"
New-ManagementRole -Parent "Distribution Groups" -Name $ManagementRoleName02

###############################################################################
# Get-ManagementRoleEntry
###############################################################################
Get-ManagementRoleEntry -Identity "$ManagementRoleName02\*"

###############################################################################
# Remove unneeded commands
###############################################################################
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Get-EligibleDistributionGroupForMigration" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Get-AcceptedDomain" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Get-BookingMailbox" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Test-MailboxAssistant" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Get-ScopeAdmins" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Get-ScopeEntities" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Set-Group" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Set-DistributionGroup" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\New-DistributionGroup" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Test-DatabaseEvent" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Set-DynamicDistributionGroup" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Get-DynamicDistributionGroupMember" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\New-DynamicDistributionGroup" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Get-DistributionGroup" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Write-AdminAuditLog" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Stop-ExoJob" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Start-AuditAssistant" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Set-UnifiedAuditSetting" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Set-OrganizationConfig" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Remove-DynamicDistributionGroup" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Remove-DistributionGroup" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Remove-ExoJob" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Receive-ExoJob" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Get-User" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Get-UnifiedAuditSetting" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Get-Recipient" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Get-OrganizationalUnit" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Get-MailboxPreferredLocation" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Get-Mailbox" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Get-MailUser" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Get-Group" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Get-ExoJob" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Get-DynamicDistributionGroup" -Confirm:$false
Remove-ManagementRoleEntry -Identity "$ManagementRoleName02\Get-DistributionGroupMember" -Confirm:$false

###############################################################################
# Check-ManagementRoleEntry after removal
###############################################################################
Get-ManagementRoleEntry -Identity "$ManagementRoleName02\*"

Finally we need to get the Details for creating the Service Principal in Exchange Online

###############################################################################
# Get AzureAD Application with Microsoft.Graph PowerShell
###############################################################################
# Connect-MgGraph -Scopes 'Application.Read.All'
$Filter = "DisplayName eq '" + $AppName + "'"
$ServicePrincipalDetails = Get-MgServicePrincipal -Filter $Filter
$ServicePrincipalDetails
$ServicePrincipalID = $ServicePrincipalDetails.ID

Create the Service Principal in Exchange Online.

###############################################################################
# Create Exchange Service Principal
###############################################################################
New-ServicePrincipal -AppId $AppId -ObjectId $ServicePrincipalID -DisplayName "EXO Serviceprincipal $AppName"
Get-ServicePrincipal | where {$_.AppId -eq "$AppID"}

Create the RoleGroup and assingn the new RBAC Role with the Management Scope to the Service Principal

###############################################################################
# New-RoleGroup
###############################################################################
$SP = Get-ServicePrincipal | where {$_.AppId -eq $AppID}
$ServiceId = $SP.ServiceId
New-RoleGroup -Name 'Icewolf-AWS-Group' -Roles $ManagementRoleName01,$ManagementRoleName02 -CustomRecipientWriteScope "AAD-AWS" -Description "Custom Role for AWS Mailenabled Security Group"
Add-RoleGroupMember -Identity "Icewolf-AWS-Group" -Member $ServicePrincipalID

Testing the Solution

Time to put our Solution at a Test

###############################################################################
# Connect-ExchangeOnline
###############################################################################
$AppID = "c292695d-13c4-4cb1-9378-66b9d91e73d2"
$CertificateThumbprint = "8C4BF617BF6633170F0F47C9AC16B8E8835F6E25"
$TenantID = "icewolfch.onmicrosoft.com"
Connect-ExchangeOnline -AppId $AppID -CertificateThumbprint $CertificateThumbprint -Organization $TenantID -ShowBanner:$false

Get-Module
Get-Command -Module tmpEXO_c2fvmgy4.frb

Now let’s try to create Mail Enabled Security Groups that matches / not matches the ManagementScope

###############################################################################
# Testing Group Manipulation
###############################################################################
#Group that matches ManagementScope
New-DistributionGroup -Name "AAD-AWS-DEMO" -Type Security

#Group that does not match the ManagementScope
New-DistributionGroup -Name "AAD-XXX-DEMO" -Type Security

Now let’s add and remove Distribution Group Members

# Add-/Get-/Remove-DistributionGroupMember
Add-DistributionGroupMember -Identity "AAD-AWS-DEMO" -Member m.muster@icewolf.ch -BypassSecurityGroupManagerCheck
Get-DistributionGroupMember -Identity "AAD-AWS-DEMO" 
Remove-DistributionGroupMember -Identity "AAD-AWS-DEMO" -Member m.muster@icewolf.ch -BypassSecurityGroupManagerCheck -Confirm:$false
Get-DistributionGroupMember -Identity "AAD-AWS-DEMO" 

# Remove Distribution Group
Remove-DistributionGroup -Identity "AAD-AWS-DEMO" -BypassSecurityGroupManagerCheck -Confirm:$false

# Disconnect from Exchange Online
Disconnect-ExchangeOnline -Confirm:$false

Summary

I think it’s a well designed Solution that gives the AWS Cloud Team exactly the right permissions to do the Job they need to. I guess that also others from the Community can benefit from this Solution. This Article has all the Scripts needed to automate the entire Process.

Regards
Andres Bohren

Exchange Logo