Configure custom extensions for PIM role activation (Preview)
In brief
The documentation adds postapproval extensions with deferred outcomes and asynchronous validation before assignment provisioning. It also describes collecting attributes such as cost center and project code, along with the required Graph permissions and endpoint configuration.
What Entra admins need to know
Administrators can add business-context collection and postapproval checks to PIM workflows. These custom extensions require an Entra ID Governance or Entra Suite license; Entra ID P2 alone does not include them.
This editorial summary was generated by AI from the documentation changes. Verify important details in the full Microsoft Learn article.
Documentation change
The comparison below shows only the changed extract. Use the full-page view for complete context.
- Enforce HR-based access rules, such as employment status or role eligibility.
- Integrate compliance workflows or audit systems.
- Apply dynamic approval logic based on business context.
- Run asynchronous validation after manual approval and before PIM provisions the assignment.
- Collect extra business context, such as a cost center or project code, during activation.
Custom extensions support PIM for Groups, PIM for Microsoft Entra roles, and PIM for Azure resources.
License Requirementsrequirements
Using Custom Extensionscustom extensions with PIM requires an active Microsoft Entra ID Governance (EIG) or Microsoft Entra Suite license. Microsoft Entra ID P2 alone doesn't include PIM custom extensions. For more information on licensing, see:information, see Microsoft Entra ID Governance licensing fundamentals.
Prerequisites
- An assignment of at least the Privileged Role Administrator role.
- A REST API endpoint that accepts HTTP POST requests and returns evaluation responses in the format PIM expects. The API can be built in any language and hosted on any platform.
- A Microsoft Entra ID application registration to secure communication between PIM and your custom extension endpoint.
- The Microsoft Graph API
betaendpoint is required because this feature is in preview. | Create or update a custom extension |PrivilegedAccess-CustomExt.ReadWrite.All| | Get or list custom extensions |PrivilegedAccess-CustomExt.Read.All|
How custom extensions work
When a user submits a role activation request, PIM:
- Evaluates the role management policy that applies to the request.
- Calls the customer-hosted REST endpoint when a custom extension is configured for the applicable policy stage.
- Sends the request information that the extension needs to evaluate the activation.
- Waits for the extension to apply customer-defined business logic and return an outcome.
- Records the interaction for audit and traceability.
- Continues, denies, auto-approves, or defers the request based on the extension type and returned outcome.
Step 1 - Build your custom extension endpoint
Build a REST API that accepts HTTP POST requests from PIM. During role activation, PIM sends a request payload to your endpoint with the information needed to evaluate the activation. Your API evaluates the request and returns a response indicating whether the activation should be approved, auto-approved, or denied.
| Field | Type | Description |
|---|---|---|
evaluationId |
String | A unique identifier generated by the custom extension for the evaluation. This value is logged in PIM audits for traceability. |
evaluationOutcome |
String | The result of the evaluation. Approved, AutoApproved, and Denied. A postapproval extension supports approved, denied, and deferred. |
reason |
List<String> | Messages that explain the evaluation outcome. These messages appear in the UI when a request is denied. |
The following examples show valid response payloads for each outcome: }
##**Deferred**: A postapproval extension needs more time to make a decision. PIM pauses the request until the extension calls `/updateRequest`:
```json
{
"evaluationId": "dddd3333-ee44-5555-66ff-777777aaaaaa",
"evaluationOutcome": "deferred",
"reason": ["Waiting for the change advisory board decision"]
}
Step 2 - Secure your custom extension service with Microsoft Entra ID
Secure communication between PIM and your custom extension REST API by registering a Microsoft Entra ID application and configuring token validation.
###
Step 2.A - Register a Microsoft Entra ID application for your custom extension service
Your custom extension service must be backed by a Microsoft Entra ID application. PIM uses this app registration to authenticate to your custom extension endpoint and to request an access token for the API. To register a Microsoft Entra ID application, do the followingfollow these steps:
1.
- Sign in to the
[Microsoft Entra admin center](https://entra.microsoft.com)Microsoft Entra admin center as at least an[Application Developer](~/identity/role-based-access-control/permissions-reference.md#application-developer)Application Developer.1. - Go to
**Identity**Identity >**Applications**Applications >**Appregistrations**registrations.1. - A redirect URI isn't required for this service-to-service registration.
1. - Select
**Register**Register.###
Configure the Application ID URI
After the app is created, configure an Application ID URI. The host name in the Application ID URI must match the host name of your custom extension endpoint URL.
1.
Enter a value that represents your API. The URI must end with the Application (client) ID of the app registration. For example, if your custom extension endpoint is
`https:https://api.contoso.com/webhooks/entra-role-and the Application (client) ID isassignments`assignments`00001111-00001111-aaaa-2222-bbbb-, set the Application ID URI to:3333cccc4444`3333cccc4444`api:api://api.contoso.com/00001111-aaaa-2222-bbbb-3333cccc4444` 1.3333cccc4444Select
**Save**Save. :::image type="content" source="media/privileged-identity-management-custom-extensions/app-registration-expose-api.png" alt-text="Screenshot of the app registrationto expose API page.that protects the PIM custom extension API." lightbox="media/privileged-identity-management-custom-extensions/app-registration-expose-api.png":::
Save the Application ID URI. You provide this value as the `resourceId`resourceId when you configure the custom extension in PIM.
### Validate bearer tokens in
Step 2.B - Verify that requests to your API
custom extension come from PIM
When PIM calls your custom extension REST API, it sends an HTTP Authorization header with a bearer token issued by Microsoft Entra ID. Implement the following token validation checks in your API:
- **
- Audience
claim**claim - Validate that the`aud`audclaim contains the Application ID URI you configured for your app registration.- ** - Issuer
claim**claim - Validate that the`iss`issclaim contains the Microsoft Entra issuer URL for your tenant:`https:https://login.microsoftonline.com/{tenantId}/v2..0`0##
Step 3 - Onboard a custom extension to PIM
Create a custom extension in your tenant so that PIM can call your endpoint during role activation requests. When you create the extension, you define the endpoint PIM calls, the Microsoft Entra ID resource used to secure that endpoint, and the PIM resource type the extension applies to.
Delegated Microsoft Graph permissions required
Use PrivilegedAccess-CustomExt.ReadWrite.All to create or update custom extensions. Use PrivilegedAccess-CustomExt.Read.All to read or list custom extensions.
Create a custom extension by using Microsoft Graph
Send a POST request to the Microsoft Graph API `beta`beta endpoint to create the custom extension:
```http
"@odata.type": "#microsoft.graph.roleManagementCustomCalloutExtension",
"displayName": "Role Assignment Ticket Validation",
"description": "Validates ticket info provided by requestor against ServiceNow",
"type": "postApproval",
"endpointConfiguration": {
"@odata.type": "#microsoft.graph.httpRequestEndpoint",
"targetUrl": "https://api.contoso.com/webhooks/entra-role-assignments"
"resourceId": "api://api.contoso.com/a0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1"
},
"resourceType": "entraGroups",
"customAttributes": [][
"costCenter",
"projectCode"
]
}
| Field | Type | Description |
|---|---|---|
id | String | Unique identifier assigned to the custom extension. |
displayName |
String | Friendly name shown for the custom extension in management experiences. |
description |
String | Description of what the custom extension does. |
type | String | Stage when PIM calls the extension. Valid values are preApproval and postApproval. |
endpointConfiguration | Object | Configuration for the HTTP endpoint that PIM calls. |
[email protected] | String | Endpoint configuration type. Use #microsoft.graph.httpRequestEndpoint. |
endpointConfiguration.targetUrl |
String | HTTPS endpoint that receives the custom extension call. |
clientConfiguration | Object | Timeout and retry configuration for calls to the endpoint. |
[email protected] | String | Client configuration type. Use #microsoft.graph.customExtensionClientConfiguration. |
clientConfiguration.timeoutInMilliseconds |
Integer | Maximum time PIM waits for the endpoint to respond. |
clientConfiguration.maximumRetries |
Integer | Maximum number of retry attempts if the call fails. |
authenticationConfiguration | Object | Microsoft Entra authentication configuration for the endpoint. |
[email protected] | String | Authentication configuration type. Use #microsoft.graph.azureAdTokenAuthentication. |
authenticationConfiguration.resourceId |
String | Application ID URI of the Microsoft Entra app that secures the endpoint. |
resourceType |
String | Identifies the PIM provider the extension applies to. |
customAttributes | String collection | Optional attribute names that collect extra information during role activation and include it in the extension request. |
A successful response returns an HTTP 201 Created status code with the created custom extension object.
Create a custom extension in the portalUI walkthrough
You can also create a custom extension by using the Microsoft Entra admin center:
Sign in to the Microsoft Entra admin center as at least a Privileged Role Administrator.
Go to Identity governance > Privileged Identity Management > Custom Extensions.
Select Create a custom extension.
On the Basics tab, enter a
name,name and description, and select the resourcetype fortype.Select Pre-approval or Post-approval as the custom
extension.extension type. A postapproval extension runs after manual approval and immediately before PIM provisions the assignment.:::::image type="content" source="media/privileged-identity-management-custom-extensions/create-custom-extension-basics.png" alt-text="Screenshot of thebasics of creatingBasics tab for a new PIM customextension.extension, including preapproval and postapproval extension types." lightbox="media/privileged-identity-management-custom-extensions/create-custom-extension-basics.png":::On the Endpoint configuration tab, enter the target URL, timeout, and maximum retries for your custom extension endpoint.
:::::image type="content" source="media/privileged-identity-management-custom-extensions/create-custom-extension-endpoint-configuration.endpoint.png" alt-text="Screenshot of thecreateendpoint configuration for a PIM customextension endpoint configuration.extension." lightbox="media/privileged-identity-management-custom-extensions/create-custom-extension-endpoint-configuration.endpoint.png":::On the API authentication tab, select the application registration that secures your custom extension endpoint.
:::::image type="content" source="media/privileged-identity-management-custom-extensions/create-custom-extension-api-authentication.png" alt-text="Screenshot ofcreatingthe API authentication configuration for a PIM customextension APIextension." lightbox="media/privileged-identity-management-custom-extensions/create-extension-authentication.png":::On the Custom attributes tab, add the names of any extra attributes that users must provide during activation. For more information, see Custom attributes.
:::image type="content" source="media/privileged-identity-management-custom-extensions/create-extension-custom-attributes.png" alt-text="Screenshot of the custom attributes configuration for a PIM custom extension." lightbox="media/privileged-identity-management-custom-extensions/create-extension-custom-attributes.png":::
On the Review + create tab, review the configuration, and select Create.
:::::image type="content" source="media/privileged-identity-management-custom-extensions/create-custom-extension-review-create.review.png" alt-text="Screenshot of theReview and create tab showingreview page for asummary of thenew PIM customextension configuration including basics, endpoint, and API authentication details.extension." lightbox="media/privileged-identity-management-custom-extensions/create-extension-review.png":::
After the custom extension is created, it appears in the Custom Extensions list.
:::image type="content" source="media/privileged-identity-management-custom-extensions/custom-extensions-list.png" alt-text="List of custom extensions available in Privileged Identity Management after creation.":::
Step 4 - Link a custom extension to PIM role settings
After you onboard a custom extension, link it to the role settings for each role where you want PIM to call the extension during activation. To update role settings for a group, you need the Owner role for that group.
Select the role you want to configure, such as Owner or Member, and open the role settings.
Select Edit.
Select
the checkbox forRequire pre-approval custom extension to activate or Require post-approval custom extension to activate.Select the custom extension you want to link to this role.
::To call the extension and record the result without enforcing a denial or call failure, enable Audit mode.:::image type="content" source="media/privileged-identity-management-custom-extensions/
edit-configure-role-setting-select-custom-extension.png" alt-text="Screenshot ofsetting thegroup role settingswhen selecting thewith a preapproval customextension.extension selected." lightbox="media/privileged-identity-management-custom-extensions/edit-configure-role-setting-select-custom-extension.png":::Select Save to apply the updated role settings.
After you save, the role setting details page confirms the custom extension is linked.
You can follow a similar process to link custom extensions to Azure resource roles and Microsoft Entra directory roles.
Step 5 - Verify the role activation flow
After you complete the configuration, verify that the custom extension works as expected by activating a role and confirming that PIM sends the request to your API and enforces the decision.
Sign in to the Microsoft Entra admin center as a user who is eligible for the role.
Go to Privileged Identity Management and activate the role that has the custom extension configured.
Provide the required
justificationjustification, ticket information, andticket information.custom attributes.:::image type="content" source="media/privileged-identity-management-custom-extensions/activate-role-with-ticket-info.png" alt-text="Screenshot of activating a role with ticket
information.information and custom attributes." lightbox="media/privileged-identity-management-custom-extensions/activate-role-with-ticket-info.png":::Submit the activation request.
Verify that your API endpoint receives the POST request from PIM.
Confirm that PIM enforces the decision returned by your custom extension:
- Approved: The activation proceeds through the normal PIM workflow, including any other approval steps.
- AutoApproved: The activation proceeds without requiring manual PIM approval.
- Denied: The activation is blocked, and the reason messages from your API response appear in the PIM UI.
The following example shows an activation denied by a custom extension that validated ticket information:
:::image type="content" source="media/privileged-identity-management-custom-extensions/
activation-extension-denied-custom-extension.activation.png" alt-text="Screenshot ofthea PIM activation deniedwithbecause the customextension.extension rejected the ticket number." lightbox="media/privileged-identity-management-custom-extensions/extension-denied-activation.png":::
Related contentPreapproval and postapproval extensions
Preapproval and postapproval extensions are blocking gates, but they run at different points in the activation workflow.
| Capability | preApproval | postApproval |
|---|---|---|
| Execution point | Before manual approval | After manual approval and immediately before assignment provisioning |
| Supported outcomes | Approved, Denied, AutoApproved | approved, denied, deferred |
| Skip manual approval | Yes, with AutoApproved | No, because manual approval is already complete |
| Deferred evaluation | No | Yes, through /updateRequest |
| Blocks | Manual approval and provisioning | Assignment provisioning |
| Common scenarios | Compliance prescreening, risk assessment, early denial, and conditional automatic approval | IT service management validation, security scanning, external approval, and asynchronous processing |
Use a preapproval extension to evaluate a request before a PIM approver sees it. Use a postapproval extension when custom logic must run after the built-in PIM approval stage but before PIM provisions the assignment.
Use a preapproval extension to:
Use a postapproval extension to:
A postapproval extension can return The extension must authenticate to the callback API by using the same Microsoft Entra app registration configured for the extension. PIM verifies that the calling application ID matches the registered application. The callback can't return The deferred flow works as follows:
Use:
Use:
Use:
The callback request body supports the following properties:
Handle callback failures based on the returned HTTP status:
Custom attributes collect extra key-value context during activation and include it in the extension request. Use custom attributes when your extension needs information that isn't part of the standard request, such as a cost center, project code, business justification category, or identifier in an external system.
Add the attribute names when you create the extension. During activation, the user enters the corresponding values. PIM sends the values in a provider-specific format.
For Azure resource roles, PIM sends a For Microsoft Entra roles, PIM sends a JSON-encoded For PIM for Groups, PIM also sends a JSON-encoded Validate custom-attribute names and values in your API before using them in a query, workflow, or access decision.
The following table shows how the request status transitions based on the postapproval evaluation outcome and the callback decision:
When to use each extension type
Complete a deferred postapproval evaluation
deferred when it needs more time to make a decision. PIM places the request in PostApprovalExtensionPendingEvaluation and doesn't provision the role assignment. After the asynchronous process finishes, the extension calls /updateRequest on the original activation request with a final approved or denied decision.
deferred again.
deferred because it needs more time.PostApprovalExtensionPendingEvaluation./updateRequest endpoint with approved or denied.Microsoft Entra roles
POST https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignmentScheduleRequests/{requestId}/updateRequest
Content-Type: application/json
{
"evaluationOutcome": "approved",
"evaluationId": "aaaa0000-bb11-2222-33cc-444444dddddd",
"reason": "The external compliance system approved the request."
}
PIM for Groups
POST https://graph.microsoft.com/beta/identityGovernance/privilegedAccess/group/assignmentScheduleRequests/{requestId}/updateRequest
Content-Type: application/json
{
"evaluationOutcome": "denied",
"evaluationId": "aaaa0000-bb11-2222-33cc-444444dddddd",
"reason": "The external compliance system denied the request."
}
Azure resources
POST https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignmentScheduleRequests/{requestId}/updateRequest?api-version=2020-10-01
Content-Type: application/json
{
"evaluationOutcome": "denied",
"evaluationId": "aaaa0000-bb11-2222-33cc-444444dddddd",
"reason": [
"The change advisory board rejected ticket CHANGE-9921."
]
}
Callback endpoint summary
Provider
Endpoint
Microsoft Entra roles
POST https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignmentScheduleRequests/{requestId}/updateRequest
PIM for Groups
POST https://graph.microsoft.com/beta/identityGovernance/privilegedAccess/group/assignmentScheduleRequests/{requestId}/updateRequest
Azure resources
POST https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignmentScheduleRequests/{requestId}/updateRequest?api-version=2020-10-01
Property
Required
Description
evaluationOutcomeYes
Final outcome. Use
approved or denied; a callback can't return deferred.
evaluationIdYes
Correlation identifier from the original extension response.
reasonNo
Human-readable reason for the decision. Azure Resource Manager accepts a string collection; Microsoft Graph accepts a string.
scheduleInfoNo
Microsoft Graph only. Optional updated activation schedule for an approved request.
HTTP status
Cause
400 Bad RequestThe request isn't waiting for a callback, or the callback outcome isn't
approved or denied.
401 UnauthorizedThe calling application doesn't match the app registered for the custom extension.
404 Not FoundThe request ID doesn't exist.
Custom attributes
Azure resources
customAttributes array:
{
"customAttributes": [
{
"attributeName": "costCenter",
"attributeValue": "CC-4521"
},
{
"attributeName": "projectCode",
"attributeValue": "PROJ-ALPHA"
}
]
}
Microsoft Entra roles
customData string:
{
"customData": "{\"costCenter\":\"CC-4521\",\"projectCode\":\"PROJ-ALPHA\"}"
}
PIM for Groups
customData string:
{
"customData": "{\"costCenter\":\"CC-4521\",\"approverGroup\":\"finance-approvers\"}"
}
Provider
Field
Format
Azure resources
customAttributesArray of
attributeName and attributeValue objects.
Microsoft Entra roles
customDataJSON-encoded string.
PIM for Groups
customDataJSON-encoded string.
Quick reference
Extension type decision matrix
Scenario
preApprovalpostApproval
Compliance check before approvers
Best fit
Not applicable
Auto-approve low-risk requests
Best fit
Not supported
Block based on external data
Supported
Supported
Custom approval after PIM approval
Not applicable
Best fit
Asynchronous processing with callback
Not supported
Best fit
Security scan before provisioning
Not applicable
Best fit
Evaluation outcome quick reference
Outcome
preApproval effectpostApproval effectCallback effect
Approved / approvedContinues to manual approval.
Continues to provisioning.
Resolves a deferred request and provisions the role.
Denied / deniedDenies the request.
Denies the request.
Resolves a deferred request and denies it.
deferredNot supported.
Pauses and waits for
/updateRequest.Not supported; a callback can't defer again.
AutoApprovedSkips manual approval.
Not supported.
Not supported.
Postapproval request status transitions
Stage
Outcome
New status
Next step
Postapproval extension
approvedGranted
PIM provisions the assignment.
Postapproval extension
deniedDenied
The request ends.
Postapproval extension
deferredPostApprovalExtensionPendingEvaluationPIM waits for
/updateRequest.
Callback
approvedPostApprovalExtensionAdminApprovedPIM provisions the assignment.
Callback
deniedPostApprovalExtensionAdminDeniedThe request ends.
Related content
@@ -2,7 +2,7 @@ title: Configure custom extensions for PIM role activation (Preview) description: Learn how to configure custom extensions in Microsoft Entra Privileged Identity Management (PIM) to integrate custom business logic into role activation workflows. ms.topic: how-to-ms.date: 06/02/2026+ms.date: 10/12/2026 ms.custom: pim, msecd-doc-authoring-1012 ai-usage: ai-assisted @@ -20,6 +20,8 @@ Use custom extensions to: - Enforce HR-based access rules, such as employment status or role eligibility. - Integrate compliance workflows or audit systems. - Apply dynamic approval logic based on business context.+- Run asynchronous validation after manual approval and before PIM provisions the assignment.+- Collect extra business context, such as a cost center or project code, during activation. Custom extensions support PIM for Groups, PIM for Microsoft Entra roles, and PIM for Azure resources. @@ -28,13 +30,13 @@ Custom extensions support PIM for Groups, PIM for Microsoft Entra roles, and PIM > [!IMPORTANT] > Custom extensions for PIM are in preview. See the [Supplemental Terms of Use for Microsoft Azure Previews](https://azure.microsoft.com/support/legal/preview-supplemental-terms/) for legal terms that apply to Azure features that are in beta, preview, or otherwise not yet released into general availability. -## License Requirements+## License requirements -Using Custom Extensions with PIM requires an active Microsoft Entra ID Governance (EIG) or Entra Suite license. For more information on licensing, see: [Microsoft Entra ID Governance licensing fundamentals](~/id-governance/licensing-fundamentals.md).+Using custom extensions with PIM requires an active Microsoft Entra ID Governance or Microsoft Entra Suite license. Microsoft Entra ID P2 alone doesn't include PIM custom extensions. For more information, see [Microsoft Entra ID Governance licensing fundamentals](~/id-governance/licensing-fundamentals.md). ## Prerequisites -- An assignment of at least the **Privileged Role Administrator** role.+- An assignment of at least the **Privileged Role Administrator** role. - A REST API endpoint that accepts HTTP POST requests and returns evaluation responses in the format PIM expects. The API can be built in any language and hosted on any platform. - A Microsoft Entra ID application registration to secure communication between PIM and your custom extension endpoint. - The Microsoft Graph API `beta` endpoint is required because this feature is in preview.@@ -45,7 +47,18 @@ Using Custom Extensions with PIM requires an active Microsoft Entra ID Governanc | Create or update a custom extension | `PrivilegedAccess-CustomExt.ReadWrite.All` | | Get or list custom extensions | `PrivilegedAccess-CustomExt.Read.All` | -## Build your custom extension endpoint+## How custom extensions work++When a user submits a role activation request, PIM:++1. Evaluates the role management policy that applies to the request.+1. Calls the customer-hosted REST endpoint when a custom extension is configured for the applicable policy stage.+1. Sends the request information that the extension needs to evaluate the activation.+1. Waits for the extension to apply customer-defined business logic and return an outcome.+1. Records the interaction for audit and traceability.+1. Continues, denies, auto-approves, or defers the request based on the extension type and returned outcome.++## Step 1 - Build your custom extension endpoint Build a REST API that accepts HTTP POST requests from PIM. During role activation, PIM sends a request payload to your endpoint with the information needed to evaluate the activation. Your API evaluates the request and returns a response indicating whether the activation should be approved, auto-approved, or denied. @@ -132,7 +145,7 @@ After your custom extension evaluates the activation request, it must return a r | Field | Type | Description | |---|---|---| | `evaluationId` | String | A unique identifier generated by the custom extension for the evaluation. This value is logged in PIM audits for traceability. |-| `evaluationOutcome` | String | The result of the evaluation. Supported values: `Approved`, `AutoApproved`, `Denied`. |+| `evaluationOutcome` | String | The result of the evaluation. A preapproval extension supports `Approved`, `AutoApproved`, and `Denied`. A postapproval extension supports `approved`, `denied`, and `deferred`. | | `reason` | List\<String\> | Messages that explain the evaluation outcome. These messages appear in the UI when a request is denied. | The following examples show valid response payloads for each outcome:@@ -167,13 +180,23 @@ The following examples show valid response payloads for each outcome: } ``` -## Secure your custom extension service+**Deferred**: A postapproval extension needs more time to make a decision. PIM pauses the request until the extension calls `/updateRequest`:++```json+{+ "evaluationId": "dddd3333-ee44-5555-66ff-777777aaaaaa",+ "evaluationOutcome": "deferred",+ "reason": ["Waiting for the change advisory board decision"]+}+```++## Step 2 - Secure your custom extension service with Microsoft Entra ID Secure communication between PIM and your custom extension REST API by registering a Microsoft Entra ID application and configuring token validation. -### Register a Microsoft Entra ID application+### Step 2.A - Register a Microsoft Entra ID application for your custom extension service -Your custom extension service must be backed by a Microsoft Entra ID application. PIM uses this app registration to authenticate to your custom extension endpoint and to request an access token for the API. To register a Microsoft Entra ID application, do the following steps:+Your custom extension service must be backed by a Microsoft Entra ID application. PIM uses this app registration to authenticate to your custom extension endpoint and to request an access token for the API. To register a Microsoft Entra ID application, follow these steps: 1. Sign in to the [Microsoft Entra admin center](https://entra.microsoft.com) as at least an [Application Developer](~/identity/role-based-access-control/permissions-reference.md#application-developer). 1. Go to **Identity** > **Applications** > **App registrations**.@@ -183,7 +206,7 @@ Your custom extension service must be backed by a Microsoft Entra ID application 1. A redirect URI isn't required for this service-to-service registration. 1. Select **Register**. -### Configure the Application ID URI+#### Configure the Application ID URI After the app is created, configure an Application ID URI. The host name in the Application ID URI must match the host name of your custom extension endpoint URL. @@ -192,11 +215,11 @@ After the app is created, configure an Application ID URI. The host name in the 1. Enter a value that represents your API. The URI must end with the Application (client) ID of the app registration. For example, if your custom extension endpoint is `https://api.contoso.com/webhooks/entra-role-assignments` and the Application (client) ID is `00001111-aaaa-2222-bbbb-3333cccc4444`, set the Application ID URI to: `api://api.contoso.com/00001111-aaaa-2222-bbbb-3333cccc4444` 1. Select **Save**.- :::image type="content" source="media/privileged-identity-management-custom-extensions/app-registration-expose-api.png" alt-text="Screenshot of the app registration to expose API page." lightbox="media/privileged-identity-management-custom-extensions/app-registration-expose-api.png":::+ :::image type="content" source="media/privileged-identity-management-custom-extensions/app-registration-api.png" alt-text="Screenshot of the app registration that protects the PIM custom extension API." lightbox="media/privileged-identity-management-custom-extensions/app-registration-api.png"::: Save the Application ID URI. You provide this value as the `resourceId` when you configure the custom extension in PIM. -### Validate bearer tokens in your API+### Step 2.B - Verify that requests to your custom extension come from PIM When PIM calls your custom extension REST API, it sends an HTTP Authorization header with a bearer token issued by Microsoft Entra ID. Implement the following token validation checks in your API: @@ -204,10 +227,16 @@ When PIM calls your custom extension REST API, it sends an HTTP Authorization he - **Audience claim** - Validate that the `aud` claim contains the Application ID URI you configured for your app registration. - **Issuer claim** - Validate that the `iss` claim contains the Microsoft Entra issuer URL for your tenant: `https://login.microsoftonline.com/{tenantId}/v2.0`. -## Onboard a custom extension to PIM+## Step 3 - Onboard a custom extension to PIM Create a custom extension in your tenant so that PIM can call your endpoint during role activation requests. When you create the extension, you define the endpoint PIM calls, the Microsoft Entra ID resource used to secure that endpoint, and the PIM resource type the extension applies to. +### Delegated Microsoft Graph permissions required++Use `PrivilegedAccess-CustomExt.ReadWrite.All` to create or update custom extensions. Use `PrivilegedAccess-CustomExt.Read.All` to read or list custom extensions.++### Create a custom extension by using Microsoft Graph+ Send a POST request to the Microsoft Graph API `beta` endpoint to create the custom extension: ```http@@ -222,6 +251,7 @@ The following example creates a custom extension for PIM for Groups: "@odata.type": "#microsoft.graph.roleManagementCustomCalloutExtension", "displayName": "Role Assignment Ticket Validation", "description": "Validates ticket info provided by requestor against ServiceNow",+ "type": "postApproval", "endpointConfiguration": { "@odata.type": "#microsoft.graph.httpRequestEndpoint", "targetUrl": "https://api.contoso.com/webhooks/entra-role-assignments"@@ -236,7 +266,10 @@ The following example creates a custom extension for PIM for Groups: "resourceId": "api://api.contoso.com/a0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1" }, "resourceType": "entraGroups",- "customAttributes": []+ "customAttributes": [+ "costCenter",+ "projectCode"+ ] } ``` @@ -247,44 +280,58 @@ The following table describes the key fields in the custom extension API model: | Field | Type | Description | |---|---|---|+| `id` | String | Unique identifier assigned to the custom extension. | | `displayName` | String | Friendly name shown for the custom extension in management experiences. | | `description` | String | Description of what the custom extension does. |+| `type` | String | Stage when PIM calls the extension. Valid values are `preApproval` and `postApproval`. |+| `endpointConfiguration` | Object | Configuration for the HTTP endpoint that PIM calls. |+| `[email protected]` | String | Endpoint configuration type. Use `#microsoft.graph.httpRequestEndpoint`. | | `endpointConfiguration.targetUrl` | String | HTTPS endpoint that receives the custom extension call. |+| `clientConfiguration` | Object | Timeout and retry configuration for calls to the endpoint. |+| `[email protected]` | String | Client configuration type. Use `#microsoft.graph.customExtensionClientConfiguration`. | | `clientConfiguration.timeoutInMilliseconds` | Integer | Maximum time PIM waits for the endpoint to respond. | | `clientConfiguration.maximumRetries` | Integer | Maximum number of retry attempts if the call fails. |+| `authenticationConfiguration` | Object | Microsoft Entra authentication configuration for the endpoint. |+| `[email protected]` | String | Authentication configuration type. Use `#microsoft.graph.azureAdTokenAuthentication`. | | `authenticationConfiguration.resourceId` | String | Application ID URI of the Microsoft Entra app that secures the endpoint. | | `resourceType` | String | Identifies the PIM provider the extension applies to. |+| `customAttributes` | String collection | Optional attribute names that collect extra information during role activation and include it in the extension request. | A successful response returns an HTTP `201 Created` status code with the created custom extension object. -### Create a custom extension in the portal+### UI walkthrough You can also create a custom extension by using the Microsoft Entra admin center: 1. Sign in to the [Microsoft Entra admin center](https://entra.microsoft.com) as at least a [Privileged Role Administrator](~/identity/role-based-access-control/permissions-reference.md#privileged-role-administrator). 1. Go to **Identity governance** > **Privileged Identity Management** > **Custom Extensions**. 1. Select **Create a custom extension**.-1. On the **Basics** tab, enter a name, description, and resource type for the custom extension.+1. On the **Basics** tab, enter a name and description, and select the resource type.+1. Select **Pre-approval** or **Post-approval** as the custom extension type. A postapproval extension runs after manual approval and immediately before PIM provisions the assignment. - :::image type="content" source="media/privileged-identity-management-custom-extensions/create-custom-extension-basics.png" alt-text="Screenshot of the basics of creating custom extension." lightbox="media/privileged-identity-management-custom-extensions/create-custom-extension-basics.png":::+ :::image type="content" source="media/privileged-identity-management-custom-extensions/create-extension-basics.png" alt-text="Screenshot of the Basics tab for a new PIM custom extension, including preapproval and postapproval extension types." lightbox="media/privileged-identity-management-custom-extensions/create-extension-basics.png"::: 1. On the **Endpoint configuration** tab, enter the target URL, timeout, and maximum retries for your custom extension endpoint. - :::image type="content" source="media/privileged-identity-management-custom-extensions/create-custom-extension-endpoint-configuration.png" alt-text="Screenshot of the create custom extension endpoint configuration." lightbox="media/privileged-identity-management-custom-extensions/create-custom-extension-endpoint-configuration.png":::+ :::image type="content" source="media/privileged-identity-management-custom-extensions/create-extension-endpoint.png" alt-text="Screenshot of the endpoint configuration for a PIM custom extension." lightbox="media/privileged-identity-management-custom-extensions/create-extension-endpoint.png"::: 1. On the **API authentication** tab, select the application registration that secures your custom extension endpoint. - :::image type="content" source="media/privileged-identity-management-custom-extensions/create-custom-extension-api-authentication.png" alt-text="Screenshot of creating custom extension API authentication.":::+ :::image type="content" source="media/privileged-identity-management-custom-extensions/create-extension-authentication.png" alt-text="Screenshot of the API authentication configuration for a PIM custom extension." lightbox="media/privileged-identity-management-custom-extensions/create-extension-authentication.png":::++1. On the **Custom attributes** tab, add the names of any extra attributes that users must provide during activation. For more information, see [Custom attributes](#custom-attributes).++ :::image type="content" source="media/privileged-identity-management-custom-extensions/create-extension-custom-attributes.png" alt-text="Screenshot of the custom attributes configuration for a PIM custom extension." lightbox="media/privileged-identity-management-custom-extensions/create-extension-custom-attributes.png"::: 1. On the **Review + create** tab, review the configuration, and select **Create**. - :::image type="content" source="media/privileged-identity-management-custom-extensions/create-custom-extension-review-create.png" alt-text="Screenshot of the Review and create tab showing a summary of the custom extension configuration including basics, endpoint, and API authentication details.":::+ :::image type="content" source="media/privileged-identity-management-custom-extensions/create-extension-review.png" alt-text="Screenshot of the review page for a new PIM custom extension." lightbox="media/privileged-identity-management-custom-extensions/create-extension-review.png"::: After the custom extension is created, it appears in the **Custom Extensions** list. :::image type="content" source="media/privileged-identity-management-custom-extensions/custom-extensions-list.png" alt-text="List of custom extensions available in Privileged Identity Management after creation."::: -## Link a custom extension to PIM role settings+## Step 4 - Link a custom extension to PIM role settings After you onboard a custom extension, link it to the role settings for each role where you want PIM to call the extension during activation. To update role settings for a group, you need the Owner role for that group. @@ -302,10 +349,11 @@ To link a custom extension to a group role, follow these steps: 1. Select the role you want to configure, such as **Owner** or **Member**, and open the role settings. 1. Select **Edit**.-1. Select the checkbox for **Require pre-approval custom extension to activate**.+1. Select **Require pre-approval custom extension to activate** or **Require post-approval custom extension to activate**. 1. Select the custom extension you want to link to this role.+1. To call the extension and record the result without enforcing a denial or call failure, enable **Audit mode**. - :::image type="content" source="media/privileged-identity-management-custom-extensions/edit-role-setting-select-custom-extension.png" alt-text="Screenshot of setting the role settings when selecting the custom extension." lightbox="media/privileged-identity-management-custom-extensions/edit-role-setting-select-custom-extension.png":::+ :::image type="content" source="media/privileged-identity-management-custom-extensions/configure-role-extension.png" alt-text="Screenshot of group role settings with a preapproval custom extension selected." lightbox="media/privileged-identity-management-custom-extensions/configure-role-extension.png"::: 1. Select **Save** to apply the updated role settings. After you save, the role setting details page confirms the custom extension is linked.@@ -314,26 +362,240 @@ After you save, the role setting details page confirms the custom extension is l You can follow a similar process to link custom extensions to Azure resource roles and Microsoft Entra directory roles. -## Verify the role activation flow+## Step 5 - Verify the role activation flow After you complete the configuration, verify that the custom extension works as expected by activating a role and confirming that PIM sends the request to your API and enforces the decision. 1. Sign in to the [Microsoft Entra admin center](https://entra.microsoft.com) as a user who is eligible for the role. 1. Go to **Privileged Identity Management** and activate the role that has the custom extension configured.-1. Provide the required justification and ticket information.+1. Provide the required justification, ticket information, and custom attributes. - :::image type="content" source="media/privileged-identity-management-custom-extensions/activate-role-with-ticket-info.png" alt-text="Screenshot of activating a role with ticket information." lightbox="media/privileged-identity-management-custom-extensions/activate-role-with-ticket-info.png":::+ :::image type="content" source="media/privileged-identity-management-custom-extensions/activate-role-with-ticket-info.png" alt-text="Screenshot of activating a role with ticket information and custom attributes." lightbox="media/privileged-identity-management-custom-extensions/activate-role-with-ticket-info.png"::: 1. Submit the activation request. 1. Verify that your API endpoint receives the POST request from PIM. 1. Confirm that PIM enforces the decision returned by your custom extension:- - **Approved**: The activation proceeds through the normal PIM workflow, including any other approval steps.- - **AutoApproved**: The activation proceeds without requiring manual PIM approval.- - **Denied**: The activation is blocked, and the reason messages from your API response appear in the PIM UI.+ - **Approved**: The activation proceeds through the normal PIM workflow, including any other approval steps.+ - **AutoApproved**: The activation proceeds without requiring manual PIM approval.+ - **Denied**: The activation is blocked, and the reason messages from your API response appear in the PIM UI. The following example shows an activation denied by a custom extension that validated ticket information: - :::image type="content" source="media/privileged-identity-management-custom-extensions/activation-denied-custom-extension.png" alt-text="Screenshot of the activation denied with custom extension.":::+ :::image type="content" source="media/privileged-identity-management-custom-extensions/extension-denied-activation.png" alt-text="Screenshot of a PIM activation denied because the custom extension rejected the ticket number." lightbox="media/privileged-identity-management-custom-extensions/extension-denied-activation.png":::++## Preapproval and postapproval extensions++Preapproval and postapproval extensions are blocking gates, but they run at different points in the activation workflow.++| Capability | `preApproval` | `postApproval` |+| --- | --- | --- |+| Execution point | Before manual approval | After manual approval and immediately before assignment provisioning |+| Supported outcomes | `Approved`, `Denied`, `AutoApproved` | `approved`, `denied`, `deferred` |+| Skip manual approval | Yes, with `AutoApproved` | No, because manual approval is already complete |+| Deferred evaluation | No | Yes, through `/updateRequest` |+| Blocks | Manual approval and provisioning | Assignment provisioning |+| Common scenarios | Compliance prescreening, risk assessment, early denial, and conditional automatic approval | IT service management validation, security scanning, external approval, and asynchronous processing |++Use a preapproval extension to evaluate a request before a PIM approver sees it. Use a postapproval extension when custom logic must run after the built-in PIM approval stage but before PIM provisions the assignment.++> [!NOTE]+> A postapproval extension doesn't run after role activation. It runs after manual approval, but before the role assignment is provisioned.++### When to use each extension type++Use a preapproval extension to:++- Perform compliance or risk checks before a human approver sees the request.+- Auto-approve low-risk requests that meet defined criteria.+- Deny requests early based on external data.+- Reduce approver workload by filtering clearly valid or invalid requests.++Use a postapproval extension to:++- Run another custom approval workflow after PIM approval.+- Validate an IT service management ticket before provisioning access.+- Perform a security scan or another long-running process.+- Defer the decision and call PIM later with the final outcome.++### Complete a deferred postapproval evaluation++A postapproval extension can return `deferred` when it needs more time to make a decision. PIM places the request in `PostApprovalExtensionPendingEvaluation` and doesn't provision the role assignment. After the asynchronous process finishes, the extension calls `/updateRequest` on the original activation request with a final `approved` or `denied` decision.++The extension must authenticate to the callback API by using the same Microsoft Entra app registration configured for the extension. PIM verifies that the calling application ID matches the registered application. The callback can't return `deferred` again.++The deferred flow works as follows:++1. PIM calls the postapproval extension after manual approval.+1. The extension returns `deferred` because it needs more time.+1. PIM moves the request to `PostApprovalExtensionPendingEvaluation`.+1. The extension completes its asynchronous validation or approval process.+1. The extension calls the provider-specific `/updateRequest` endpoint with `approved` or `denied`.+1. PIM validates the callback and either provisions or denies the assignment.++#### Microsoft Entra roles++Use:++```http+POST https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignmentScheduleRequests/{requestId}/updateRequest+Content-Type: application/json+```++```json+{+ "evaluationOutcome": "approved",+ "evaluationId": "aaaa0000-bb11-2222-33cc-444444dddddd",+ "reason": "The external compliance system approved the request."+}+```++#### PIM for Groups++Use:++```http+POST https://graph.microsoft.com/beta/identityGovernance/privilegedAccess/group/assignmentScheduleRequests/{requestId}/updateRequest+Content-Type: application/json+```++```json+{+ "evaluationOutcome": "denied",+ "evaluationId": "aaaa0000-bb11-2222-33cc-444444dddddd",+ "reason": "The external compliance system denied the request."+}+```++#### Azure resources++Use:++```http+POST https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignmentScheduleRequests/{requestId}/updateRequest?api-version=2020-10-01+Content-Type: application/json+```++```json+{+ "evaluationOutcome": "denied",+ "evaluationId": "aaaa0000-bb11-2222-33cc-444444dddddd",+ "reason": [+ "The change advisory board rejected ticket CHANGE-9921."+ ]+}+```++#### Callback endpoint summary++| Provider | Endpoint |+|---|---|+| Microsoft Entra roles | `POST https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignmentScheduleRequests/{requestId}/updateRequest` |+| PIM for Groups | `POST https://graph.microsoft.com/beta/identityGovernance/privilegedAccess/group/assignmentScheduleRequests/{requestId}/updateRequest` |+| Azure resources | `POST https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleAssignmentScheduleRequests/{requestId}/updateRequest?api-version=2020-10-01` |++The callback request body supports the following properties:++| Property | Required | Description |+|---|---|---|+| `evaluationOutcome` | Yes | Final outcome. Use `approved` or `denied`; a callback can't return `deferred`. |+| `evaluationId` | Yes | Correlation identifier from the original extension response. |+| `reason` | No | Human-readable reason for the decision. Azure Resource Manager accepts a string collection; Microsoft Graph accepts a string. |+| `scheduleInfo` | No | Microsoft Graph only. Optional updated activation schedule for an approved request. |++Handle callback failures based on the returned HTTP status:++| HTTP status | Cause |+|---|---|+| `400 Bad Request` | The request isn't waiting for a callback, or the callback outcome isn't `approved` or `denied`. |+| `401 Unauthorized` | The calling application doesn't match the app registered for the custom extension. |+| `404 Not Found` | The request ID doesn't exist. |++## Custom attributes++Custom attributes collect extra key-value context during activation and include it in the extension request. Use custom attributes when your extension needs information that isn't part of the standard request, such as a cost center, project code, business justification category, or identifier in an external system.++Add the attribute names when you create the extension. During activation, the user enters the corresponding values. PIM sends the values in a provider-specific format.++### Azure resources++For Azure resource roles, PIM sends a `customAttributes` array:++```json+{+ "customAttributes": [+ {+ "attributeName": "costCenter",+ "attributeValue": "CC-4521"+ },+ {+ "attributeName": "projectCode",+ "attributeValue": "PROJ-ALPHA"+ }+ ]+}+```++### Microsoft Entra roles++For Microsoft Entra roles, PIM sends a JSON-encoded `customData` string:++```json+{+ "customData": "{\"costCenter\":\"CC-4521\",\"projectCode\":\"PROJ-ALPHA\"}"+}+```++### PIM for Groups++For PIM for Groups, PIM also sends a JSON-encoded `customData` string:++```json+{+ "customData": "{\"costCenter\":\"CC-4521\",\"approverGroup\":\"finance-approvers\"}"+}+```++| Provider | Field | Format |+|---|---|---|+| Azure resources | `customAttributes` | Array of `attributeName` and `attributeValue` objects. |+| Microsoft Entra roles | `customData` | JSON-encoded string. |+| PIM for Groups | `customData` | JSON-encoded string. |++Validate custom-attribute names and values in your API before using them in a query, workflow, or access decision.++## Quick reference++### Extension type decision matrix++| Scenario | `preApproval` | `postApproval` |+|---|---|---|+| Compliance check before approvers | Best fit | Not applicable |+| Auto-approve low-risk requests | Best fit | Not supported |+| Block based on external data | Supported | Supported |+| Custom approval after PIM approval | Not applicable | Best fit |+| Asynchronous processing with callback | Not supported | Best fit |+| Security scan before provisioning | Not applicable | Best fit |++### Evaluation outcome quick reference++| Outcome | `preApproval` effect | `postApproval` effect | Callback effect |+|---|---|---|---|+| `Approved` / `approved` | Continues to manual approval. | Continues to provisioning. | Resolves a deferred request and provisions the role. |+| `Denied` / `denied` | Denies the request. | Denies the request. | Resolves a deferred request and denies it. |+| `deferred` | Not supported. | Pauses and waits for `/updateRequest`. | Not supported; a callback can't defer again. |+| `AutoApproved` | Skips manual approval. | Not supported. | Not supported. |++### Postapproval request status transitions++The following table shows how the request status transitions based on the postapproval evaluation outcome and the callback decision:++| Stage | Outcome | New status | Next step |+|---|---|---|---|+| Postapproval extension | `approved` | Granted | PIM provisions the assignment. |+| Postapproval extension | `denied` | Denied | The request ends. |+| Postapproval extension | `deferred` | `PostApprovalExtensionPendingEvaluation` | PIM waits for `/updateRequest`. |+| Callback | `approved` | `PostApprovalExtensionAdminApproved` | PIM provisions the assignment. |+| Callback | `denied` | `PostApprovalExtensionAdminDenied` | The request ends. | ## Related content 