Skip to main content

Set up an Azure app registration for PowerShell mailbox migrations

How to set up an Azure app registration to run ShareGate Migrate PowerShell mailbox migrations without a signed-in user account.

Note: PowerShell integration requires a ShareGate Migrate Pro or Enterprise subscription. It is not available on the Essentials plan.

This article is for the ShareGate Migrate PowerShell integration only. Application-only authentication lets you run mailbox migrations from PowerShell without a signed-in user account. Instead of user credentials, ShareGate Migrate authenticates using an Azure app registration. This is well-suited for automated or scheduled migrations in enterprise environments.

During setup, you'll capture two values to pass to ShareGate's PowerShell commands: the Application (client) ID and the Directory (tenant) ID.

Before you begin

  • ShareGate Migrate Pro or Enterprise subscription

  • Global Administrator access on both the source and destination Microsoft 365 tenants, needed to create the app registration and grant admin consent

Single-tenant vs multi-tenant

Knowing the difference between two terms will help you choose the right setup:

  • An app registration is the blueprint of the application. It lives in the home tenant (the tenant where you create it) and defines the app's identity, API permissions, and certificates.

  • An enterprise application (also called a service principal) is the local instance of that app inside a specific tenant. This is what you grant admin consent on and assign roles to.

Choose based on how many tenants you migrate:

  • Single-tenant: the app is used only in the tenant where you register it. Under Supported account types, choose My organization only. To migrate between two tenants, repeat the full setup in each tenant.

  • Multi-tenant: one app registration in your home tenant, reused across several tenants. This is the common choice for consultants, managed service providers, and source-to-destination migrations. Under Supported account types, choose Multiple Entra ID tenants and keep Allow all tenants selected. You then consent the app in each additional tenant and assign the Exchange Administrator role there. See Set up additional tenants below.

With a multi-tenant app, you build the credential object once and pass a different -TenantId to Connect-MicrosoftOnline for each tenant.

Set up the app registration

Do this in your home tenant. For a single-tenant app, repeat in each tenant you migrate to or from. For a multi-tenant app, do it once and then follow Set up additional tenants below.

Option 1: Manual setup in the Azure portal

Step 1: Create the app registration

  1. Sign in to the Microsoft Entra admin center as a Global Administrator.

  2. Go to Entra ID > App registrations > New registration.

  3. Give the app a name (for example, ShareGate Mailbox Migration).

  4. Under Supported account types, choose the option that matches your scenario. See Single-tenant vs multi-tenant above.

  5. Leave the Redirect URI empty for now.

  6. Click Register.

  7. On the Overview page, copy the Application (client) ID and the Directory (tenant) ID. You'll use these with New-AzureApplication and Connect-MicrosoftOnline. Also note the Display name or Object ID for the role assignment in Step 4.

Step 2: Add a reply URL

Note: This step is required for multi-tenant apps. Admin consent won't complete without at least one reply URL. https://go.sharegate.com/vmen is a ShareGate landing page that confirms consent succeeded.

  1. In your app registration, go to Authentication and click + Add Redirect URI.

  2. In the Web platform pane, under Redirect URI, enter https://go.sharegate.com/vmen.

  3. Click Configure.

Step 3: Add API permissions

Choose one of the two methods below.

Option A: Manually via the Azure portal

  1. Go to API permissions > Add a permission.

  2. Add each of the Application permissions listed below (not Delegated). You add the Microsoft Graph permissions and the Office 365 Exchange Online permissions separately.

  3. Click Grant admin consent for [your tenant] and confirm.

Microsoft Graph permissions:

Mail.ReadWrite

Mail items

Calendars.ReadWrite

Calendar events

Contacts.ReadWrite

Contacts

MailboxSettings.ReadWrite

Mailbox settings

User.Read.All

Mailbox listing, user profiles

Directory.Read.All

License validation

Organization.Read.All

Domain validation

Office 365 Exchange Online permissions:

Note: These permissions are found under Office 365 Exchange Online, not Microsoft Graph. Use Exchange.ManageAsApp, not Exchange.ManageAsAppV2.

full_access_as_app

Archive and group mailboxes (EWS)

Exchange.ManageAsApp

Mailbox management

Option B: Via the app manifest

This lets you add all permissions at once by pasting a JSON snippet, avoiding manual selection errors.

  1. In your app registration, go to Manifest.

  2. Find the requiredResourceAccess array and replace it with the following:

    "requiredResourceAccess": [
    {
    "resourceAppId": "00000002-0000-0ff1-ce00-000000000000",
    "resourceAccess": [
    { "id": "dc50a0fb-09a3-484d-be87-e023b12c6440", "type": "Role" },
    { "id": "dc890d15-9560-4a4c-9b7f-a736ec74ec40", "type": "Role" }
    ]
    },
    {
    "resourceAppId": "00000003-0000-0000-c000-000000000000",
    "resourceAccess": [
    { "id": "ef54d2bf-783f-4e0f-bca1-3210c0444d99", "type": "Role" },
    { "id": "6918b873-d17a-4dc1-b314-35f528134491", "type": "Role" },
    { "id": "7ab1d382-f21e-4acd-a863-ba3e13f7da61", "type": "Role" },
    { "id": "e2a3a72e-5f79-4c64-b1b1-878b674786c9", "type": "Role" },
    { "id": "6931bccd-447a-43d1-b442-00a195474933", "type": "Role" },
    { "id": "498476ce-e0fe-48b0-b801-37ba7e2685c6", "type": "Role" },
    { "id": "df021288-bdef-4463-88db-98f22de89214", "type": "Role" }
    ]
    }
    ]
  3. Click Save.

  4. Go to API permissions and click Grant admin consent for [your tenant] and confirm.

Step 4: Assign the Exchange Administrator role

Note: This step is required for archive management, litigation holds, and mailbox size operations.

Exchange Administrator is a directory-level role, so you can't assign it directly from the app registration's Roles and administrators page. The steps below take you to the directory-level assignment view.

  1. In your app registration, go to Roles and administrators.

  2. In the line that reads "Directory-level roles have inherited access to this resource and can only be assigned at the directory level here," click here to open directory-level role assignment.

  3. Search for and select Exchange Administrator.

  4. Click + Add assignments, then Select member(s).

  5. Search for your app registration's Display name or Object ID, select it, and click Select.

  6. Click Next, then Assign.

Option 2: Quick setup with PowerShell (recommended)

This script automates everything in Option 1: it creates the app registration, adds the reply URL, grants all required permissions, and assigns the Exchange Administrator role. It does not create a credential. You do that in Add a credential below.

First, install the Microsoft Graph PowerShell SDK (one time):

Install-Module Microsoft.Graph -Scope CurrentUser

Then run the following, signed in as a Global Administrator of the home tenant. Set $multiTenant = $true for a multi-tenant app.

$ErrorActionPreference = 'Stop'
$displayName = 'ShareGate Mailbox Migration'
$multiTenant = $false # set to $true to allow the app to be consented in other tenants
$replyUrl = 'https://go.sharegate.com/vmen'

$signInAudience = if ($multiTenant) { 'AzureADMultipleOrgs' } else { 'AzureADMyOrg' }

# Well-known resource app IDs
$graphAppId = '00000003-0000-0000-c000-000000000000' # Microsoft Graph
$exoAppId = '00000002-0000-0ff1-ce00-000000000000' # Office 365 Exchange Online

# Application permissions to grant
$graphRoleIds = @(
'e2a3a72e-5f79-4c64-b1b1-878b674786c9', # Mail.ReadWrite
'ef54d2bf-783f-4e0f-bca1-3210c0444d99', # Calendars.ReadWrite
'6918b873-d17a-4dc1-b314-35f528134491', # Contacts.ReadWrite
'6931bccd-447a-43d1-b442-00a195474933', # MailboxSettings.ReadWrite
'df021288-bdef-4463-88db-98f22de89214', # User.Read.All
'7ab1d382-f21e-4acd-a863-ba3e13f7da61', # Directory.Read.All
'498476ce-e0fe-48b0-b801-37ba7e2685c6' # Organization.Read.All
)
$exoRoleIds = @(
'dc50a0fb-09a3-484d-be87-e023b12c6440', # full_access_as_app
'dc890d15-9560-4a4c-9b7f-a736ec74ec40' # Exchange.ManageAsApp
)
$exchangeAdminRoleId = '29232cdf-9323-42fd-ade2-1d097af3e4de' # Exchange Administrator (built-in)

# 1. Sign in
Connect-MgGraph -Scopes 'Application.ReadWrite.All','AppRoleAssignment.ReadWrite.All','RoleManagement.ReadWrite.Directory'

# 2. Create the app registration
$requiredResourceAccess = @(
@{ ResourceAppId = $graphAppId; ResourceAccess = @($graphRoleIds | ForEach-Object { @{ Id = $_; Type = 'Role' } }) },
@{ ResourceAppId = $exoAppId; ResourceAccess = @($exoRoleIds | ForEach-Object { @{ Id = $_; Type = 'Role' } }) }
)
$app = New-MgApplication -DisplayName $displayName -SignInAudience $signInAudience `
-Web @{ RedirectUris = @($replyUrl) } `
-RequiredResourceAccess $requiredResourceAccess

# 3. Create the service principal
$sp = New-MgServicePrincipal -AppId $app.AppId
Start-Sleep -Seconds 10 # let the service principal replicate

# 4. Grant admin consent
$graphSp = Get-MgServicePrincipal -Filter "appId eq '$graphAppId'"
$exoSp = Get-MgServicePrincipal -Filter "appId eq '$exoAppId'"
foreach ($roleId in $graphRoleIds) {
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $sp.Id -PrincipalId $sp.Id -ResourceId $graphSp.Id -AppRoleId $roleId | Out-Null
}
foreach ($roleId in $exoRoleIds) {
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $sp.Id -PrincipalId $sp.Id -ResourceId $exoSp.Id -AppRoleId $roleId | Out-Null
}

# 5. Assign the Exchange Administrator role
New-MgRoleManagementDirectoryRoleAssignment -PrincipalId $sp.Id -RoleDefinitionId $exchangeAdminRoleId -DirectoryScopeId '/' | Out-Null

# 6. Output the values you'll use with ShareGate
[pscustomobject]@{
DisplayName = $displayName
ApplicationId = $app.AppId
TenantId = (Get-MgContext).TenantId
} | Format-List

Copy the ApplicationId and TenantId from the output. You'll use them when you connect. For a multi-tenant app, this configures your home tenant only. Follow Set up additional tenants below for each additional tenant.

Set up additional tenants

Note: This section applies to multi-tenant apps only. The app registration and its credential live in your home tenant. To use the app in another tenant, an administrator of that tenant must consent to it and assign the Exchange Administrator role. Repeat for each additional tenant.

An administrator of the additional tenant opens the following URL in a browser, replaces the placeholders, and signs in as a Global Administrator of that tenant:

https://login.microsoftonline.com/<additional-tenant-id-or-domain>/adminconsent?client_id=<application-client-id>

After reviewing and accepting the permissions, the browser redirects to the ShareGate confirmation page. This provisions the app's enterprise application (service principal) in that tenant.

2. Assign the Exchange Administrator role in the additional tenant

While signed in to the additional tenant:

  1. Go to Entra ID > Roles & admins.

  2. Search for and select Exchange Administrator.

  3. Click + Add assignments, then Select member(s).

  4. Search for the app by its Display name or Application (client) ID, select it, and click Select.

  5. Click Next, then Assign.

Add a credential

New-AzureApplication supports two certificate-based authentication methods. Choose one.

Option A: Certificate (from file)

  1. Go to Certificates & secrets > Certificates > Upload certificate.

  2. Upload your .cer or .pem public key file.

  3. Keep the private key (.pfx) accessible from PowerShell at runtime.

Option B: Certificate thumbprint (Windows Certificate Store)

  1. Install the certificate (with private key) into the Windows Certificate Store on the machine running the migration.

  2. Go to Certificates & secrets > Certificates > Upload certificate and upload the public key.

  3. Note the thumbprint. You'll pass it directly to New-AzureApplication.

Note: For a multi-tenant app, upload the credential once to the app registration in your home tenant. The same credential works for every tenant the app is consented in.

Connect from ShareGate

Build a credential object with New-AzureApplication, then connect with Connect-MicrosoftOnline. Pass the target tenant with -TenantId.

# Build a credential object from a certificate thumbprint
$azureApp = New-AzureApplication -ClientId <application-client-id> -Thumbprint <certificate-thumbprint>

# Connect to a tenant
Connect-MicrosoftOnline -TenantId <directory-tenant-id> -AzureApplication $azureApp

With a multi-tenant app, reuse the same $azureApp object and change -TenantId for each tenant:

Connect-MicrosoftOnline -TenantId <source-tenant-id> -AzureApplication $azureApp
Connect-MicrosoftOnline -TenantId <destination-tenant-id> -AzureApplication $azureApp

Note: If your security policy prevents granting one of the required permissions, add -AllowMissingPermissions to Connect-MicrosoftOnline to connect anyway. Operations that rely on the missing permission will fail with Forbidden errors. This switch only relaxes the permission check after authentication succeeds. It won't resolve a failed sign-in.

Known limitations

  • Group calendars are not supported under application-only authentication. This is a Microsoft Graph API limitation that affects all migration tools.

Did this answer your question?