Problem
Aus Berechtigungsgründen konnte ESG das Mapping von Microsoft 365-delegierten Benutzern und das Mapping als sekundäre E-Mail-Adressen für Benutzer nicht verarbeiten.
ESG wird über die Graph API mit Microsoft 365 integriert, um die Benutzersynchronisierung und das Single Sign-On (SSO) zu ermöglichen. Bei diesem Vorgang werden Shared Mailboxes als 'Functional Users' im Benutzerverwaltungssystem von ESG angelegt. Eine Einschränkung der Graph API besteht darin, dass sie keine detaillierten Berechtigungsinformationen zum Zugriff auf Shared Mailboxes unterstützt. Folglich kann ESG nicht feststellen, welche Benutzer über Berechtigungen zur Nutzung bestimmter Shared Mailboxes verfügen, und kann diese daher nicht korrekt als sekundäre Adressen innerhalb der ESG-Plattform zuordnen.
Lösung :
Voraussetzungen
Um das Mapping von Shared Mailboxes zu den entsprechenden Mitgliedern in ESG zu automatisieren, müssen einige Voraussetzungen erfüllt sein:- Die Integration von ESG mit Microsoft 365 muss konfiguriert sein und funktionieren
- Es muss mindestens 1 Import in ESG über die M365-Integration durchgeführt worden sein
- Shared Mailboxes sind in ESG in der Benutzerverwaltung vorhanden
Automatisches Mapping über Powershell & ESG APIs
#Import Exchange Online module, it must be installed first here
Import-Module ExchangeOnlineManagement
Connect-ExchangeOnline -UserPrincipalName <M365 Tenant admin>
# Get all shared mailboxes
$sharedMailboxes = Get-Mailbox -RecipientTypeDetails SharedMailbox -ResultSize Unlimited
$shared_mailbox_list = @()
Get-Mailbox -RecipientTypeDetails SharedMailbox -ResultSize Unlimited | ForEach-Object {
$mailbox = $_
# Retrieve the permissions for the mailbox: filter for FullAccess (non-inherited and non-system accounts)
$permissions = Get-MailboxPermission -Identity $mailbox.PrimarySmtpAddress | Where-Object {
$_.AccessRights -contains "FullAccess" -and `
-not $_.IsInherited -and `
$_.User -notmatch "NT AUTHORITY|S-1-5-|SELF|SYSTEM"
}
# Process each permission entry
foreach ($perm in $permissions) {
$delegatedUser = $perm.User.ToString()
$isGroup = $false
# Try to determine if the delegated user is a group using Get-Recipient
try {
$recipient = Get-Recipient -Identity $delegatedUser -ErrorAction Stop
if ($recipient.RecipientType -match "Group") {
$isGroup = $true
}
}
catch {
# If not found or error occurs, assume it's not a group.
$isGroup = $false
}
if ($isGroup) {
# Expand the group: get each member using Get-DistributionGroupMember
$groupMembers = Get-DistributionGroupMember -Identity $delegatedUser -ErrorAction SilentlyContinue
if ($groupMembers) {
foreach ($member in $groupMembers) {
$shared_mailbox_list += [PSCustomObject]@{
SharedMailbox = $mailbox.DisplayName
PrimarySmtpAddress = $mailbox.PrimarySmtpAddress
ExternalObjectId = $mailbox.ExternalDirectoryObjectId
User = $member.PrimarySmtpAddress.ToString()
AccessRights = ($perm.AccessRights -join ", ")
}
}
}
}
else {
# Not a group: add one object with the delegated user identity
$shared_mailbox_list += [PSCustomObject]@{
SharedMailbox = $mailbox.DisplayName
PrimarySmtpAddress = $mailbox.PrimarySmtpAddress
ExternalObjectId = $mailbox.ExternalDirectoryObjectId
User = $delegatedUser
AccessRights = ($perm.AccessRights -join ", ")
}
}
}
}
# Display the results in a formatted table could be uncommented to see all the shared mailbox mapped.
#$shared_mailbox_list
$shared_unique = $shared_mailbox_list.PrimarySmtpAddress | Group-Object
# Number of users returned per page in each GET, 150 is the maximum chunk.
$page_size = 150
$page_num = 1
# Define the ESG API endpoint to get the list of users with maximum page size (150)
$apiUrl = "<youresgAddress>/api/v2/user?page="+$page_num+"&itemsPerPage=150"
# Define headers for the api call, use your apiToken generated
$headers = @{
'Accept' = 'application/hal+json'
'X-ESG-Auth-Token' = 'apiToken'
}
# Send the GET request
$get_users_response = Invoke-RestMethod -Uri $apiUrl -Method Get -Headers $headers
$users_list_body = $get_users_response._embedded.item
# Number of total users extracted from the GET call
$total_users = $get_users_response.totalItems
Write-Host "Total users" $total_users
# Check if number of total users is < than page size, if yes no multiple calls are needed to loop through pages
if ($total_users -lt 50){
Write-Host "user page page > total"
$users_list = $users_list_body
} else {
# Number of total users is > than page size, looping through pages to get all users
Write-Host "users per page < total, looping through pages"
while ($true){
# Check if GET request for page_num is empty, meaning we reached end of user list
if ($get_users_response.PSObject.Properties.Name -notcontains '_embedded') {
break
} else {
# Go to next page
$page_num += 1
# GET request of current page is not empty
$apiUrl = "<youresgAddress>/api/v2/user?page=" + $page_num +"&itemsPerPage=150"
# Send the GET request
$get_users_response = Invoke-RestMethod -Uri $apiUrl -Method Get -Headers $headers
# Appending list of users of each page
$users_list_body += $get_users_response._embedded.item
}
}
}
# Filter only users that are members of the Shared mailbox list
$users_list = $users_list_body| Where-Object {$shared_mailbox_list.User -contains $_.username}
Write-Host "total number of users to map: " $users_list.count
#Filter only shared mailbox , this could be also -> $shared_mailbox_list.PrimarySmtpAddress
$mailbox_list_values = $users_list_body| Where-Object {$shared_mailbox_list.SharedMailbox -contains $_.username}
Write-Host "total number of Mailbox to remap: " $mailbox_list_values.count
#delete all shared mailbox saved as functional_users
# Loop all users and check if the shared mailbox is already present as an alias, if not add the Shared Mailbox as alias
foreach ($user in $users_list){
# ID of the ESG user
$id = $user.id
Write-Host "Esg user id:" $id
$apiUrl = "<youresgAddress>/api/v2/user/$id"
# Loop each Shared mailbox
foreach($shared in $shared_mailbox_list){
$match = $user.emailAddresses | Where-Object {
$_.PSObject.Properties['address'].Value -eq $shared.User
}
if ($match){
$shared_mailbox = [PSCustomObject]@{
address = $shared.PrimarySmtpAddress
active = $true
primary = $false
}
$headers = @{
"Accept" = "application/hal+json"
"Content-Type" = "application/merge-patch+json"
"X-ESG-Auth-Token" = "apiToken"
}
$user.emailAddresses = $user.emailAddresses + $shared_mailbox
$body = [PSCustomObject] @{ emailAddresses = $user.emailAddresses } | ConvertTo-Json
$patch_response = Invoke-RestMethod -Uri $apiUrl -Method Patch -Headers $headers -Body $body
}
}
}
#delete all the shared mailbox that are functional users. Iterate all functional_users
foreach($functional in $mailbox_list_values)
{
$id = $functional.id
$apiUrl = "<youresgAddress>/api/v2/user/$id"
# Define headers
$headers = @{
'Accept' = 'application/json'
'X-ESG-Auth-Token' = 'apiToken'
}
$response = Invoke-RestMethod -Uri $apiUrl -Method Get -Headers $headers
#Functional user role, is it needed to check if the retrieved user is functional, il that case it will remove it (After having remapped it)
$functionalUser = "/api/v2/user-role/499"
$otherFunctionalUser = "/api/v2/user-role/400"
if($response.role -eq $functionalUser)
{
$apiUrl = "<youresgAddress>/api/v2/user/$id"
$apiUrl
# Define headers
$headers = @{
'Accept' = 'application/json'
'X-ESG-Auth-Token' = 'apiToken'
}
$response = Invoke-RestMethod -Uri $apiUrl -Method DELETE -Headers $headers
Write-Host "Deleting the user ..."
}
if($response.role -eq $otherFunctionalUser)
{
$apiUrl = "<youresgAddress>/api/v2/user/$id"
$apiUrl
# Define headers
$headers = @{
'Accept' = 'application/json'
'X-ESG-Auth-Token' = 'apiToken'
}
$response = Invoke-RestMethod -Uri $apiUrl -Method DELETE -Headers $headers
Write-Host "Deleting the user ..."
}
}
Write-Host "all shared mailbox are now mapped"