6월에 다중 관리자 승인이 이제 Graph API를 통한 자동화에도 적용된다는 짧은 글을 올렸을 때, 그 글은 이 사이트에서 가장 많이 읽힌 페이지 중 하나가 되었고 이어서 받은 메일은 모두 같은 질문을 했습니다. 무엇이 바뀌었는지가 아니라 어떻게 스크립트로 구현하는지였습니다. 이 가이드가 그 실습 안내서입니다. 2026년 10월 기준으로 다중 관리자 승인(MAA)이 무엇을 보호하는지, 스스로를 잠가 버리지 않고 액세스 정책과 승인자 그룹을 설계하는 방법, 사람 승인자가 관리 센터에서 누르는 정확한 경로, 그리고 많은 분이 기다렸던 부분, 즉 보호되는 개체를 만드는 스크립트가 승인 요청을 어떻게 받고, 어떻게 기다리고, 승인자가 Graph에서 어떻게 승인하며, 스크립트가 어떻게 작업을 끝내는지를 다룹니다. 여기 나오는 모든 엔드포인트, 헤더, 권한, 제한은 Microsoft Learn에서 확인했습니다. 제 의견을 덧붙이는 곳에서는 그렇게 밝힙니다.
다중 관리자 승인이란 무엇이고 왜 Graph 자동화까지 포함하게 되었나
Microsoft Learn 개요에 따르면 MAA 액세스 정책은 “변경이 적용되기 전에 두 번째 관리 계정이 그 변경을 승인하도록 요구"합니다. 액세스 정책은 보호할 프로필 유형 하나와 그 유형의 변경을 승인할 수 있는 계정 그룹을 지정합니다. 어떤 계정이든 보호되는 리소스를 변경하면 Intune은 다른 계정이 명시적으로 승인할 때까지 변경을 보류하며, 승인자는 거부할 수도 있습니다. 비즈니스 근거, 승인자 메모, 결정은 모두 Intune 감사 로그에 남습니다.
2026년 6월까지는 대화형(위임) 관리자 작업만 가로채졌습니다. 서비스 주체가 앱 전용 토큰으로 같은 초기화나 스크립트 배포를 수행하면 그대로 통과했습니다. Microsoft는 2026년 6월 22일 주의 What’s new 항목에서 이 틈을 메웠습니다. “다중 관리자 승인(MAA)이 이제 대화형 관리자 작업뿐 아니라 Microsoft Graph API를 통해 자동화가 수행하는 API 호출에도 적용됩니다.” 여기에는 두 가지 안전장치가 따라옵니다. API 호출에 대한 적용은 MAA 액세스 정책이 이미 구성된 테넌트에만 해당하며, 어디에서도 MAA를 임의로 켜지 않습니다. 또한 Graph API 방법 문서에 따르면 “MAA는 보호되는 리소스를 수정하는 작업(POST, PATCH, PUT, DELETE)에만 적용됩니다. 읽기 전용 작업(GET)은 영향을 받지 않습니다.”
Microsoft가 왜 이렇게 했는지는 공격자처럼 생각해 보면 분명합니다. DeviceManagementManagedDevices.ReadWrite.All을 가진 앱의 클라이언트 비밀이 유출되면 예전에는 테넌트 전체 초기화 버튼이었습니다. 이제 같은 호출은 사람이 읽어야 하는 승인 요청만 만들어 냅니다.
보호되는 리소스
개요 페이지는 일곱 가지 리소스 유형을 나열합니다. Graph의 operationApprovalPolicyType 열거형에는 훨씬 많은 값(autopilot, windows365, endpointPrivilegeManagement, filter, appProtectionPolicy 등)이 있지만, 열거형은 서비스가 오늘 실제로 적용하는 범위의 상위 집합일 뿐입니다. 문서화된 일곱 가지만 보호되고, 앱 보호 정책은 명시적으로 제외되며, 수정(remediations)은 목록에 없습니다.
| 리소스(프로필 유형) | 승인을 유발하는 작업 | 시작 시점(What’s new 기준) |
|---|---|---|
| 앱 | 앱 배포의 생성, 편집, 삭제, 할당. 앱 보호 정책에는 적용되지 않음. | 2023년 8월 28일 주에 GA. 2024년 8월(2408)에 Windows와 비 Windows 범위 지정 추가 |
| 스크립트 | Windows 디바이스에 PowerShell 스크립트 배포 | Graph 스키마의 최초 두 정책 유형(app, script) 중 하나 |
| 디바이스 작업 | 초기화, 사용 중지, 삭제 | 초기화는 2025년 8월 18일 주(2508)에 추가. 사용 중지와 삭제는 개요에 명시 |
| 역할 기반 액세스 제어 | 역할에 대한 모든 변경: 권한, 관리자 그룹, 멤버 그룹 할당 | 2025년 8월 18일 주(2508) |
| 테넌트 구성 | 디바이스 범주의 생성, 편집, 삭제 | 2025년 9월 15일 주(2509) |
| 규정 준수 정책 | 규정 준수 정책 생성과 관리 | 2026년 3월 2일 주(2602) |
| 구성 정책 | 설정 카탈로그로 만든 정책 | 2026년 3월 2일 주(2602) |
| 액세스 정책(MAA 자체) | 제외를 포함한 액세스 정책의 모든 변경 | 자동으로 보호되며 프로필 유형으로 선택 불가 |
이 표에 두 가지를 덧붙입니다. “보호되는 리소스의 모든 작업이 보호되며 편집, 생성, 수정, 삭제, 할당을 포함하되 이에 국한되지 않습니다"는 Microsoft의 표현이므로 기존 앱의 할당 변경도 해당됩니다. 그리고 2026년 6월의 앱 전용 Graph 호출 적용은 모든 행에 동시에 적용되며, 자동화에 대해 유형별로 선택 가입하는 방법은 없습니다.
필수 조건, 역할, 라이선스
Microsoft는 MAA 워크플로에 세 가지 역할을 정의하며, 서비스 주체는 세 번째 역할만 맡을 수 있기 때문에 이 구분이 자동화에 중요합니다.
액세스 정책 관리자. 액세스 정책을 만들고 관리합니다. Microsoft는 권한이 높은 Intune Administrator Entra 역할 대신 사용자 지정 역할 참조의 Multi Admin Approval 그룹에 있는 네 가지 권한(Create access policy, Read access policy, Update access policy, Delete access policy)을 가진 사용자 지정 Intune 역할을 권장합니다. 그 그룹의 다섯 번째 권한인 Approval for Multi Admin Approval은 두 번째 관리자가 새 액세스 정책이나 변경된 정책을 승인할 때 필요한 권한입니다.
승인자. 다음 세 가지를 모두 충족해야 합니다. 액세스 정책에 할당된 승인자 그룹의 멤버일 것, 승인하는 유형에 대한 리소스별 Read 권한을 가질 것(예: 디바이스 삭제 승인에는 ManagedDevices/Read), 그리고 승인자 그룹 자체가 적어도 하나의 Intune 역할 할당에 멤버 그룹으로 추가되어 있을 것. 그룹이 역할에 할당되지 않으면 Microsoft는 “승인자 그룹 멤버가 주기적으로 그룹에서 제거된다"고 말하며, 제 경험상 이것이 승인이 갑자기 동작하지 않는 가장 흔한 원인입니다. 그룹은 보안 그룹이어야 하고(메일 그룹, Microsoft 365 그룹, 메일 사용 보안 그룹은 “승인자 멤버 자격을 확인하지 못한 채 조용히 실패"합니다), 중첩 멤버 자격은 “신뢰할 수 없는 동작을 일으킬 수 있으므로” 멤버는 직접 멤버여야 합니다.
변경 요청자. 해당 작업에 대한 일반 Intune RBAC 권한(MobileApps/Create, RemoteTasks/Wipe)이 필요합니다. 같은 계정이 요청을 제출하고 나중에 Complete를 선택합니다. 자동화에서는 이것이 서비스 주체이며, Microsoft는 분명히 말합니다. “애플리케이션은 MAA 요청을 승인하거나 거부할 수 없습니다. 대화형 관리자 계정만 요청을 승인할 수 있습니다.”
전역 관리자와 Intune 관리자를 포함해 누구도 자신의 요청을 승인할 수 없습니다. 기본적으로 참여하는 모든 관리자는 Intune 라이선스가 필요합니다. Allow access to unlicensed admins 설정으로 이 요건을 없앨 수 있지만, Microsoft는 이 설정을 되돌릴 수 없다고 경고합니다.
Graph 권한
아래는 beta 참조 페이지에 문서화된 권한입니다. MAA 전용 항목은 모두 beta에 있으며, v1.0 참조에는 operationApprovalPolicy나 operationApprovalRequest 리소스가 없습니다. Microsoft의 표준 주석도 적용됩니다. Intune beta API는 지원되지만 v1.0보다 자주 바뀝니다.
| 호출 | 문서화된 권한(위임과 애플리케이션) |
|---|---|
operationApprovalPolicies 목록, 조회, 생성, 업데이트, 삭제 | DeviceManagementRBAC.Read.All, DeviceManagementRBAC.ReadWrite.All |
operationApprovalRequests 목록 또는 조회 | DeviceManagementConfiguration.Read.All, DeviceManagementConfiguration.ReadWrite.All, DeviceManagementRBAC.Read.All, DeviceManagementRBAC.ReadWrite.All |
approve, reject, cancelMyRequest, retrieveMyRequests, retrieveRequestStatus | DeviceManagementConfiguration.Read.All, DeviceManagementRBAC.Read.All |
deviceManagementScript 생성(실습 예제) | DeviceManagementScripts.ReadWrite.All 또는 DeviceManagementConfiguration.ReadWrite.All |
auditEvents 목록(v1.0) | DeviceManagementApps.Read.All, DeviceManagementApps.ReadWrite.All |
Graph 권한은 필요조건이지 충분조건이 아닙니다. 그 위에서 Intune RBAC 검사가 계속 실행되므로, DeviceManagementConfiguration.ReadWrite.All은 있지만 Intune 역할 할당이 없는 앱은 같은 범위를 가진 위임 관리자와 다르게 동작합니다.
액세스 정책 설계
정책은 몇 개가 필요한가
각 액세스 정책은 프로필 유형 하나만 지원하므로, 앱, 스크립트, 디바이스 작업, RBAC, 규정 준수 및 구성 정책을 보호하려는 테넌트에는 최소 여섯 개의 정책이 필요합니다. 제 패턴은 유형별로 정책 하나씩 두고 엔드포인트 승인자 그룹 하나를 공유하며, Role 유형은 별도 정책으로 마지막에 만드는 것입니다. 앱 정책을 플랫폼별로 나누는 것(2024년 8월 릴리스에서 앱 정책을 Windows 애플리케이션, 비 Windows 애플리케이션 또는 둘 다로 제한할 수 있게 되었습니다)은 서로 다른 두 팀이 서로를 승인할 때만 합니다.
승인자 그룹
승인 계층마다 전용 보안 그룹을 만들고, Intune 역할(리소스 Read 권한을 가진 사용자 지정 역할로 충분합니다)에 직접 할당하고, 멤버는 직접 멤버로 유지합니다. 감사 로그가 읽기 쉽도록 이름을 붙입니다. SG-Intune-MAA-Approvers-Endpoint, SG-Intune-MAA-Approvers-RBAC처럼요. 요청이 만료되므로 각 그룹에 최소 두 명을 두고, 팔로우 더 선 방식으로 운영한다면 서로 다른 시간대에 배치합니다.
비상(브레이크글라스) 고려 사항
MAA에는 비상 우회 스위치가 없고 Intune은 알림을 보내지 않으므로 세 가지 장애 모드에 대비합니다.
- 승인자가 모두 부재. 요청은 3일 후 만료됩니다. 대책은 우회가 아니라 그룹 규모와 대기 당번 로테이션입니다.
- Role 교착 상태. Role 액세스 정책은 승인자 그룹에 필요한 역할 할당을 포함해 모든 RBAC 변경을 보호합니다. Microsoft가 문서화한 복구 절차는 Tenant administration > Multi Admin Approval > Access policies에서 Role 정책을 삭제하고, 3~5분 기다린 뒤, Tenant administration > Roles에서 할당을 고치고, 정책을 다시 만드는 것입니다. Role 유형을 켜기 전에 다른 모든 정책을 구성하고 RBAC를 검증하면 피할 수 있습니다.
- 기다릴 수 없는 자동화. Exclusions 탭으로 특정 엔터프라이즈 애플리케이션을 정책 하나에서 제외합니다. 제외는 앱 전용 토큰에만 적용되고(같은 앱의 위임 호출은 여전히 가로채집니다), 액세스 정책당 50개 애플리케이션이 상한이며, 두 번째 관리자의 승인이 필요하고, 모든 추가, 제거, 수정은 감사 로그에 기록됩니다. Microsoft의 경고는 직설적입니다. 각 제외는 “승인 워크플로에 틈을 만들며, 제외된 애플리케이션이 손상되면 악용될 수 있습니다”.
제외에 대한 제 규칙은 이렇습니다. 바꿀 수 없는 공급업체 도구에만, 필요한 정책 하나에만, 클라이언트 비밀 대신 인증서 자격 증명을, 그리고 알려진 IP 범위 밖에서 서비스 주체를 차단하는 워크로드 ID용 조건부 액세스 정책을 적용합니다. 이 정책에는 Workload Identities Premium 라이선스가 필요하고 테넌트에 등록된 단일 테넌트 서비스 주체만 다루지만, 승인 없이 디바이스를 초기화할 수 있는 앱에 대해서는 라이선스 비용을 들일 가치가 있습니다.
정책 만들기
관리 센터에서는 Tenant administration > Multi Admin Approval > Access policies > Create입니다. Basics에서 이름과 단일 Profile type을 설정하고, Approvers에서 Add groups를 선택하고, Exclusions에서 필요하면 Add enterprise applications를 선택하고, Review + submit for approval에서 Business justification을 입력한 뒤 Submit for approval을 선택합니다. Approval for Multi Admin Approval 권한을 가진 두 번째 관리자가 승인하면 첫 번째 관리자가 정책으로 돌아와 Complete를 선택합니다.
Graph에서는 POST 한 번입니다. 액세스 정책 자체가 보호되므로 이 호출에도 아래 설명하는 승인 흐름이 발생한다고 예상하십시오.
POST https://graph.microsoft.com/beta/deviceManagement/operationApprovalPolicies
Content-Type: application/json
{
"@odata.type": "#microsoft.graph.operationApprovalPolicy",
"displayName": "MAA - Windows scripts",
"description": "Second-admin approval for PowerShell script deployments",
"policyType": "script",
"policyPlatform": "notApplicable",
"policySet": {
"@odata.type": "microsoft.graph.operationApprovalPolicySet",
"policyType": "script",
"policyPlatform": "notApplicable"
},
"approverGroupIds": [ "<approver security group object ID>" ]
}GET https://graph.microsoft.com/beta/deviceManagement/operationApprovalPolicies는 기존 정책을 나열합니다. displayName은 128자, description은 1024자가 상한입니다.
사람 관리자의 승인 워크플로
제출
요청자는 일반적인 생성 또는 편집 흐름을 사용합니다. 저장 전 마지막 페이지에 Business justification 필드가 나타납니다. 승인자가 읽는 것이 바로 이것이므로 성실히 채운 뒤 제출합니다. 같은 개체에 대한 요청이 이미 보류 중이면 Intune은 메시지와 함께 새 요청을 차단합니다. 상태는 Tenant administration > Multi Admin Approval > My requests에서 볼 수 있고, 보류 중인 요청은 같은 곳에서 Cancel request로 철회할 수 있습니다.
승인 또는 거부
승인자는 Tenant administration > Multi Admin Approval > Received requests(개요는 이 목록을 All requests라고도 부릅니다)로 가거나, 통합된 Tenant administration > Admin tasks 창으로 갑니다. 후자는 MAA 요청을 Defender 보안 작업, Endpoint Privilege Management 권한 상승 요청과 함께 표시하며 열려면 Organization > Read가 필요합니다. Business justification 링크를 선택해 변경 내용을 읽고, Approver notes를 입력하고, Approve request 또는 Reject request를 선택합니다. 메모는 요청자의 My requests 보기로 되돌아가므로 거부 이유는 이렇게 전달됩니다. 2026년 4월 27일 주(서비스 릴리스 2604)부터 Change Review Agent에 제안이 있으면 My requests와 All requests 탭에 Windows PowerShell 스크립트 요청용 Agent Response 열도 표시됩니다. 이 통합은 미리 보기입니다.
완료
승인해도 변경이 적용되지는 않습니다. 원래 요청자가 요청을 열어 Complete를 선택하면 Intune이 처리하고 상태가 Completed로 바뀌며, 콘솔 알림이 성공 또는 실패를 확인해 줍니다. 자동화는 이 단계를 승인 코드를 붙인 재제출로 재현합니다.
수명 주기와 만료
| 관리 센터 상태 | Graph status 값 | 의미 |
|---|---|---|
| Needs approval | needsApproval | 승인자를 기다리는 중 |
| Approved | approved | 승인됨. 요청자의 Complete를 기다리는 중 |
| Completed | completed | 변경 적용됨 |
| Rejected | rejected | 승인자가 거부. 여전히 필요하면 새 요청 제출 |
| Canceled | cancelled | 요청자가 철회 |
| Expired | expired | 3일 이내에 더 처리되지 않음. 다시 제출해야 함 |
Microsoft의 정확한 표현은 이렇습니다. “요청이 3일 이내에 더 처리되지 않으면 Expired 상태가 되며 다시 제출해야 합니다. 각 상태 변경은 변경 후 최대 30일간 표시됩니다.” 모든 요청에는 expirationDateTime도 있으므로 자동화는 72시간을 하드코딩하지 말고 이 값을 지켜봐야 합니다.
자동화: Graph API 워크플로
엔드포인트와 API 버전
아래 내용은 모두 beta 엔드포인트입니다. 오늘 v1.0 대응 항목은 없으므로 코드를 이 URL에 고정하고, 옮겨 가는 날을 위해 Intune What’s New 시리즈를 지켜보십시오.
| 목적 | 요청 |
|---|---|
| 액세스 정책 목록 | GET /beta/deviceManagement/operationApprovalPolicies |
| 액세스 정책 생성 | POST /beta/deviceManagement/operationApprovalPolicies |
| 승인 요청 목록 | GET /beta/deviceManagement/operationApprovalRequests |
| 요청 하나 조회 | GET /beta/deviceManagement/operationApprovalRequests/{id} |
| 내 요청 | GET /beta/deviceManagement/operationApprovalRequests/retrieveMyRequests |
| 개체가 보류 중인 요청으로 잠겨 있는지 | POST /beta/deviceManagement/operationApprovalRequests/retrieveRequestStatus에 entityId와 entityType 포함 |
| 승인 | POST /beta/deviceManagement/operationApprovalRequests/{id}/approve |
| 거부 | POST /beta/deviceManagement/operationApprovalRequests/{id}/reject |
| 내 요청 취소 | POST /beta/deviceManagement/operationApprovalRequests/cancelMyRequest에 { "id": "..." } 포함 |
| 감사 추적 | GET /v1.0/deviceManagement/auditEvents |
앱 전용과 위임의 차이
승인 프로토콜은 두 토큰 유형 모두 동일하며 헤더와 상태 코드는 바뀌지 않습니다. 바뀌는 것은 누가 무엇을 할 수 있는가입니다.
- 앱 전용(클라이언트 자격 증명) 주체는 보호되는 변경을 제출하고, 요청 상태를 읽고, 승인 코드로 다시 제출하고, 정책에서 제외될 수 있습니다. 승인이나 거부는 할 수 없으며, 만든 요청에는
requestor.application에 앱이 기록됩니다. - 위임 토큰은 로그인한 관리자로 동작합니다. 제출과 읽기가 가능하고, 그 관리자가 승인자 그룹에 속하고 리소스 Read 권한을 가지면 다른 사람의 요청을 승인하거나 거부할 수 있습니다. 자신의 요청은 절대 승인할 수 없고, 앱이 제외되어 있어도 위임 호출에는 제외가 적용되지 않습니다.
설계상의 결론은 이렇습니다. 파이프라인 ID가 제출하고, 사람이 승인하고(포털에서, 또는 본인이 로그인한 세션에서 위임 Graph 호출로), 파이프라인 ID가 완료합니다. “승인자"로 두 번째 서비스 주체를 만들지 마십시오. Microsoft는 동작하지 않는다고 말하며, 목적 자체에 어긋납니다.
1단계: 근거 헤더를 붙여 제출
수정 요청에 x-msft-approval-justification을 추가합니다. 값은 Base64로 인코딩해야 하며, Microsoft 예제 값 YXBwIG9ubHkgdGVzdA==는 app only test로 디코딩됩니다. 아래 샘플은 Windows PowerShell 스크립트를 만들고, 그 헤더는 CHG0012345: deploy pilot script to Ring 0으로 디코딩됩니다.
POST https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts
Content-Type: application/json
x-msft-approval-justification: Q0hHMDAxMjM0NTogZGVwbG95IHBpbG90IHNjcmlwdCB0byBSaW5nIDA=
{
"@odata.type": "#microsoft.graph.deviceManagementScript",
"displayName": "BHG - Pilot script",
"description": "Created by automation under Multi Admin Approval",
"scriptContent": "V3JpdGUtT3V0cHV0ICJIZWxsbyBXb3JsZCI=",
"runAsAccount": "system",
"enforceSignatureCheck": false,
"fileName": "Pilot.ps1",
"runAs32Bit": false,
"roleScopeTagIds": ["0"]
}헤더가 없으면 호출은 x-msft-approval-justification 헤더가 필수라는 오류로 실패합니다. 방법 문서는 이를 HTTP 400으로 설명합니다.
2단계: 412를 읽고 승인 코드 보관
대부분의 스크립트가 여기서 깨집니다. 제출이 성공해도 201 Created가 돌아오지 않습니다. 바깥쪽 Graph 오류 코드가 BadRequest인 HTTP 412 Precondition Failed가 돌아오고, 응답에는 x-msft-approval-code 헤더가 있습니다. Microsoft의 지침은 이렇습니다. “이 헤더의 존재와 HTTP 412를 함께, MAA가 요청을 받아들여 승인 요청을 만들었다는 신호로 사용하십시오.” 메서드, URL, 본문, 코드를 저장하십시오. 나중에 네 가지가 모두 필요합니다.
HTTP/1.1 412 Precondition Failed
x-msft-approval-code: aabb1234-5678-9012-abcd-ef0123456789
Content-Type: application/json
{
"error": {
"code": "BadRequest",
"message": "{ \"Message\": \"Approval Required. Request Approval using the request ID returned as part of the x-msft-approval-code response header. x-msft-approval-code: aabb1234-5678-9012-abcd-ef0123456789 ...\" }"
}
}3단계: 대기, 폴링, 알림
Intune은 누구에게도 알리지 않으므로 스크립트가 그 일을 해야 합니다. 승인 코드는 요청 ID이므로 요청을 직접 읽거나 Microsoft가 방법 문서에서 보여 주는 필터 형식을 사용할 수 있습니다. 비즈니스 근거와 Tenant administration > Multi Admin Approval로 가는 안내를 승인자가 실제로 보는 곳에 게시하고, expirationDateTime으로 언제 에스컬레이션할지 결정하십시오.
GET https://graph.microsoft.com/beta/deviceManagement/operationApprovalRequests/aabb1234-5678-9012-abcd-ef0123456789GET https://graph.microsoft.com/beta/deviceManagement/operationApprovalRequests?$filter=requestId eq 'aabb1234-5678-9012-abcd-ef0123456789'제출하기 전에 retrieveRequestStatus를 쓰면 대상 개체가 다른 사람의 보류 중인 요청으로 이미 잠겨 있는지(entityLocked: true) 알 수 있어, “이미 보류 중” 오류를 만나는 대신 빨리 실패할 수 있습니다.
4단계: Graph로 승인
사람 승인자는 포털 대신 위임 토큰으로 Graph에서 처리할 수 있습니다. justification은 1024자가 상한이고, approvalSource는 선택 사항이며 기본값은 unknown입니다.
POST https://graph.microsoft.com/beta/deviceManagement/operationApprovalRequests/aabb1234-5678-9012-abcd-ef0123456789/approve
Content-Type: application/json
{
"justification": "Approved per CHG0012345 after reviewing the script content in the request pane",
"approvalSource": "adminConsole"
}거부는 /reject에 대해 같은 형태입니다. 둘 다 문자열 본문과 함께 200 OK를 반환합니다. 이 작업들의 권한 표에는 애플리케이션 권한이 나열되어 있지만, 방법 문서는 애플리케이션이 승인이나 거부를 할 수 없다고 단언하므로 위임 경로만 동작하는 것으로 취급하십시오.
5단계: 승인 코드를 붙여 다시 제출
status가 approved가 되면 근거 헤더를 x-msft-approval-code로 바꿔 동일한 요청을 다시 보냅니다. 이것이 요청자가 Complete를 선택하는 것의 API 버전이며, 정상적인 201 Created와 새 개체를 반환합니다.
POST https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts
Content-Type: application/json
x-msft-approval-code: aabb1234-5678-9012-abcd-ef0123456789
{ ...the same body as Step 1... }PowerShell 전체 실습 예제
Microsoft Graph PowerShell SDK의 Connect-MgGraph와 Invoke-MgGraphRequest를 사용합니다. Microsoft.Graph.Beta.DeviceManagement.Administration 참조에서 작업 승인 엔드포인트 전용 cmdlet을 찾지 못했고, Invoke-MgGraphRequest는 “아직 대응 cmdlet이 없는 API"를 위해 Microsoft가 문서화한 경로입니다. -SkipHttpErrorCheck는 412가 예외를 던지지 않게 하고, -ResponseHeadersVariable과 -StatusCodeVariable은 헤더와 상태 코드를 잡아냅니다.
첫 번째 스크립트는 인증서로 앱 전용 연결을 하고, Windows PowerShell 스크립트를 제출하고, 승인을 기다린 뒤 완료합니다.
#Requires -Modules Microsoft.Graph.Authentication
<#
Submit-IntuneScriptWithMaa.ps1
App-only automation that creates a Windows PowerShell script in Intune under Multi Admin Approval.
The app registration needs DeviceManagementScripts.ReadWrite.All (create the script) and
DeviceManagementConfiguration.Read.All or DeviceManagementRBAC.Read.All (read approval requests).
#>
param(
[Parameter(Mandatory)] [string] $TenantId,
[Parameter(Mandatory)] [string] $ClientId,
[Parameter(Mandatory)] [string] $CertificateThumbprint,
[string] $Justification = "CHG0012345: deploy pilot script to Ring 0",
[int] $PollIntervalSeconds = 300
)
Connect-MgGraph -ClientId $ClientId -TenantId $TenantId -CertificateThumbprint $CertificateThumbprint -NoWelcome
$uri = "https://graph.microsoft.com/beta/deviceManagement/deviceManagementScripts"
$scriptText = 'Write-Output "Hello from Intune"'
$body = @{
"@odata.type" = "#microsoft.graph.deviceManagementScript"
displayName = "BHG - Pilot script"
description = "Created by automation under Multi Admin Approval"
scriptContent = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($scriptText))
runAsAccount = "system"
enforceSignatureCheck = $false
fileName = "Pilot.ps1"
runAs32Bit = $false
roleScopeTagIds = @("0")
} | ConvertTo-Json -Depth 5
# Step 1: submit with the Base64 justification header. Expect HTTP 412 plus x-msft-approval-code.
$encodedJustification = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($Justification))
$null = Invoke-MgGraphRequest -Method POST -Uri $uri -Body $body -ContentType "application/json" `
-Headers @{ "x-msft-approval-justification" = $encodedJustification } `
-SkipHttpErrorCheck -ResponseHeadersVariable responseHeaders -StatusCodeVariable statusCode
if ($statusCode -eq 201) {
Write-Output "Created immediately: scripts are not MAA-protected in this tenant, or this app is excluded."
return
}
# Step 2: the 412 is the success signal. Pull the approval code out of the response headers.
$codeKey = $responseHeaders.Keys | Where-Object { $_ -ieq "x-msft-approval-code" } | Select-Object -First 1
if ($statusCode -ne 412 -or -not $codeKey) {
throw "Unexpected HTTP $statusCode without an approval code. Check Graph permissions and Intune RBAC."
}
$approvalCode = @($responseHeaders[$codeKey])[0]
Write-Output "Approval request $approvalCode created. Waiting for a second administrator."
# Step 3: notify your approvers here (ticket, chat, email). Intune sends nothing on its own.
# Then poll the request by ID until it leaves needsApproval. The service expires it after 3 days.
$requestUri = "https://graph.microsoft.com/beta/deviceManagement/operationApprovalRequests/$approvalCode"
do {
Start-Sleep -Seconds $PollIntervalSeconds
$request = Invoke-MgGraphRequest -Method GET -Uri $requestUri
Write-Output ("{0:u} status={1} expires={2}" -f (Get-Date), $request.status, $request.expirationDateTime)
} while ($request.status -eq "needsApproval")
if ($request.status -ne "approved") {
throw "Request $approvalCode ended as '$($request.status)'. Approver notes: $($request.approvalJustification)"
}
# Step 5: replay the identical request with the approval code. This is the API form of 'Complete'.
$created = Invoke-MgGraphRequest -Method POST -Uri $uri -Body $body -ContentType "application/json" `
-Headers @{ "x-msft-approval-code" = $approvalCode }
Write-Output "Script created with id $($created.id). Approved by $($request.approver.user.displayName)."승인자 쪽 절반은 승인자 그룹 멤버로 로그인해 대화형으로 실행합니다. 대기 중인 요청을 나열하고, 누가 왜 요청했는지 보여 주고, 승인자가 관리 센터에서 변경 내용을 읽은 뒤 요청 하나를 승인합니다.
# Approve-IntuneMaaRequests.ps1 - delegated session for a human approver
Connect-MgGraph -Scopes "DeviceManagementRBAC.Read.All" -NoWelcome
$all = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/deviceManagement/operationApprovalRequests"
$pending = @($all.value | Where-Object { $_.status -eq "needsApproval" })
foreach ($r in $pending) {
$who = if ($r.requestor.application.displayName) { $r.requestor.application.displayName } else { $r.requestor.user.displayName }
Write-Output "$($r.id) from $who types=$($r.requiredOperationApprovalPolicyTypes -join ',') expires $($r.expirationDateTime)"
Write-Output " justification: $($r.requestJustification)"
}
$id = Read-Host "Request ID to approve"
$decision = @{
justification = "Approved per CHG0012345 after reviewing the script content in the admin center"
approvalSource = "adminConsole"
} | ConvertTo-Json
Invoke-MgGraphRequest -Method POST -Body $decision -ContentType "application/json" `
-Uri "https://graph.microsoft.com/beta/deviceManagement/operationApprovalRequests/$id/approve"스크립트 개체가 만들어지면 할당하고, 테스트 디바이스에서 Intune 정책 동기화를 강제해 적용되는 것을 확인합니다. 할당도 보호되는 리소스의 수정이므로 그룹을 추가할 때 412 왕복이 한 번 더 있을 것으로 예상하십시오.
매일 밤 Win32 앱 업데이트를 게시하는 Azure Automation Runbook을 이 패턴으로 옮긴다면 첫 주에 두 가지를 예상하십시오. Runbook이 샌드박스를 몇 시간씩 붙잡아 두지 않도록 승인 코드를 키로 하는 별도의 재개 작업이 필요하고, 승인자들은 근거가 변경 내용을 설명하는 대신 변경 티켓 번호로 시작하길 원할 것입니다. 그들이 실제로 여는 것은 티켓이기 때문입니다.
운영 지침
SLA, 만료, 알림
내부 승인 SLA는 3일 만료 기한보다 충분히 안쪽에 둡니다. 저는 앱과 스크립트는 업무 시간 4시간, 디바이스 작업과 RBAC 변경은 1시간으로 정하고, 티켓 시스템에 대기 승인자를 지정합니다. Intune이 알림을 보내지 않으므로 알림 장치는 제출 스크립트에 넣고, retrieveMyRequests나 목록 엔드포인트를 호출해 needsApproval 상태로 expirationDateTime까지 24시간이 채 남지 않은 요청을 끌어올리는 예약 작업으로 보강합니다. 30일 표시 기간도 기억하십시오. 그 뒤에는 요청이 콘솔에서 사라지므로 증빙으로 필요한 것은 내보내 두어야 합니다.
감사와 보고
Graph 방법 문서는 MAA 이벤트가 “승인, 차단, 통과, 제외 추가, 제외 제거를 포함해” 기존 Intune 감사 로그에 기록된다고 확인합니다. 관리 센터에서는 Tenant administration > Audit logs이며 Add filters > Category와 Activity로 필터링합니다. Export는 CSV를 쓰고, 같은 창에서 콘솔 표시 범위를 넘는 보존을 위해 로그를 Azure Monitor로 라우팅할 수 있습니다. 감사 로그 문서에 따르면 보기에는 Intune Administrator 역할 또는 Audit data > Read가 있는 Intune 역할이 필요합니다. 프로그래밍 방식으로는 GET https://graph.microsoft.com/v1.0/deviceManagement/auditEvents가 2년 분량의 이벤트를 반환하며, 각 이벤트의 actor 블록에 applicationId, servicePrincipalName, userPrincipalName이 있어 보고서에서 자동화 승인과 사람 승인을 구분할 수 있습니다.
자주 발생하는 오류
| 증상 | 가능한 원인 | 해결 |
|---|---|---|
HTTP 400과 x-msft-approval-justification이 필수라는 메시지 | 보호되는 리소스, 헤더 누락 | Base64 헤더를 추가해 다시 전송 |
HTTP 412 Precondition Failed, 코드 BadRequest, x-msft-approval-code 헤더 존재 | 예상된 동작. 승인 요청이 생성됨 | 코드 저장, 폴링, 알림, 재제출 |
| HTTP 403 Forbidden | 요청자의 Graph 권한 또는 Intune RBAC 누락. Microsoft의 2026년 6월 발표도 MAA 차단을 403으로 설명 | 앱의 권한과 역할 할당을 확인한 뒤 제외를 확인 |
“이 개체에 대한 요청이 이미 보류 중” 또는 entityLocked: true | 같은 개체에 다른 요청이 열려 있음 | 기다리거나 그 요청자에게 취소 요청 |
상태 expired | 3일 이내에 아무도 처리하지 않음 | 근거 헤더를 붙여 다시 제출. 새 코드가 발급됨 |
| 승인자가 요청을 보거나 승인할 수 없음 | 그룹이 Intune 역할에 미할당, 보안 그룹이 아님, 중첩 멤버 자격, 리소스 Read 권한 누락 | 필수 조건 목록에 맞춰 그룹 수정 |
| 자신의 요청 승인 실패 | 설계상 허용되지 않음 | 다른 관리자가 승인 |
| Role 정책 활성화 후 RBAC 변경 차단 | Role 교착 상태 | Role 정책 삭제, 3~5분 대기, RBAC 수정, 재생성 |
| 승인 코드를 붙인 재제출 실패 | 원래 요청과 다르거나 기간이 지남 | Microsoft는 같은 요청을 다시 제출하라고 함. 변경 없이 신속히 재전송 |
조치 체크리스트
- Tenant administration > Multi Admin Approval > Access policies를 열어 활성 정책을 나열합니다. 하나도 없다면 API 적용은 아직 해당되지 않으며, 물려받는 대신 의도적으로 설계할 때입니다.
- Entra ID > Enterprise apps에서 Intune 쓰기 권한을 가진 서비스 주체를 모두 조사하고, 각각이 건드리는 보호 유형(앱, 스크립트, 디바이스 작업, RBAC, 규정 준수, 구성, 디바이스 범주)에 매핑합니다.
- 승인자 그룹을 먼저 바로잡습니다. 보안 그룹만, 직접 멤버, 그룹당 최소 두 명, 각 그룹을 리소스 Read 권한을 가진 Intune 역할에 직접 할당합니다.
- 프로필 유형마다 액세스 정책을 하나씩 만듭니다. 교착 상태를 피하려면 Role 유형은 나머지를 검증한 뒤로 미룹니다.
- 보호 유형을 수정하는 모든 스크립트와 Runbook을 업데이트합니다.
x-msft-approval-justification(Base64)을 보내고,x-msft-approval-code가 있는 HTTP 412를 성공으로 처리하고, 메서드, URL, 본문, 코드를 보존하고,operationApprovalRequests/{id}를 폴링하고, 승인자에게 알리고,x-msft-approval-code로 다시 보냅니다. - 제외는 의도적으로 결정합니다. 바꿀 수 없는 공급업체 도구에만, 각각 정책 하나에만, 인증서 자격 증명, 워크로드 ID용 조건부 액세스 정책, 그리고 분기별 목록 검토입니다.
- 승인 SLA를 문서화하고, 승인자를 대기 당번에 두고, 위의 위임 PowerShell 또는 Admin tasks 창을 승인자에게 제공합니다.
- Intune 감사 로그를 Azure Monitor로 라우팅하고, MAA 승인, 차단, 통과 이벤트를
actor.applicationId와actor.userPrincipalName으로 나누는 보고서 하나를 만듭니다. - 전체 흐름을 한 번 파일럿합니다. 412 왕복으로 스크립트를 만들고, 할당하고, 테스트 디바이스에서 동기화를 강제하고, 감사 항목이 감사자가 기대하는 대로 읽히는지 확인합니다.
- 코드를 beta URL에 고정하고, v1.0으로의 이전과 새 보호 유형에 대해 Intune What’s New 시리즈를 지켜봅니다. 열거형은 이미 Microsoft가 향하는 곳을 암시합니다.
손이 하나 더 필요하신가요? Big Hat Group은 미국과 캐나다의 조직에 2주 상태 점검부터 전체 마이그레이션까지 Microsoft Intune 컨설팅을 제공합니다. 디스커버리 콜을 예약해 환경에 대해 이야기해 보십시오.
참고 자료
- Use Multi Admin Approval in Intune - Microsoft Intune
- Use Multi Admin Approval with the Microsoft Graph API - Microsoft Intune
- What’s new in Microsoft Intune (2026년 4월 27일과 6월 22일 주)
- What’s new in Microsoft Intune archive (2023년 8월, 2024년 8월, 2025년 8월과 9월, 2026년 3월 항목)
- Create a custom role in Intune - Microsoft Intune
- Centrally manage Admin Tasks - Microsoft Intune
- Audit changes and events in Microsoft Intune - Microsoft Intune
- operationApprovalPolicy resource type - Microsoft Graph beta
- Create operationApprovalPolicy - Microsoft Graph beta
- List operationApprovalPolicies - Microsoft Graph beta
- operationApprovalRequest resource type - Microsoft Graph beta
- List operationApprovalRequests - Microsoft Graph beta
- Get operationApprovalRequest - Microsoft Graph beta
- approve action - Microsoft Graph beta
- reject action - Microsoft Graph beta
- cancelMyRequest action - Microsoft Graph beta
- retrieveMyRequests function - Microsoft Graph beta
- retrieveRequestStatus action - Microsoft Graph beta
- operationApprovalRequestEntityStatus resource type - Microsoft Graph beta
- Create deviceManagementScript - Microsoft Graph beta
- List auditEvents - Microsoft Graph v1.0
- Connect-MgGraph (Microsoft.Graph.Authentication)
- Invoke-MgGraphRequest (Microsoft.Graph.Authentication)
- Microsoft Entra Conditional Access for workload identities