Public/TenantConfiguration/New-BitTitanAppRegistration.ps1

function New-BitTitanAppRegistration() {
    param(
        [Parameter(Mandatory)]
        [string]$TenantId
    )
    Connect-CustomerGraph -CustomerTenantId $TenantId
    Connect-CustomerExchange -CustomerTenantId $TenantId
        
    try {
        $Resource = Get-MgServicePrincipal -Filter "appId eq '00000002-0000-0ff1-ce00-000000000000'" -ErrorAction Stop
        if(!$Resource) {
            throw "Failed to find Exchange Online service principal. The customer does not have Exchange Online - and therefore app registration is impossible. Assign a license to the customer, and wait 10 minutes before trying again."
        }
    }
    catch {
        throw "Failed to find Exchange Online service principal. The customer does not have Exchange Online - and therefore app registration is impossible. Assign a license to the customer, and wait 10 minutes before trying again."
    }

    try {
        $AppRegistrationParams = @{
            displayName            = "BitTitan MigrationWiz"
            description            = "App registration for BitTitan MigrationWiz usage."
            isFallbackPublicClient = "True"
            signInAudience         = "AzureADMultipleOrgs"
            publicClient           = @{
                redirectUris = @(
                    "urn:ietf:wg:oauth:2.0:oob"
                )
            }
            requiredResourceAccess = @(
                @{
                    resourceAppId  = "00000002-0000-0ff1-ce00-000000000000"
                    resourceAccess = @(
                        @{
                            id   = "3b5f3d61-589b-4a3c-a359-5dd4b5ee5bd5"
                            type = "Scope"
                        }
                        @{
                            id   = "dc50a0fb-09a3-484d-be87-e023b12c6440"
                            type = "Role"
                        }
                        @{
                            id   = "dc890d15-9560-4a4c-9b7f-a736ec74ec40"
                            type = "Role"
                        }
                    )
                }
            )
        }
        $Application = New-MgApplication -BodyParameter $AppRegistrationParams -ErrorAction Stop
        Write-Host "Completed creating BitTitan app registration." -ForegroundColor Green
    }
    catch {
        throw "Failed to create BitTitan app registration: $_"
    }

    try {
        $ServicePrincipal = Get-MgServicePrincipal -Filter "appId eq '$($Application.AppId)'" -ErrorAction Stop
        if (!$ServicePrincipal) {
            $ServicePrincipal = New-MgServicePrincipal -AppId $Application.AppId
            Write-Host "Service Principal created." -ForegroundColor Green
        }
    }
    catch {
        throw "Failed to find/create app registration service principal: $_"
    }

    try {
        Write-Host "Waiting 30 seconds to ensure Entra ID Service Principal is synced to Exchange Online"
        Start-Sleep 30
        $EXOSP = New-ServicePrincipal -AppId $ServicePrincipal.AppId -ObjectId $ServicePrincipal.Id -DisplayName "BitTitan MigrationWiz" -ErrorAction Stop
        Write-Host "Succesfully created Exchange Online Service Principal" -ForegroundColor Green
    }
    catch {
        throw "Failed to create Exchange Online Service Principal: $_"
    }

    try {
        $OrgConfig = Get-OrganizationConfig -ErrorAction Stop
        if($OrgConfig.isDehydrated) {
            Enable-OrganizationCustomization
            Write-Host "Successfully enabled organization customization." -ForegroundColor Green
        } else {
            Write-Host "Organization customization is already enabled." -ForegroundColor Green
        }
    } catch {
        throw "Failed to enable organization customization: $_"
    }

    try {
        $ManagementRoleAssignment = New-ManagementRoleAssignment -App $EXOSP.Id -Role "Application EWS.AccessAsApp" -ErrorAction Stop
        Write-Host "Successfully assigned Application EWS.AccessAsApp role." -ForegroundColor Green
    }
    catch {
        throw "Failed to assign Application EWS.AccessAsApp role: $_"
    }

    # Create a client secret
    try {
        $PasswordCredential = @{
            displayName = "MigrationWiz"
            endDateTime = (Get-Date).AddMonths(6)
        }
        $ClientSecret = (Add-MgApplicationPassword -ApplicationId $Application.Id -PasswordCredential $PasswordCredential).SecretText
        Write-Host "Successfully created client secret." -ForegroundColor Green
    }
    catch {
        throw "Failed to create client secret: $_"
    }

    try {        
        $AppRoleAssignment1 = New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ServicePrincipal.Id -PrincipalId $ServicePrincipal.Id -PrincipalDisplayName "BitTitan MigrationWiz" -ResourceDisplayName "Office 365 Exchange Online" -ResourceId $Resource.Id -AppRoleId "dc50a0fb-09a3-484d-be87-e023b12c6440" -ErrorAction Stop
        $AppRoleAssignment2 = New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ServicePrincipal.Id -PrincipalId $ServicePrincipal.Id -PrincipalDisplayName "BitTitan MigrationWiz" -ResourceDisplayName "Office 365 Exchange Online" -ResourceId $Resource.Id -AppRoleId "dc890d15-9560-4a4c-9b7f-a736ec74ec40" -ErrorAction Stop
    } catch {
        throw "Failed to create app role assignments: $_"
    }

    try {
        New-MgOauth2PermissionGrant -ClientId $ServicePrincipal.Id -ConsentType "AllPrincipals" -Scope "EWS.AccessAsUser.All Exchange.ManageAsApp full_access_as_app" -ResourceId $Resource.Id -ErrorAction Stop
        Write-Host "Successfully granted admin consent." -ForegroundColor Green
    }
    catch {
        throw "Failed to grant admin consent for EWS.AccessAsUser.All: $_"
    }

    Write-Host "Client ID:" -ForegroundColor Cyan
    Write-Host "$($Application.AppId)"
    Write-Host "Tenant ID:" -ForegroundColor Cyan
    Write-Host "$($TenantId)"
    Write-Host "Client Secret:" -ForegroundColor Cyan
    Write-Host "$($ClientSecret)"
    Write-Host "For troubleshooting, view this:"
    Write-Host "https://help.bittitan.com/hc/en-us/articles/25111263275035-Replacement-to-the-Retirement-of-Role-Based-Access-Control-for-Applications-in-Exchange-Online"
}