Thiết lập Microsoft Azure để di chuyển nâng cao

Hãy làm theo các bước sau để tạo một ứng dụng Microsoft Azure trong cổng Azure, hỗ trợ việc di chuyển dữ liệu an toàn từ Exchange Online (Microsoft 365) sang tài khoản Google Workspace của bạn. Bạn có thể chọn một trong hai phương thức sau:

Sử dụng tập lệnh PowerShell để thiết lập kết nối tự động

Bạn phải là quản trị viên có vai trò toàn cầu hoặc có đặc quyền để hoàn tất các bước này.

Trước khi bắt đầu: Bạn phải cài đặt Install-Module Microsoft.Graph -Scope CurrentUser.

Cách 1: Sử dụng Azure Cloud Shell

  1. Với tư cách là quản trị viên, hãy đăng nhập vào cổng thông tin Microsoft Azure.
  2. Nhấp vào Cloud Shellsau đóPowershell.
  3. Nếu được nhắc, hãy tạo một tài khoản lưu trữ và chấp nhận các chế độ cài đặt mặc định.
  4. Dán lệnh Install-Module Microsoft.Graph -Scope CurrentUser rồi nhấp vào Enter (Nhập).
  5. Nếu bạn thấy lời nhắc cài đặt từ một Kho lưu trữ không đáng tin cậy, hãy nhập Y rồi nhấp vào Enter.
  6. Sao chép khối mã bên dưới, dán vào PowerShell rồi nhấp vào Enter.
        <#
        .SYNOPSIS
        Automates the creation of a Single-Tenant Entra ID App for Workspace Migration.
        Strictly forces account selection and verifies specific Admin roles.
        #>
    
        # Check if the module is missing
        if (-not (Get-Module -ListAvailable -Name Microsoft.Graph.Authentication)) {
        Write-Host "Microsoft Graph module is NOT installed." -ForegroundColor Yellow
        $UserResponse = Read-Host "Would you like to try installing Microsoft Graph? (Y/N)"
    
        if ($UserResponse -ieq "Y") {
            try {
                # Use only native cmdlets, no .NET property setting
                Install-Module -Name Microsoft.Graph -Scope CurrentUser -Force -AllowClobber
                Write-Host "Installation complete!" -ForegroundColor Green
            }
            catch {
                Write-Error "Policy is blocking installation. Please contact IT to install Microsoft.Graph module."
                Read-Host "Press Enter to exit"; exit
            }
        }
        else {
            exit
        }
        } else {
        Write-Host "Microsoft Graph modules detected. Proceeding..." -ForegroundColor Green
        }
    
        # --- STEP 0: THE "DEEP" LOGOUT ---
        Write-Host "Forcing session cleanup..." -ForegroundColor Gray
        Disconnect-MgGraph -ErrorAction SilentlyContinue
    
        # Force clear the local token cache folder if it exists
        $CachePath = "$env:USERPROFILE\.mg"
        if (Test-Path $CachePath) {
        try { Remove-Item $CachePath -Recurse -Force -ErrorAction SilentlyContinue } catch {}
        }
    
        Write-Host "Opening Microsoft Login... (Please select the correct account)" -ForegroundColor Cyan
    
        $RequiredScopes = @(
        "Application.ReadWrite.All", 
        "AppRoleAssignment.ReadWrite.All", 
        "Directory.Read.All", 
        "RoleManagement.Read.Directory"
        )
    
        try {
        # In v2, -ContextScope Process is the most reliable way to force account selection
        # and prevent the session from saving to the machine permanently.
        Connect-MgGraph -Scopes $RequiredScopes -ContextScope Process
    
         $Context = Get-MgContext
        if ($null -eq $Context) { throw "Login was cancelled or failed." }
    
        $UserPrincipal = $Context.Account
        Write-Host "Logged in as: $UserPrincipal" -ForegroundColor Green
    
        # --- ROLE VALIDATION ---
        Write-Host "Verifying Directory Roles..." -ForegroundColor Gray
        $UserRoles = Get-MgUserMemberOf -UserId $Context.Account -All | Where-Object { $_.AdditionalProperties.displayName -ne $null }
        
        $Authorized = $false
        $RequiredRoles = @("Global Administrator", "Privileged Role Administrator")
    
        foreach ($role in $UserRoles) {
            $roleName = $role.AdditionalProperties.displayName
            if ($roleName -in $RequiredRoles) {
                $Authorized = $true
                Write-Host "Access Granted: $roleName" -ForegroundColor Green
                break
            }
        }
    
        if (-not $Authorized) {
            Write-Host "`nCRITICAL ERROR: Insufficient Privileges." -ForegroundColor Red
    
         Write-Host "Account must be 'Global Administrator' or 'Privileged Role Administrator'." -ForegroundColor Yellow
            Disconnect-MgGraph
            Read-Host "`nPress Enter to exit"; exit
        }
    
        } catch {
        Write-Error "Login failed: $_"
        Read-Host "Press Enter to exit"; exit
        }
    
        # --- USER INPUT ---
        Write-Host "`n--- APPLICATION SETUP ---" -ForegroundColor Cyan
        $InputName = Read-Host "Enter the name for your new Entra ID Application (Default: Workspace Migration App)"
        $AppName = if ([string]::IsNullOrWhiteSpace($InputName)) { "Workspace Migration App" } else { $InputName }
    
        # --- CONFIGURATION ---
        $PermissionMap = @{
        "calendar.read"  = "Calendars.Read"     
        "mail.read"      = "Mail.Read"
        "contacts.read"  = "Contacts.Read"
        "directory.read" = "Directory.Read.All"
        }
    
        $TenantId = $Context.TenantId
        $GraphAppId = "00000003-0000-0000-c000-000000000000"
    
        try {
        # --- STEP 1: REGISTER APPLICATION ---
        Write-Host "Creating Application: $AppName..." -ForegroundColor Cyan
        $Application = New-MgApplication -BodyParameter @{
            displayName = $AppName
            signInAudience = "AzureADMyOrg"
        }
        
        # --- STEP 2: PREPARE SERVICE PRINCIPAL ---
        $NewServicePrincipal = New-MgServicePrincipal -BodyParameter @{ appId = $Application.AppId }
    
        Write-Host "Waiting 10 seconds for replication..." -ForegroundColor DarkGray
        Start-Sleep -Seconds 10
    
        # --- STEP 3: CONFIGURE & GRANT PERMISSIONS ---
        Write-Host "Configuring API Permissions & Granting Admin Consent..." -ForegroundColor Cyan
        $GraphSP = Get-MgServicePrincipal -Filter "AppId eq '$GraphAppId'" | Select-Object -First 1
        
        $ResourceAccessList = @()
    
        foreach ($key in $PermissionMap.Keys) {
            $RealRoleName = $PermissionMap[$key]
            $Role = $GraphSP.AppRoles | Where-Object { $_.Value -eq $RealRoleName }
    
            if ($Role) {
                $ResourceAccessList += @{ id = $Role.Id; type = "Role" }
    
                New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $NewServicePrincipal.Id -BodyParameter @{
                    principalId = $NewServicePrincipal.Id
                    resourceId  = $GraphSP.Id
                    appRoleId   = $Role.Id
                } | Out-Null
                Write-Host " - Granted: $RealRoleName" -ForegroundColor Gray
            }
        }
    
        Update-MgApplication -ApplicationId $Application.Id -RequiredResourceAccess @(@{
            resourceAppId  = $GraphAppId
            resourceAccess = $ResourceAccessList
        })
    
        # --- STEP 4: CREATE CLIENT SECRET ---
        Write-Host "Generating Client Secret..." -ForegroundColor Cyan
        $ExpiryDate = (Get-Date).AddYears(2).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
        $PasswordCred = Add-MgApplicationPassword -ApplicationId $Application.Id -BodyParameter @{
            passwordCredential = @{
                displayName = "MigrationToolSecret"
                endDateTime = $ExpiryDate
            }
        }
    
        # --- OUTPUT ---
        Write-Host "`n-------------------------------------------------------" -ForegroundColor Yellow
        Write-Host "        SETUP COMPLETE - SAVE THESE DETAILS" -ForegroundColor Yellow
        Write-Host "-------------------------------------------------------" -ForegroundColor Yellow
        Write-Host "Application Name        : $AppName"
        Write-Host "Application (Client) ID : $($Application.AppId)"
        Write-Host "Client Secret Value     : $($PasswordCred.SecretText)"
        Write-Host "Directory (Tenant) ID   : $TenantId"
        Write-Warning "IMPORTANT: Copy the Client Secret Value immediately."
    
        }
        catch {
        Write-Error "Operation failed: $_"
        }
    
        # --- FINAL DISCONNECT ---
        Disconnect-MgGraph
        Read-Host "`nPress Enter to close this window"
        
  7. Lưu ý những thông tin đăng nhập sau đây và lưu trữ chúng một cách an toàn. Nếu thông tin đăng nhập bị rò rỉ, tin tặc có thể truy cập vào tất cả dữ liệu của bạn trên Exchange Online.
    • Mật khẩu ứng dụng khách
    • Mã ứng dụng (ứng dụng khách)
    • Mã thư mục (người thuê nhà)

Cách 2: Sử dụng Windows PowerShell

  1. Trong Windows, hãy tạo một tệp văn bản thuần tuý mới có tên là migration_app_creator.ps1.
  2. Sao chép khối mã bên dưới, dán vào tệp mới rồi nhấp vào Run with Powershell (Chạy bằng Powershell).
  3.     <#
        .SYNOPSIS
        Automates the creation of a Single-Tenant Entra ID App for Workspace Migration.
        Strictly forces account selection and verifies specific Admin roles.
        #>
    
        # Check if the module is missing
        if (-not (Get-Module -ListAvailable -Name Microsoft.Graph.Authentication)) {
        Write-Host "Microsoft Graph module is NOT installed." -ForegroundColor Yellow
        $UserResponse = Read-Host "Would you like to try installing Microsoft Graph? (Y/N)"
    
        if ($UserResponse -ieq "Y") {
            try {
                # Use only native cmdlets, no .NET property setting
                Install-Module -Name Microsoft.Graph -Scope CurrentUser -Force -AllowClobber
                Write-Host "Installation complete!" -ForegroundColor Green
            }
            catch {
                Write-Error "Policy is blocking installation. Please contact IT to install Microsoft.Graph module."
                Read-Host "Press Enter to exit"; exit
            }
        }
        else {
            exit
        }
        } else {
        Write-Host "Microsoft Graph modules detected. Proceeding..." -ForegroundColor Green
        }
    
        # --- STEP 0: THE "DEEP" LOGOUT ---
        Write-Host "Forcing session cleanup..." -ForegroundColor Gray
        Disconnect-MgGraph -ErrorAction SilentlyContinue
    
        # Force clear the local token cache folder if it exists
        $CachePath = "$env:USERPROFILE\.mg"
        if (Test-Path $CachePath) {
        try { Remove-Item $CachePath -Recurse -Force -ErrorAction SilentlyContinue } catch {}
        }
    
        Write-Host "Opening Microsoft Login... (Please select the correct account)" -ForegroundColor Cyan
    
        $RequiredScopes = @(
        "Application.ReadWrite.All", 
        "AppRoleAssignment.ReadWrite.All", 
        "Directory.Read.All", 
        "RoleManagement.Read.Directory"
        )
    
        try {
        # In v2, -ContextScope Process is the most reliable way to force account selection
        # and prevent the session from saving to the machine permanently.
        Connect-MgGraph -Scopes $RequiredScopes -ContextScope Process
    
         $Context = Get-MgContext
        if ($null -eq $Context) { throw "Login was cancelled or failed." }
    
        $UserPrincipal = $Context.Account
        Write-Host "Logged in as: $UserPrincipal" -ForegroundColor Green
    
        # --- ROLE VALIDATION ---
        Write-Host "Verifying Directory Roles..." -ForegroundColor Gray
        $UserRoles = Get-MgUserMemberOf -UserId $Context.Account -All | Where-Object { $_.AdditionalProperties.displayName -ne $null }
        
        $Authorized = $false
        $RequiredRoles = @("Global Administrator", "Privileged Role Administrator")
    
        foreach ($role in $UserRoles) {
            $roleName = $role.AdditionalProperties.displayName
            if ($roleName -in $RequiredRoles) {
                $Authorized = $true
                Write-Host "Access Granted: $roleName" -ForegroundColor Green
                break
            }
        }
    
        if (-not $Authorized) {
            Write-Host "`nCRITICAL ERROR: Insufficient Privileges." -ForegroundColor Red
    
         Write-Host "Account must be 'Global Administrator' or 'Privileged Role Administrator'." -ForegroundColor Yellow
            Disconnect-MgGraph
            Read-Host "`nPress Enter to exit"; exit
        }
    
        } catch {
        Write-Error "Login failed: $_"
        Read-Host "Press Enter to exit"; exit
        }
    
        # --- USER INPUT ---
        Write-Host "`n--- APPLICATION SETUP ---" -ForegroundColor Cyan
        $InputName = Read-Host "Enter the name for your new Entra ID Application (Default: Workspace Migration App)"
        $AppName = if ([string]::IsNullOrWhiteSpace($InputName)) { "Workspace Migration App" } else { $InputName }
    
        # --- CONFIGURATION ---
        $PermissionMap = @{
        "calendar.read"  = "Calendars.Read"     
        "mail.read"      = "Mail.Read"
        "contacts.read"  = "Contacts.Read"
        "directory.read" = "Directory.Read.All"
        }
    
        $TenantId = $Context.TenantId
        $GraphAppId = "00000003-0000-0000-c000-000000000000"
    
        try {
        # --- STEP 1: REGISTER APPLICATION ---
        Write-Host "Creating Application: $AppName..." -ForegroundColor Cyan
        $Application = New-MgApplication -BodyParameter @{
            displayName = $AppName
            signInAudience = "AzureADMyOrg"
        }
        
        # --- STEP 2: PREPARE SERVICE PRINCIPAL ---
        $NewServicePrincipal = New-MgServicePrincipal -BodyParameter @{ appId = $Application.AppId }
    
        Write-Host "Waiting 10 seconds for replication..." -ForegroundColor DarkGray
        Start-Sleep -Seconds 10
    
        # --- STEP 3: CONFIGURE & GRANT PERMISSIONS ---
        Write-Host "Configuring API Permissions & Granting Admin Consent..." -ForegroundColor Cyan
        $GraphSP = Get-MgServicePrincipal -Filter "AppId eq '$GraphAppId'" | Select-Object -First 1
        
        $ResourceAccessList = @()
    
        foreach ($key in $PermissionMap.Keys) {
            $RealRoleName = $PermissionMap[$key]
            $Role = $GraphSP.AppRoles | Where-Object { $_.Value -eq $RealRoleName }
    
            if ($Role) {
                $ResourceAccessList += @{ id = $Role.Id; type = "Role" }
    
                New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $NewServicePrincipal.Id -BodyParameter @{
                    principalId = $NewServicePrincipal.Id
                    resourceId  = $GraphSP.Id
                    appRoleId   = $Role.Id
                } | Out-Null
                Write-Host " - Granted: $RealRoleName" -ForegroundColor Gray
            }
        }
    
        Update-MgApplication -ApplicationId $Application.Id -RequiredResourceAccess @(@{
            resourceAppId  = $GraphAppId
            resourceAccess = $ResourceAccessList
        })
    
        # --- STEP 4: CREATE CLIENT SECRET ---
        Write-Host "Generating Client Secret..." -ForegroundColor Cyan
        $ExpiryDate = (Get-Date).AddYears(2).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
        $PasswordCred = Add-MgApplicationPassword -ApplicationId $Application.Id -BodyParameter @{
            passwordCredential = @{
                displayName = "MigrationToolSecret"
                endDateTime = $ExpiryDate
            }
        }
    
        # --- OUTPUT ---
        Write-Host "`n-------------------------------------------------------" -ForegroundColor Yellow
        Write-Host "        SETUP COMPLETE - SAVE THESE DETAILS" -ForegroundColor Yellow
        Write-Host "-------------------------------------------------------" -ForegroundColor Yellow
        Write-Host "Application Name        : $AppName"
        Write-Host "Application (Client) ID : $($Application.AppId)"
        Write-Host "Client Secret Value     : $($PasswordCred.SecretText)"
        Write-Host "Directory (Tenant) ID   : $TenantId"
        Write-Warning "IMPORTANT: Copy the Client Secret Value immediately."
    
        }
        catch {
        Write-Error "Operation failed: $_"
        }
    
        # --- FINAL DISCONNECT ---
        Disconnect-MgGraph
        Read-Host "`nPress Enter to close this window"
        
  4. Lưu ý những thông tin đăng nhập sau đây và lưu trữ chúng một cách an toàn. Nếu thông tin đăng nhập bị rò rỉ, tin tặc có thể truy cập vào tất cả dữ liệu của bạn trên Exchange Online.
    • Mật khẩu ứng dụng khách
    • Mã ứng dụng (ứng dụng khách)
    • Mã thư mục (đối tượng thuê)

Sử dụng Azure AD để thiết lập kết nối theo cách thủ công

Các bước cụ thể của Microsoft có thể thay đổi tuỳ thuộc vào phiên bản cổng thông tin Azure và các bản cập nhật của Microsoft. Hãy tham khảo tài liệu của Microsoft để biết hướng dẫn mới nhất về việc đăng ký và uỷ quyền ứng dụng.

Bước 1: Đăng ký một ứng dụng mới

Vì lý do bảo mật, bạn nên đăng ký ứng dụng mới dưới dạng một đối tượng thuê bao duy nhất.

  1. Với tư cách là quản trị viên, hãy đăng nhập vào cổng thông tin Microsoft Azure.
  2. Trong Azure Active Directory (Azure AD), hãy chuyển đến phần Đăng ký ứng dụng.
  3. Nhấp vào Đăng ký mới rồi nhập tên cho ứng dụng của bạn (ví dụ: Ứng dụng di chuyển doanh nghiệp).
  4. Đối với Các loại tài khoản được hỗ trợ, hãy nhấp vào Chỉ tài khoản trong thư mục tổ chức này để tạo một ứng dụng một đối tượng thuê.
  5. Nhấp vào Đăng ký.

Bước 2: Định cấu hình quyền API

Chọn một trong các lựa chọn bên dưới:

Cách 1: Thêm quyền theo cách thủ công

  1. Trong bảng điều hướng bên trái, đối với mục Quản lý, hãy nhấp vào Quyền truy cập API.
  2. Nhấp vào Thêm quyềnsau đóAPI của Microsoftsau đóMicrosoft Graph.
  3. Đối với quyền cho ứng dụng, hãy chọn:
    • calendars.read
    • mail.read
    • contacts.read
    • Directory.read.all
  4. Nhấp vào Cấp sự đồng ý của quản trị viên cho [Tổ chức của bạn].

Cách 2: Chỉnh sửa tệp kê khai ứng dụng

  1. Mở tệp kê khai ứng dụng.
  2. Đối với "resourceAccess" : [ ], hãy sao chép và dán khối mã bên dưới.

    Nếu "resourceAccess" : [ ] đã có giá trị, hãy thêm dấu phẩy rồi dán khối mã.

            
            { "id": "089fe4d0-434a-44c5-8827-41ba8a0b17f5", "type": "Role" },
            { "id": "810c84a8-4a9e-49e6-bf7d-12d183f40d01", "type": "Role" },
            { "id": "7ab1d382-f21e-4acd-a863-ba3e13f7da61", "type": "Role" },
            { "id": "798ee544-9d2d-430c-a058-570e29e34338", "type": "Role" }
            
            
  3. Nhấp vào Cấp sự đồng ý của quản trị viên cho [Tổ chức của bạn].

Bước 3: Tạo khoá bí mật của ứng dụng khách

  1. Trong bảng điều hướng bên trái, đối với Quản lý, hãy nhấp vào Chứng chỉ và khoá bí mậtsau đóKhoá bí mật mới của ứng dụng khách.
  2. Nhập nội dung mô tả, chọn khoảng thời gian hết hạn rồi nhấp vào Thêm.
  3. Sao chép giá trị Khoá bí mật của ứng dụng khách và lưu trữ một cách an toàn. Giá trị này chỉ xuất hiện một lần.

Bước 4: Thu thập thông tin đăng nhập của ứng dụng

Nhấp vào Tổng quan và ghi lại một cách an toàn các thông tin đăng nhập sau:

  • Mã ứng dụng (ứng dụng khách)
  • Mã thư mục (đối tượng thuê)


Google, Google Workspace cũng như các nhãn hiệu và biểu trưng có liên quan là các nhãn hiệu của Google LLC. Tất cả các tên sản phẩm và công ty khác là nhãn hiệu của những công ty mà chúng liên kết.