Infrastructure

Microsoft Entra PIM: diagnose Azure role activation before granting permanent access

A production runbook for separating PIM eligibility, activation, approval, Conditional Access, RBAC propagation and effective ARM access before any permanent bypass.

15 Sept 2026 azuremicrosoft-entrapimrbacidentityconditional-accesssecurityobservabilityincidentrunbookrollbackproduction

A production incident is open. An operator needs to change an Azure resource, activates the Contributor role through Microsoft Entra Privileged Identity Management, and still receives AuthorizationFailed. Under pressure, a permanent assignment at subscription scope looks like the quickest way forward.

That workaround collapses three separate states. The operator may be eligible for the role, the activation request may be missing or awaiting approval, and a successful activation may not yet be reflected by the token or client in use. This runbook finds the broken stage before access is widened. Its output is bounded: correct the request, wait for approval, refresh the access context, invoke a controlled emergency procedure, or stop the intervention.

Freeze the denied action and expected access

Start with one exact operation. “PIM is broken” does not tell you whether activation failed or the requested action sits outside the role or scope. Preserve the identity, role, scope, UTC time, ARM action and Azure correlation ID.

yaml pim-activation-incident.yml
incident: inc-20260915-017
operator_upn: <operator-upn>
operator_object_id: <principal-object-id>
tenant_id: <tenant-id>
expected_role: Contributor
eligible_scope: /subscriptions/<subscription-id>/resourceGroups/rg-payments-prod
target_resource_id: /subscriptions/<subscription-id>/resourceGroups/rg-payments-prod/providers/<provider>/<type>/<name>
requested_action: <provider>/<resource-type>/write
failed_at_utc: <timestamp>
arm_correlation_id: <correlation-id>

activation:
request_id: <request-id-or-none>
requested_scope: <scope>
requested_duration: PT1H
status: <not-created|pending-approval|active|failed|expired>

stop_conditions:
- no eligible assignment exists at the expected scope
- requested action is outside the role definition
- activation targets another tenant, subscription or principal
- emergency access has no owner, expiry or revocation path

Then replay a read-only operation or what-if close to the intended action. Do not validate access by directly changing the incident resource. A partial success complicates rollback, and a second attempt might duplicate the effect.

Separate eligibility, request and active assignment

PIM does not turn eligibility into standing permission. For an Azure role, eligibility allows a request; activation creates a temporary active assignment; effective authorization then depends on the role, scope, optional conditions and access context presented to ARM.

text pim-access-chain.txt
1. ELIGIBILITY
 The principal is eligible for the right role at the right scope.

2. REQUEST
 SelfActivate carries the right principal, role, scope and duration.
 MFA, justification, ticket, Conditional Access and approval are satisfied.

3. ACTIVE ASSIGNMENT
 The request is approved and an active instance exists for the expected window.

4. EFFECTIVE ACCESS
 The client uses the right tenant and a refreshed context.
 The role allows the action and no condition or deny assignment blocks it.

Decision
 Repair the first unproven stage without widening later stages.

This model prevents two false diagnoses. An “active” row in PIM does not prove that Contributor permits role management. Conversely, an immediate AuthorizationFailed after activation does not prove that the assignment is absent: the portal, shell or application may still be using an earlier context.

Read PIM instances at the actual scope

First verify the shell tenant and subscription, then query eligibility and assignment instances at the affected scope. The PIM scheduling APIs distinguish schedules, their effective instances and the requests that change them.

bash 01-read-pim-state.sh
PRINCIPAL_ID="<principal-object-id>"
SCOPE="/subscriptions/<subscription-id>/resourceGroups/rg-payments-prod"

az account show \
--query '{tenantId:tenantId,subscriptionId:id,user:user.name}' \
--output yaml

az rest --method get \
--url "https://management.azure.com$SCOPE/providers/Microsoft.Authorization/roleEligibilityScheduleInstances?api-version=2020-10-01&$filter=principalId%20eq%20'$PRINCIPAL_ID'" \
--query 'value[].{name:name,principalId:properties.principalId,roleDefinitionId:properties.roleDefinitionId,scope:properties.scope,start:properties.startDateTime,end:properties.endDateTime}' \
--output yaml

az rest --method get \
--url "https://management.azure.com$SCOPE/providers/Microsoft.Authorization/roleAssignmentScheduleInstances?api-version=2020-10-01&$filter=principalId%20eq%20'$PRINCIPAL_ID'" \
--query 'value[].{name:name,assignmentType:properties.assignmentType,roleDefinitionId:properties.roleDefinitionId,scope:properties.scope,start:properties.startDateTime,end:properties.endDateTime}' \
--output yaml

Keep complete role and scope identifiers. The same role name can appear at several levels, and eligibility on one resource group says nothing about its neighbor. If eligibility comes through a PIM-managed group or the assignment has an RBAC condition, record that layer instead of reducing the diagnostic to the user alone.

Qualify the activation request

An activation can fail to produce an active assignment. The request may start in the future, target a reduced scope different from the selected filter, await approval, be denied, or expire. Read My requests and the ARM request before calling the issue propagation delay.

bash 02-read-activation-requests.sh
SCOPE="/subscriptions/<subscription-id>/resourceGroups/rg-payments-prod"

az rest --method get \
--url "https://management.azure.com$SCOPE/providers/Microsoft.Authorization/roleAssignmentScheduleRequests?api-version=2020-10-01" \
--query 'value[].{name:name,status:properties.status,requestType:properties.requestType,principalId:properties.principalId,roleDefinitionId:properties.roleDefinitionId,scope:properties.scope,createdOn:properties.createdOn,condition:properties.condition}' \
--output table

Outside the live incident, filter by principalId, role and a narrow UTC window so an old request cannot masquerade as the current activation. When approval is required, PendingApproval is a workflow decision, not an RBAC outage. Define at least two operational approvers before the incident; adding permanent access while the request waits bypasses the control precisely when it should matter.

If no request exists, go back to the activation requirements: MFA, justification, ticket information, allowed duration and any Conditional Access authentication context. An error before submission cannot be fixed by waiting for Azure propagation.

Correlate Conditional Access, MFA and session identity

A Conditional Access policy can require an authentication context, stronger method or compliant device at activation time. Verify the actual signed-in account and the corresponding sign-in event. An administrator session open in another tenant, or a browser carrying multiple identities, easily produces misleading evidence.

When SigninLogs and AuditLogs are routed to Log Analytics, query a short window around the request:

kusto pim-activation-evidence.kql
let principal = "<operator-upn>";
let start = datetime(<start-utc>);
let stop = datetime(<stop-utc>);
union isfuzzy=true
(
SigninLogs
| where TimeGenerated between (start .. stop)
| where UserPrincipalName =~ principal
| project TimeGenerated, Evidence="SignIn", CorrelationId,
          ResultType, ResultDescription,
          ConditionalAccessStatus, AuthenticationRequirement
),
(
AuditLogs
| where TimeGenerated between (start .. stop)
| where tostring(InitiatedBy.user.userPrincipalName) =~ principal
| where Category has "RoleManagement" or LoggedByService has "PIM"
| project TimeGenerated, Evidence="Audit", CorrelationId,
          ResultType=tostring(Result), ResultDescription=tostring(ResultReason),
          ConditionalAccessStatus="", AuthenticationRequirement=OperationName
)
| order by TimeGenerated asc

No row does not prove success or failure when export is disabled, retention misses the incident, or the request was initiated in another tenant. In that case, use PIM audit history and the source tenant sign-in logs, then attach the exports to the incident record.

Prove ARM access with a refreshed context

A validated activation normally creates the active assignment quickly. The client can still retain an older authorization decision or access context. Open a fresh shell or explicitly renew the sign-in, select the subscription, and test a read against the exact resource ID.

bash 03-refresh-and-test-arm-access.sh
TENANT_ID="<tenant-id>"
SUBSCRIPTION_ID="<subscription-id>"
TARGET_RESOURCE_ID="<resource-id>"

az logout
az login --tenant "$TENANT_ID"
az account set --subscription "$SUBSCRIPTION_ID"

az account get-access-token \
--resource https://management.azure.com/ \
--query '{tenant:tenant,expiresOn:expiresOn}' \
--output yaml

az resource show \
--ids "$TARGET_RESOURCE_ID" \
--query '{id:id,name:name,type:type}' \
--output yaml

Refreshing the context is a test, not a universal fix. If it still fails, compare the denied action with the effective role. Also check for an RBAC condition, deny assignment, resource lock or Azure Policy decision that can deny an operation after authentication and role activation have succeeded.

Keep a control test: an allowed read at the same scope, or the same read by an operator with a known active role. It separates a general ARM failure from a principal- or scope-specific problem.

Bound emergency access without making privilege permanent

A critical incident might not wait for the normal PIM path to be repaired. The emergency procedure must still be prepared, narrow and revocable. It is not a subscription-wide Owner assignment “to see whether it works.”

yaml emergency-access-contract.yml
trigger:
- confirmed_user_impact
- required_action_is_time_critical
- normal_pim_path_is_blocked_and_evidenced

grant:
principal: <named-emergency-operator>
role: <smallest-role-that-allows-the-action>
scope: <smallest-resource-or-resource-group>
owner: <incident-commander>
expires_at_utc: <timestamp>
ticket: <incident-id>

controls:
- second-person approval
- no shared account
- activity logging preserved
- exact action and validation recorded
- revocation command prepared before grant

close:
- remove emergency assignment
- verify access is removed with a fresh session
- restore or repair the PIM path
- review why the standard activation was unavailable

If the emergency mechanism cannot enforce expiry automatically, create the revocation action, its owner and a pre-expiry alert at the same time as the grant. An assignment is not temporary because its ticket says so.

Decide repair, wait, emergency or stop

text pim-activation-decision.txt
REPAIR THE REQUEST
Eligibility exists, but principal, role, scope, start time or duration is wrong.
Cancel the useless pending request and submit an exact one.

WAIT FOR OR ESCALATE APPROVAL
The request is PendingApproval and the action can wait.
Contact the designated approver without bypassing the workflow.

REFRESH THE CONTEXT
The active assignment exists at the right scope.
Open a clean session, obtain a new ARM token and replay a no-effect test.

ENABLE EMERGENCY ACCESS
Impact is critical, PIM is unavailable or blocked, and the exact action is known.
Grant the smallest role at the smallest scope with owner, expiry and revocation.

STOP
Required role is uncertain, tenant is wrong, action is irreversible or scope is unbounded.
Do not turn uncertainty into permanent Owner access.

After the intervention, deactivate the PIM activation when it is no longer needed or let the planned window expire, revoke every emergency assignment, and test removal from a fresh session. Complete validation includes proof of the business action, final resource state, removal of temporary privilege and restoration of the standard PIM path.

Conclusion

An activation displayed in PIM is only one stage of the authorization path. Useful diagnosis connects eligibility, the request and its requirements, the active assignment, the context presented to ARM, and the exact action at the correct scope.

Grant permanent access only through a governance decision, never as a troubleshooting probe. During an incident, repair the first proven break or invoke an already bounded emergency path. Close with a validated action, privilege removed and an activation path that is operable again.