다음 단계에 따라 Azure 포털 내에서 Microsoft Azure 애플리케이션을 만들어 Exchange Online (Microsoft 365)에서 Google Workspace 계정으로의 안전한 데이터 이전을 지원하세요. 다음 두 가지 방법 중 하나를 선택할 수 있습니다.
PowerShell 스크립트를 사용하여 자동 연결 설정하기
이 단계를 완료하려면 전역 관리자 또는 권한이 있는 역할 관리자여야 합니다.
시작하기 전에: Install-Module Microsoft.Graph -Scope CurrentUser를 설치해야 합니다.
옵션 1: Azure Cloud Shell 사용
- 관리자로 Microsoft Azure 포털에 로그인합니다.
- Cloud Shell
Powershell을 클릭합니다.
- 메시지가 표시되면 스토리지 계정을 만들고 기본 설정을 수락합니다.
- Install-Module Microsoft.Graph -Scope CurrentUser 명령어를 붙여넣고 Enter 키를 클릭합니다.
- 신뢰할 수 없는 저장소에서 설치하라는 메시지가 표시되면 Y를 입력한 다음 Enter를 클릭합니다.
- 아래 코드 블록을 복사하여 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"
- 다음 사용자 인증 정보를 기록하고 안전하게 저장합니다. 사용자 인증 정보가 유출되면 해커가 모든 Exchange Online 데이터에 액세스할 수 있습니다.
- 클라이언트 보안 비밀번호
- 애플리케이션 (클라이언트) ID
- 디렉터리 (테넌트) ID
옵션 2: Windows PowerShell 사용
- Windows에서 migration_app_creator.ps1이라는 새 일반 텍스트 파일을 만듭니다.
- 아래 코드 블록을 복사하여 새 파일에 붙여넣고 Powershell로 실행을 클릭합니다.
- 다음 사용자 인증 정보를 기록하고 안전하게 저장합니다. 사용자 인증 정보가 유출되면 해커가 모든 Exchange Online 데이터에 액세스할 수 있습니다.
- 클라이언트 보안 비밀번호
- 애플리케이션 (클라이언트) ID
- 디렉터리 (테넌트) ID
<# .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"
Azure AD를 사용하여 수동 연결 설정
구체적인 Microsoft 단계는 Azure 포털 버전 및 Microsoft에서 적용한 업데이트에 따라 다를 수 있습니다. 앱 등록 및 승인에 관한 최신 안내는 Microsoft 문서를 참고하세요.
1단계: 새 애플리케이션 등록하기
보안상의 이유로 새 애플리케이션을 단일 테넌트로 등록하는 것이 좋습니다.
- 관리자로 Microsoft Azure 포털에 로그인합니다.
- Azure Active Directory (Azure AD)에서 앱 등록으로 이동합니다.
- 새 등록을 클릭하고 애플리케이션 이름을 입력합니다 (예: 엔터프라이즈 마이그레이션 앱).
- 지원되는 계정 유형에서 이 조직 디렉터리의 계정만을 클릭하여 단일 테넌트 애플리케이션을 만듭니다.
- 등록을 클릭합니다.
2단계: API 권한 구성
아래 옵션 중 하나를 선택하세요.
옵션 1: 권한을 수동으로 추가
- 왼쪽 탐색 메뉴의 관리에서 API 권한을 클릭합니다.
- 권한 추가
Microsoft API
Microsoft Graph를 클릭합니다.
- 애플리케이션 권한에서 다음을 선택합니다.
- calendars.read
- mail.read
- contacts.read
- Directory.read.all
- [조직 이름]에 대해 관리자 동의 부여를 클릭합니다.
옵션 2: 애플리케이션 매니페스트 수정
- 애플리케이션 매니페스트를 엽니다.
- “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단계: 클라이언트 보안 비밀번호 생성하기
- 왼쪽 탐색에서 관리를 클릭한 다음 인증서 및 보안 비밀
새 클라이언트 보안 비밀번호를 클릭합니다.
- 설명을 입력하고 만료 기간을 선택한 다음 추가를 클릭합니다.
- 클라이언트 보안 비밀번호 값을 복사하여 안전하게 저장합니다. 값은 한 번만 표시됩니다.
4단계: 애플리케이션 사용자 인증 정보 수집하기
개요를 클릭하고 다음 사용자 인증 정보를 안전하게 기록합니다.
- 애플리케이션 (클라이언트) ID
- 디렉터리 (테넌트) ID
Google, Google Workspace 및 관련 마크와 로고는 Google LLC의 상표입니다. 기타 모든 회사명 및 제품명은 해당 업체의 상표입니다.