Public/Install-ByttEmail.ps1
<# .DESCRIPTION Installs Bytt.Email in the signed in tenant .SYNOPSIS Installs Bytt.Email in the signed in tenant .EXAMPLE Install-ByttEmail .EXAMPLE Install-ByttEmail -UpdateAUCriteria .EXAMPLE Install-ByttEmail -AdminUnitName "Custom Admin Unit Name" .EXAMPLE Install-ByttEmail -GroupNamingPrefix "Custom Prefix - " -GroupNamingSuffix " - Custom Suffix" .PARAMETER UpdateAUCriteria If the Bytt.Email administrative unit already exists, update its membership rule to match the expected criteria. .PARAMETER AdminUnitName The name of the administrative unit to create or update. Default is "Bytt.Email". .PARAMETER GroupNamingPrefix The prefix to use for naming the pattern groups. Default is "Bytt.Email pattern - .PARAMETER GroupNamingSuffix The suffix to use for naming the pattern groups. Default is an empty string. #> function Install-ByttEmail { [CmdletBinding()] Param( [Parameter(Mandatory = $false)] [Switch]$UpdateAUCriteria, [Parameter(Mandatory = $false)] [string]$AdminUnitName = "Bytt.Email", [Parameter(Mandatory = $false)] [string]$GroupNamingPrefix = "Bytt.Email pattern - ", [Parameter(Mandatory = $false)] [string]$GroupNamingSuffix = "" ) Begin { } Process { $fortytwoUniverseAppId = "2808f963-7bba-4e66-9eee-82d0b178f408" if (![string]::IsNullOrEmpty($ENV:FORTYTWO_UNIVERSE_APP_ID)) { $fortytwoUniverseAppId = $ENV:FORTYTWO_UNIVERSE_APP_ID } $byttEmailAppId = "34ee8edb-d2ff-4ee9-bac3-73b53303e00f" if (![string]::IsNullOrEmpty($ENV:BYTT_EMAIL_APP_ID)) { $byttEmailAppId = $ENV:BYTT_EMAIL_APP_ID } Write-Host "Signing in to Microsoft Graph..." Connect-MgGraph -Scope Application.ReadWrite.All, AppRoleAssignment.ReadWrite.All, Group.ReadWrite.All, AdministrativeUnit.ReadWrite.All, DelegatedPermissionGrant.ReadWrite.All -NoWelcome Write-Host "Checking Enterprise Application for Fortytwo Universe..." -NoNewline $fortytwoUniverseApp = Get-MgServicePrincipal -Filter "appId eq '$fortytwoUniverseAppId'" -ErrorAction SilentlyContinue if (!$fortytwoUniverseApp) { Write-Host -ForegroundColor Green " [Creating... " -NoNewline $fortytwoUniverseApp = New-MgServicePrincipal -AppId $fortytwoUniverseAppId Start-Sleep -Seconds 3 Write-Host -ForegroundColor Green " Done! (objectid $($fortytwoUniverseApp.Id))]" Write-Verbose "Enterprise Application for Fortytwo Universe created with objectid $($fortytwoUniverseApp.Id)." } else { Write-Host -ForegroundColor Green " [OK]" } Write-Host "Checking Enterprise Application for Bytt.Email..." -NoNewline $byttEmailApp = Get-MgServicePrincipal -Filter "appId eq '$byttEmailAppId'" -ErrorAction SilentlyContinue if (!$byttEmailApp) { Write-Host -ForegroundColor Green " [Creating... " -NoNewline $byttEmailApp = New-MgServicePrincipal -AppId $byttEmailAppId Start-Sleep -Seconds 3 Write-Host -ForegroundColor Green " Done! (objectid $($byttEmailApp.Id))]" Write-Verbose "Enterprise Application for Bytt.Email created with objectid $($byttEmailApp.Id)." } else { Write-Host -ForegroundColor Green " [OK]" } Write-Host "Adding admin consent for user.read to Fortytwo Universe..." -NoNewline $microsoftGraph = Get-MgServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'" -ErrorAction SilentlyContinue $graphPermissions = Get-MgOauth2PermissionGrant -Filter "consentType eq 'AllPrincipals' and resourceId eq '$($microsoftGraph.Id)'" -All $match = $graphPermissions | Where-Object { $_.ClientId -eq $fortytwoUniverseApp.Id -and $_.Scope -eq "user.read" } if (!$match) { $grant = New-MgOauth2PermissionGrant -ClientId $fortytwoUniverseApp.Id -ConsentType "AllPrincipals" -PrincipalId $null -ResourceId $microsoftGraph.Id -Scope "user.read" Write-Host -ForegroundColor Green " [OK]" } else { Write-Host -ForegroundColor Green " [OK]" } Write-Host "Adding admin consent for user.read to Bytt.Email..." -NoNewline $microsoftGraph = Get-MgServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'" -ErrorAction SilentlyContinue $graphPermissions = Get-MgOauth2PermissionGrant -Filter "consentType eq 'AllPrincipals' and resourceId eq '$($microsoftGraph.Id)'" -All $match = $graphPermissions | Where-Object { $_.ClientId -eq $byttEmailApp.Id -and $_.Scope -eq "user.read" } if (!$match) { $grant = New-MgOauth2PermissionGrant -ClientId $byttEmailApp.Id -ConsentType "AllPrincipals" -PrincipalId $null -ResourceId $microsoftGraph.Id -Scope "user.read" Write-Host -ForegroundColor Green " [OK]" } else { Write-Host -ForegroundColor Green " [OK]" } Write-Host "Granting user.read.all application permission to Bytt.Email..." -NoNewline $appRoleAssignments = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $byttEmailApp.Id -All $match = $appRoleAssignments | Where-Object { $_.AppRoleId -eq "df021288-bdef-4463-88db-98f22de89214" -and $_.PrincipalId -eq $byttEmailApp.Id -and $_.ResourceId -eq $microsoftGraph.Id } if (!$match) { $appRoleAssignment = New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $byttEmailApp.Id -PrincipalId $byttEmailApp.Id -ResourceId $microsoftGraph.Id -AppRoleId "df021288-bdef-4463-88db-98f22de89214" Write-Host -ForegroundColor Green " [OK]" } else { Write-Host -ForegroundColor Green " [OK]" } Write-Host "Granting groupmember.read.all application permission to Bytt.Email..." -NoNewline $appRoleAssignments = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $byttEmailApp.Id -All $match = $appRoleAssignments | Where-Object { $_.AppRoleId -eq "98830695-27a2-44f7-8c18-0c3ebc9698f6" -and $_.PrincipalId -eq $byttEmailApp.Id -and $_.ResourceId -eq $microsoftGraph.Id } if (!$match) { $appRoleAssignment = New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $byttEmailApp.Id -PrincipalId $byttEmailApp.Id -ResourceId $microsoftGraph.Id -AppRoleId "98830695-27a2-44f7-8c18-0c3ebc9698f6" Write-Host -ForegroundColor Green " [OK]" } else { Write-Host -ForegroundColor Green " [OK]" } Write-Host "Adding admin consent for Fortytwo Universe user_impersonation Bytt.Email..." -NoNewline $microsoftGraph = Get-MgServicePrincipal -Filter "appId eq '$($fortytwoUniverseAppId)'" -ErrorAction SilentlyContinue $graphPermissions = Get-MgOauth2PermissionGrant -Filter "consentType eq 'AllPrincipals' and resourceId eq '$($microsoftGraph.Id)'" -All $match = $graphPermissions | Where-Object { $_.ClientId -eq $byttEmailApp.Id -and $_.Scope -eq "user_impersonation" } if (!$match) { $grant = New-MgOauth2PermissionGrant -ClientId $byttEmailApp.Id -ConsentType "AllPrincipals" -PrincipalId $null -ResourceId $fortytwoUniverseApp.Id -Scope "user_impersonation" Write-Host -ForegroundColor Green " [OK]" } else { Write-Host -ForegroundColor Green " [OK]" } Write-Host "Creating pattern groups for all domains..." $groups = @() $domains = Get-MgDomain -All | Where-Object { $_.IsVerified -eq $true } | Where-Object { $_.Id -notlike "*mail.onmicrosoft.com" } $creates = $false foreach ($domain in $domains) { $groupName = "{0}{1}{2}" -f $GroupNamingPrefix, $domain.Id, $GroupNamingSuffix Write-Host " - Processing group for domain $($domain.Id)..." -NoNewline $group = Get-MgGroup -Filter "displayName eq '$groupName'" -ErrorAction SilentlyContinue if (!$group) { $group = New-MgGroup -DisplayName $groupName -MailEnabled:$false -SecurityEnabled:$true -MailNickname "$(new-guid)".Substring(0, 8) -AdditionalProperties @{ "extension_34ee8edbd2ff4ee9bac373b53303e00f_patterns" = @( "{firstname1}.{lastname-1}@$($domain.Id)" "{firstname1}.{firstname2}.{lastname-1}@$($domain.Id)" "{firstname1}.{lastname-2}.{lastname-1}@$($domain.Id)" "{firstnamewd1}.{lastnamewd-1}@$($domain.Id)" "{firstnamewd1}.{firstnamewd2}.{lastnamewd-1}@$($domain.Id)" "{firstnamewd1}.{lastnamewd-2}.{lastnamewd-1}@$($domain.Id)" "{firstname1}.{firstname2,1}.{lastname-1}@$($domain.Id)" "{firstname1}.{lastname-1}2@$($domain.Id)" ) } -GroupTypes @("DynamicMembership") -MembershipRule "(user.userPrincipalName -endsWith ""@$($domain.Id)"")" -MembershipRuleProcessingState "On" Write-Host -ForegroundColor Green " [OK]" $creates = $true } else { Write-Host -ForegroundColor Green " [OK]" } $groups += $group } if($creates) { Start-Sleep 3 } Write-Host "Assigning all created pattern groups to Fortytwo Universe role for Bytt.Email..." $appRoleId = "f7906386-2397-411c-820f-270e4f905c05" $appRoleAssignments = Get-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $fortytwoUniverseApp.Id foreach ($group in $groups) { Write-Host " - Processing group $($group.DisplayName)..." -NoNewline $match = $appRoleAssignments | Where-Object { $_.AppRoleId -eq $appRoleId -and $_.PrincipalId -eq $group.Id } if (!$match) { $appRoleAssignment = New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $fortytwoUniverseApp.Id -PrincipalId $group.Id -ResourceId $fortytwoUniverseApp.Id -AppRoleId $appRoleId Write-Host -ForegroundColor Green " [OK]" } else { Write-Host -ForegroundColor Green " [OK]" } } Write-Host "Assigning all created pattern groups to the Bytt.Email application..." $appRoleId = "00000000-0000-0000-0000-000000000000" $appRoleAssignments = Get-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $byttEmailApp.Id foreach ($group in $groups) { Write-Host " - Processing group $($group.DisplayName)..." -NoNewline $match = $appRoleAssignments | Where-Object { $_.AppRoleId -eq $appRoleId -and $_.PrincipalId -eq $group.Id } if (!$match) { $appRoleAssignment = New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $byttEmailApp.Id -PrincipalId $group.Id -ResourceId $byttEmailApp.Id -AppRoleId $appRoleId Write-Host -ForegroundColor Green " [OK]" } else { Write-Host -ForegroundColor Green " [OK]" } } Write-Host "Creating administrative unit for Bytt.Email..." -NoNewline $adminUnit = Get-MgDirectoryAdministrativeUnit -Filter "displayName eq '$adminUnitName'" -ErrorAction SilentlyContinue $membershipRule = "user.memberof -any (group.objectId -in ['$($groups.Id -join "','")'])" if (!$adminUnit) { $params = @{ displayName = $adminUnitName description = "Used for delegating the Bytt.Email application permissions to read users and groups" membershipType = "Dynamic" membershipRule = $membershipRule membershipRuleProcessingState = "On" } Write-Host -ForegroundColor Green " [Creating... " -NoNewline $adminUnit = New-MgDirectoryAdministrativeUnit -DisplayName $params.displayName -Description $params.description -MembershipType $params.membershipType -MembershipRule $params.membershipRule -MembershipRuleProcessingState $params.membershipRuleProcessingState Start-Sleep -Seconds 3 Write-Host -ForegroundColor Green " Done! ($($adminUnit.Id))]" } else { Write-Host -ForegroundColor Green " [OK]" if ($adminUnit.MembershipRule -ne $membershipRule -and !$UpdateAUCriteria.IsPresent) { Write-Warning "The $($adminUnitName) administrative unit exists, but the membership rule is different than expected. However, we will not update it automatically to avoid removing any existing members. Please review and update the membership rule manually if needed. The expected rule is: `n`n$membershipRule`n`nYou can re-run Install-ByttEmail with the -UpdateAUCriteria switch to update the rule automatically." } else { if ($adminUnit.MembershipRule -ne $membershipRule -and $UpdateAUCriteria.IsPresent) { Write-Host "Updating membership rule for $($adminUnitName) administrative unit..." -NoNewline Update-MgDirectoryAdministrativeUnit ` -AdministrativeUnitId $adminUnit.Id ` -MembershipRule $membershipRule ` -MembershipRuleProcessingState "On" ` -MembershipType "Dynamic" Write-Host -ForegroundColor Yellow " [Updated]" } } } Write-Host "Granting the Bytt.Email servicePrincipal access to the Bytt.Email administrative unit..." -NoNewline $roleTemplateId = "fe930be7-5e62-47db-91af-98c3a49a38b1" # User Administrator $role = Get-MgDirectoryRole | Where-Object roleTemplateId -eq $roleTemplateId if(!$role.id) { Write-Host -ForegroundColor Red " [Role not found]" Write-Warning "The User Administrator role was not found in the tenant. Please ensure that the role exists and try again. `n`nYou can do this with the following cmdlet:`n`nNew-MgDirectoryRole -RoleTemplateId '$roleTemplateId'`n`nAfter that, re-run the installation." return } $roleAssignments = Get-MgDirectoryAdministrativeUnitScopedRoleMember -AdministrativeUnitId $adminUnit.Id $match = $roleAssignments | Where-Object { $_.RoleId -eq $role.Id -and $_.RoleMemberInfo.Id -eq $byttEmailApp.Id } if (!$match) { $params = @{ roleId = $role.Id roleMemberInfo = @{ id = $byttEmailApp.Id } } Invoke-MgGraphRequest -Method Post -uri "https://graph.microsoft.com/v1.0/directory/administrativeUnits/$($adminUnit.Id)/scopedRoleMembers" -Body ($params | ConvertTo-Json) -ContentType "application/json" | Out-Null Write-Host -ForegroundColor Green " [OK]" } else { Write-Host -ForegroundColor Green " [OK]" } } } |