애플리케이션에 부여된 철회된 권한 복원
이 문서에서는 이전에 애플리케이션에 부여된 철회된 권한을 복원하는 방법을 알아봅니다. 조직의 데이터에 액세스할 수 있는 권한이 부여된 애플리케이션에 대한 권한을 복원할 수 있습니다. 사용자 역할을 할 수 있는 권한이 부여된 애플리케이션에 대한 권한을 복원할 수도 있습니다.
현재 권한 복원은 Microsoft Graph PowerShell 및 Microsoft Graph API 호출을 통해서만 가능합니다. Microsoft Entra 관리 센터를 통해 권한을 복원할 수 없습니다. 이 문서에서는 Microsoft Graph PowerShell을 사용하여 권한을 복원하는 방법을 알아봅니다.
필수 조건
이전에 철회된 애플리케이션 권한을 복원하려면 다음이 필요합니다.
- 활성 구독이 있는 Azure 계정. 체험 계정을 만듭니다.
- 클라우드 애플리케이션 관리자와 애플리케이션 관리자 역할 중 하나.
- 관리자가 아닌 서비스 주체 소유자는 새로 고침 토큰을 무효화할 수 있습니다.
애플리케이션에 대해 철회된 권한 복원
권한을 복원하기 위해 다양한 방법을 시도해 볼 수 있습니다.
- 앱에 대한 동의를 다시 적용하려면 권한 페이지에서 관리자 동의 부여 단추를 사용합니다. 이 동의는 앱 개발자가 원래 앱 매니페스트에서 요청한 권한 집합을 적용합니다.
참고 항목
관리자 동의를 다시 부여하면 개발자가 구성한 기본 집합에 포함되지 않은 부여된 권한이 모두 제거됩니다.
- 철회된 특정 권한을 알고 있는 경우 PowerShell 또는 Microsoft Graph API를 사용하여 수동으로 다시 부여할 수 있습니다.
- 철회된 권한을 모르는 경우 이 문서에 제공된 스크립트를 사용하여 철회된 권한을 검색하고 복원할 수 있습니다.
먼저 스크립트의 servicePrincipalId 값을 권한을 복원하려는 엔터프라이즈 앱의 ID 값으로 설정합니다. 이 ID는 Microsoft Entra 관리 센터의 엔터프라이즈 애플리케이션 페이지에서 object ID
라고도 합니다.
그런 다음, 제거되었을 수 있는 위임된 권한 또는 앱 전용 권한 목록을 보려면 $ForceGrantUpdate = $false
를 사용하여 각 스크립트를 실행합니다. 권한이 이미 복원된 경우에도 감사 로그의 철회 이벤트가 스크립트 결과에 계속 나타날 수 있습니다.
스크립트가 검색한 철회된 권한을 복원하려고 시도하도록 하려면 $ForceGrantUpdate
를 $true
로 설정된 상태로 둡니다. 스크립트는 확인을 요청하지만 복원하는 각 권한에 대해 개별 승인을 요청하지는 않습니다.
앱에 권한을 부여할 때는 주의합니다. 권한을 평가하는 방법에 대해 자세히 알아보려면 권한 평가를 참조하세요.
위임된 권한 복원
# WARNING: Setting $ForceGrantUpdate to true will modify permission grants without
# prompting for confirmation. This can result in unintended changes to your
# application's security settings. Use with caution!
$ForceGrantUpdate = $false
# Set the start and end dates for the audit log search
# If setting date use yyyy-MM-dd format
# endDate is set to tomorrow to include today's audit logs
$startDate = (Get-Date).AddDays(-7).ToString('yyyy-MM-dd')
$endDate = (Get-Date).AddDays(1).ToString('yyyy-MM-dd')
# Set the service principal ID
$servicePrincipalId = "aaaaaaaa-bbbb-cccc-1111-222222222222"
Write-Host "Searching for audit logs between $startDate and $endDate" -ForegroundColor Green
Write-Host "Searching for audit logs for service principal $servicePrincipalId" -ForegroundColor Green
if ($ForceGrantUpdate -eq $true) {
Write-Host "WARNING: ForceGrantUpdate is set to true. This will modify permission grants without prompting for confirmation. This can result in unintended changes to your application's security settings. Use with caution!" -ForegroundColor Red
$continue = Read-Host "Do you want to continue? (Y/N)"
if ($continue -eq "Y" -or $continue -eq "y") {
Write-Host "Continuing..."
} else {
Write-Host "Exiting..."
exit
}
}
# Connect to MS Graph
Connect-MgGraph -Scopes "AuditLog.Read.All","DelegatedPermissionGrant.ReadWrite.All" -ErrorAction Stop | Out-Null
# Create a hashtable to store the OAuth2PermissionGrants
$oAuth2PermissionGrants = @{}
function Merge-Scopes($oldScopes, $newScopes) {
$oldScopes = $oldScopes.Trim() -split '\s+'
$newScopes = $newScopes.Trim() -split '\s+'
$mergedScopesArray = $oldScopes + $newScopes | Select-Object -Unique
$mergedScopes = $mergedScopesArray -join ' '
return $mergedScopes.Trim()
}
# Function to merge scopes if multiple OAuth2PermissionGrants are found in the audit logs
function Add-Scopes($resourceId, $newScopes) {
if($oAuth2PermissionGrants.ContainsKey($resourceId)) {
$oldScopes = $oAuth2PermissionGrants[$resourceId]
$oAuth2PermissionGrants[$resourceId] = Merge-Scopes $oldScopes $newScopes
}
else {
$oAuth2PermissionGrants[$resourceId] = $newScopes
}
}
function Get-ScopeDifference ($generatedScope, $currentScope) {
$generatedScopeArray = $generatedScope.Trim() -split '\s+'
$currentScopeArray = $currentScope.Trim() -split '\s+'
$difference = $generatedScopeArray | Where-Object { $_ -notin $currentScopeArray }
$difference = $difference -join ' '
return $difference.Trim()
}
# Set the filter for the audit log search
$filterOAuth2PermissionGrant = "activityDateTime ge $startDate and activityDateTime le $endDate" +
" and Result eq 'success'" +
" and ActivityDisplayName eq 'Remove delegated permission grant'" +
" and targetResources/any(x: x/id eq '$servicePrincipalId')"
try {
# Retrieve the audit logs for removed OAuth2PermissionGrants
$oAuth2PermissionGrantsAuditLogs = Get-MgAuditLogDirectoryAudit -Filter $filterOAuth2PermissionGrant -All -ErrorAction Stop
}
catch {
Disconnect-MgGraph | Out-Null
throw $_
}
# Remove User Delegated Permission Grants
$oAuth2PermissionGrantsAuditLogs = $oAuth2PermissionGrantsAuditLogs | Where-Object {
-not ($_.TargetResources.ModifiedProperties.OldValue -eq '"Principal"')
}
# Merge duplicate OAuth2PermissionGrants from AuditLogs using Add-Scopes
foreach ($auditLog in $oAuth2PermissionGrantsAuditLogs) {
$resourceId = $auditLog.TargetResources[0].Id
# We only want to process OAuth2PermissionGrant Audit Logs where $servicePrincipalId is the clientId not the resourceId
if ($resourceId -eq $servicePrincipalId) {
continue
}
$oldScope = $auditLog.TargetResources[0].ModifiedProperties | Where-Object { $_.DisplayName -eq "DelegatedPermissionGrant.Scope" } | Select-Object -ExpandProperty OldValue
if ($oldScope -eq $null) {
$oldScope = ""
}
$oldScope = $oldScope.Replace('"', '')
$newScope = $auditLog.TargetResources[0].ModifiedProperties | Where-Object { $_.DisplayName -eq "DelegatedPermissionGrant.Scope" } | Select-Object -ExpandProperty NewValue
if ($newScope -eq $null) {
$newScope = ""
}
$newScope = $newScope.Replace('"', '')
$scope = Merge-Scopes $oldScope $newScope
Add-Scopes $resourceId $scope
}
$permissionCount = 0
foreach ($resourceId in $oAuth2PermissionGrants.keys) {
$scope = $oAuth2PermissionGrants[$resourceId]
$params = @{
clientId = $servicePrincipalId
consentType = "AllPrincipals"
resourceId = $resourceId
scope = $scope
}
try {
$currentOAuth2PermissionGrant = Get-MgOauth2PermissionGrant -Filter "clientId eq '$servicePrincipalId' and consentType eq 'AllPrincipals' and resourceId eq '$resourceId'" -ErrorAction Stop
$action = "Creating"
if ($currentOAuth2PermissionGrant -ne $null) {
$action = "Updating"
}
Write-Host "--------------------------"
if ($ForceGrantUpdate -eq $true) {
Write-Host "$action OAuth2PermissionGrant with the following parameters:"
} else {
Write-Host "Potentially removed OAuth2PermissionGrant scopes with the following parameters:"
}
Write-Host " clientId: $($params.clientId)"
Write-Host " consentType: $($params.consentType)"
Write-Host " resourceId: $($params.resourceId)"
if ($currentOAuth2PermissionGrant -ne $null) {
$scopeDifference = Get-ScopeDifference $scope $currentOAuth2PermissionGrant.Scope
if ($scopeDifference -eq "") {
Write-Host "OAuth2PermissionGrant already exists with the same scope" -ForegroundColor Yellow
if ($ForceGrantUpdate -eq $true) {
Write-Host "Skipping Update" -ForegroundColor Yellow
}
continue
}
else {
Write-Host " scope diff: '$scopeDifference'"
}
}
else {
Write-Host " scope: '$($params.scope)'"
}
if ($ForceGrantUpdate -eq $true -and $currentOAuth2PermissionGrant -eq $null) {
New-MgOauth2PermissionGrant -BodyParameter $params -ErrorAction Stop | Out-Null
Write-Host "OAuth2PermissionGrant was created successfully" -ForegroundColor Green
}
if ($ForceGrantUpdate -eq $true -and $currentOAuth2PermissionGrant -ne $null) {
Write-Host " Current Scope: '$($currentOAuth2PermissionGrant.scope)'" -ForegroundColor Yellow
Write-Host " Merging with scopes from audit logs" -ForegroundColor Yellow
$params.scope = Merge-Scopes $currentOAuth2PermissionGrant.scope $params.scope
Write-Host " New Scope: '$($params.scope)'" -ForegroundColor Yellow
Update-MgOauth2PermissionGrant -OAuth2PermissionGrantId $currentOAuth2PermissionGrant.id -BodyParameter $params -ErrorAction Stop | Out-Null
Write-Host "OAuth2PermissionGrant was updated successfully" -ForegroundColor Green
}
$permissionCount++
}
catch {
Disconnect-MgGraph | Out-Null
throw $_
}
}
Disconnect-MgGraph | Out-Null
if ($ForceGrantUpdate -eq $true) {
Write-Host "--------------------------"
Write-Host "$permissionCount OAuth2PermissionGrants were created/updated successfully" -ForegroundColor Green
} else {
Write-Host "--------------------------"
Write-Host "$permissionCount OAuth2PermissionGrants were found" -ForegroundColor Green
}
앱 전용 권한 복원
참고 항목
앱 전용 Microsoft Graph 권한을 부여하려면 권한 있는 역할 관리자 역할이 필요합니다.
# WARNING: Setting $ForceGrantUpdate to true will modify permission grants without
# prompting for confirmation. This can result in unintended changes to your
# application's security settings. Use with caution!
$ForceGrantUpdate = $false
# Set the start and end dates for the audit log search
# If setting date use yyyy-MM-dd format
# endDate is set to tomorrow to include today's audit logs
$startDate = (Get-Date).AddDays(-7).ToString('yyyy-MM-dd')
$endDate = (Get-Date).AddDays(1).ToString('yyyy-MM-dd')
# Set the service principal ID
$servicePrincipalId = "aaaaaaaa-bbbb-cccc-1111-222222222222"
Write-Host "Searching for audit logs between $startDate and $endDate" -ForegroundColor Green
Write-Host "Searching for audit logs for service principal $servicePrincipalId" -ForegroundColor Green
if ($ForceGrantUpdate -eq $true) {
Write-Host "WARNING: ForceGrantUpdate is set to true. This will modify permission grants without prompting for confirmation. This can result in unintended changes to your application's security settings. Use with caution!" -ForegroundColor Red
$continue = Read-Host "Do you want to continue? (Y/N)"
if ($continue -eq "Y" -or $continue -eq "y") {
Write-Host "Continuing..."
} else {
Write-Host "Exiting..."
exit
}
}
# Connect to MS Graph
Connect-MgGraph -Scopes "AuditLog.Read.All","Application.Read.All","AppRoleAssignment.ReadWrite.All" -ErrorAction Stop | Out-Null
# Set the filter for the audit log search
$filterAppRoleAssignment = "activityDateTime ge $startDate and activityDateTime le $endDate" +
" and Result eq 'success'" +
" and ActivityDisplayName eq 'Remove app role assignment from service principal'" +
" and targetResources/any(x: x/id eq '$servicePrincipalId')"
try {
# Retrieve the audit logs for removed AppRoleAssignments
$appRoleAssignmentsAuditLogs = Get-MgAuditLogDirectoryAudit -Filter $filterAppRoleAssignment -All -ErrorAction Stop
}
catch {
Disconnect-MgGraph | Out-Null
throw $_
}
$permissionCount = 0
foreach ($auditLog in $appRoleAssignmentsAuditLogs) {
$resourceId = $auditLog.TargetResources[0].Id
# We only want to process AppRoleAssignments Audit Logs where $servicePrincipalId is the principalId not the resourceId
if ($resourceId -eq $servicePrincipalId) {
continue
}
$appRoleId = $auditLog.TargetResources[0].ModifiedProperties | Where-Object { $_.DisplayName -eq "AppRole.Id" } | Select-Object -ExpandProperty OldValue
$appRoleId = $appRoleId.Replace('"', '')
$params = @{
principalId = $servicePrincipalId
resourceId = $resourceId
appRoleId = $appRoleId
}
try {
$sp = Get-MgServicePrincipal -ServicePrincipalId $resourceId
$appRole = $sp.AppRoles | Where-Object { $_.Id -eq $appRoleId }
Write-Host "--------------------------"
if ($ForceGrantUpdate -eq $true) {
Write-Host "Creating AppRoleAssignment with the following parameters:"
} else {
Write-Host "Potentially removed AppRoleAssignment with the following parameters:"
}
Write-Host " principalId: $($params.principalId)"
Write-Host " resourceId: $($params.resourceId)"
Write-Host " appRoleId: $($params.appRoleId)"
Write-Host " appRoleValue: $($appRole.Value)"
Write-Host " appRoleDisplayName: $($appRole.DisplayName)"
if ($ForceGrantUpdate -eq $true) {
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $servicePrincipalId -BodyParameter $params -ErrorAction Stop | Out-Null
Write-Host "AppRoleAssignment was created successfully" -ForegroundColor Green
}
$permissionCount++
}
catch {
if ($_.Exception.Message -like "*Permission being assigned already exists on the object*") {
Write-Host "AppRoleAssignment already exists skipping creation" -ForegroundColor Yellow
}
else {
Disconnect-MgGraph | Out-Null
throw $_
}
}
}
Disconnect-MgGraph | Out-Null
if ($ForceGrantUpdate -eq $true) {
Write-Host "--------------------------"
Write-Host "$permissionCount AppRoleAssignments were created successfully" -ForegroundColor Green
} else {
Write-Host "--------------------------"
Write-Host "$permissionCount AppRoleAssignments were found" -ForegroundColor Green
}