고급 데이터 가져오기를 위한 Azure 애플리케이션 설정

Azure 포털에서 Microsoft Azure 애플리케이션을 만들려면 다음 단계를 따르세요. Microsoft Exchange Online에서 Google Workspace 계정으로 데이터를 안전하게 가져오려면 애플리케이션을 만들어야 합니다. 다음 두 가지 방법 중 하나를 선택할 수 있습니다.

PowerShell 스크립트를 사용하여 자동 연결 설정하기

이 단계를 완료하려면 전역 관리자 또는 권한이 있는 역할 관리자여야 합니다.

옵션 1: Azure Cloud Shell 사용

  1. 관리자로 Azure 포털에 로그인합니다.
  2. Cloud Shell다음Powershell을 클릭합니다.
  3. 메시지가 표시되면 스토리지 계정을 만들고 기본 설정을 수락합니다.
  4. 애플리케이션을 만들려면 다음 명령어를 입력하고 Enter를 클릭합니다.

    Install-Module Microsoft.Graph -Scope CurrentUser

  5. 신뢰할 수 없는 저장소에서 설치하라는 메시지가 표시되면 Y를 입력한 후 Enter를 클릭합니다.
  6. 다음 코드 블록을 복사하여 PowerShell에 붙여넣고 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. 다음 사용자 인증 정보를 기록하고 안전하게 저장합니다. 사용자 인증 정보가 유출되면 해커가 모든 Exchange Online 데이터에 액세스할 수 있습니다.
    • 클라이언트 보안 비밀번호
    • 애플리케이션(클라이언트) ID
    • 디렉터리(테넌트) ID

옵션 2: Windows PowerShell 사용

  1. Windows에서 새 일반 텍스트 파일을 만들고 이름을 migration_app_creator.ps1로 지정합니다.
  2. 다음 코드 블록을 복사하여 새 파일에 붙여넣고 Powershell로 실행을 클릭합니다.
    <#
    .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"
    
  • 다음 사용자 인증 정보를 기록하고 안전하게 저장합니다. 사용자 인증 정보가 유출되면 해커가 모든 Exchange Online 데이터에 액세스할 수 있습니다.
    • 클라이언트 보안 비밀번호
    • 애플리케이션(클라이언트) ID
    • 디렉터리(테넌트) ID
  • Azure Active Directory를 사용하여 수동 연결 설정

    구체적인 Microsoft 단계는 Azure 포털 버전 및 Microsoft에서 적용한 업데이트에 따라 다를 수 있습니다. 앱 등록 및 승인에 관한 최신 안내는 Microsoft 문서를 참고하세요.

    1단계: 새 애플리케이션 등록하기

    보안상의 이유로 새 애플리케이션을 단일 테넌트로 등록하는 것이 좋습니다.

    1. 관리자로 Azure 포털에 로그인합니다.
    2. Azure Active Directory (Azure AD)에서 앱 등록으로 이동합니다.
    3. 새 등록을 클릭하고 애플리케이션 이름을 입력합니다 (예: 고급 가져오기 앱).
    4. 지원되는 계정 유형에서 이 조직 디렉터리의 계정만을 클릭하여 단일 테넌트 애플리케이션을 만듭니다.
    5. 등록을 클릭합니다.

    2단계: API 권한 구성하기

    다음 옵션 중 하나를 선택합니다.

    옵션 1: 권한을 수동으로 추가

    1. 측면의 관리에서 API 권한을 클릭합니다.
    2. 권한 추가다음Microsoft API다음Microsoft Graph를 클릭합니다.
    3. 애플리케이션 권한에서 다음을 선택합니다.
      • calendars.read
      • mail.read
      • contacts.read
      • Directory.read.all
    4. 조직에 대해 관리자 동의 부여를 클릭합니다.

    옵션 2: 애플리케이션 매니페스트 수정

    1. 애플리케이션 매니페스트를 엽니다.
    2. “resourceAccess” : [ ]로 이동하여 다음 옵션 중 하나를 선택합니다.
      • “resourceAccess” : [ ]에 이미 값이 있는 경우 쉼표를 추가한 후 다음 코드 블록을 붙여넣습니다.
      • 'resourceAccess' : [ ]에 값이 없으면 다음 코드 블록을 복사하여 붙여넣습니다.

      { "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. 조직에 대해 관리자 동의 부여를 클릭합니다.

    3단계: 클라이언트 보안 비밀번호 생성하기

    1. 측면의 관리에서 인증서 및 보안 비밀다음새 클라이언트 보안 비밀번호를 클릭합니다.
    2. 설명을 입력하고 만료 기간을 선택한 다음 추가를 클릭합니다.
    3. 클라이언트 보안 비밀번호 값을 복사하여 안전하게 저장합니다. 값은 한 번만 표시됩니다.

    4단계: 애플리케이션 사용자 인증 정보 수집하기

    중요: 애플리케이션 사용자 인증 정보를 안전하게 저장하세요. 사용자 인증 정보가 유출되면 해커가 모든 Exchange 데이터에 액세스할 수 있습니다.

    개요를 클릭하고 다음 사용자 인증 정보를 안전하게 기록합니다.

    • 애플리케이션(클라이언트) ID
    • 디렉터리(테넌트) ID


    Google, Google Workspace 및 관련 마크와 로고는 Google LLC의 상표입니다. 기타 모든 회사명 및 제품명은 해당 업체의 상표입니다.