Table of Contents
The following policy rules failed: [“ExpirationRule”] Status: 400 ErrorCode: RoleAssignmentRequestPolicyValidationFailed
At work we have automation in place for deploying and updating Privileged Identity Management (PIM) configurations for a customer.
I didn’t build the automation myself, someone else did that before I joined InSpark, but I’ve been maintaining parts of it ever since.
The setup is simple: when a workload team creates a pull request in Azure DevOps, and the PR includes changes to specific JSON files (like for PIM), the automation kicks off and deploys the changes automatically.
That’s the idea at least, but recently, one of those deployments suddenly started failing on this error:
New-MgIdentityGovernancePrivilegedAccessGroupEligibilityScheduleRequest_CreateExpanded: The following policy rules failed: ["ExpirationRule"] Status: 400| (BadRequest) ErrorCode: RoleAssignmentRequestPolicyValidationFailedEventually I found a workaround for us, but it took me a while before I understood what was happening, even with documentation from Microsoft.
Unfortunately, at this customer we do not have complete ownership over Microsoft Entra Id or Privileged Identity Management, and we had to work with what we can.
I’ll share below what I’ve done, but there are different ways of course.
Disclaimer: Below is for scope group in Privileged Identity Management and I have to use a workaround.
Let’s go through this step by step for scope group
First thing I did was checking the Azure portal if the setting is not allowed to be set to NoExpiration, and yeah, it is:
![FIX: The following policy rules failed: ["ExpirationRule"] Status: 400 ErrorCode: RoleAssignmentRequestPolicyValidationFailed](https://baswijdenes.com/wp-content/uploads/2025/08/image-1024x353.png)
Second thing I did was copying the error to an AI and see what they had to say, but unfortunately, if there is no good documentation from Microsoft, the AI’s do not know this well either.
So, I copied this
‘New-MgIdentityGovernancePrivilegedAccessGroupEligibilityScheduleRequest_CreateExpanded: The following policy rules failed: [“ExpirationRule”] Status: 400| (BadRequest) ErrorCode: RoleAssignmentRequestPolicyValidationFailed‘
into a search engine, and found this documentation from Microsoft:
Common errors returned by Azure Privileged Identity Management API | Microsoft Learn
And my error matched this table row:
| Error Message | Explanantion | Mitigation |
|---|---|---|
code: RoleAssignmentRequestPolicyValidationFailedmessage: The following policy rules failed: [“ExpirationRule”] | The ScheduleInfo specified in the request exceeds the maximum allowed duration | You can GET the RoleManagementPolicy for this RoleDefinitionId and check the RoleManagementPolicyExpirationRule |
Just when you think you’ve figured it out… turns out it’s not that simple after all. It’s not much of help unless you can configure the default policies.
If you’ve come across a better solution, I’d love to hear it in the comments!
First thing I noticed is that the Scope is Group isn’t an option.
| Scope | Type |
|---|---|
providers/Microsoft.Management/managementGroups/{mg-name} | Management Group |
subscriptions/{subscriptionId} | Subscription |
subscriptions/{subscriptionId}/resourceGroups/myresourcegroup1 | Resource group |
subscriptions/{subscriptionId}/resourceGroups/myresourcegroup1/providers/Microsoft.Web/sites/mysite1 | Resource |
So, I went back to investigating the issue.
This is the URL from the Azure REST API: https://management.azure.com/{scope}/providers/Microsoft.Authorization/roleManagementPolicies?api-version=2020-10-01&$filter={filter}
I was curious if I could find something on the search engine for roleManagementPolicies in Microsoft Graph as well, and low and behold:
List roleManagementPolicyAssignments – Microsoft Graph v1.0 | Microsoft Learn
And I found this:
- To retrieve policies for groups in PIM for Groups, the scopeId must be the group ID and scopeType must be
Group.
With PowerShell I came up with this:
$MgGraphToken = (Get-AzAccessToken -ResourceTypeName MSGraph).Token
$InvokeRestMethodSplat = @{
Uri = "https://graph.microsoft.com/v1.0/policies/roleManagementPolicyAssignments?`$filter=scopeId eq '$($group.Id)' and scopeType eq 'Group'"
Headers = @{
"Content-Type" = "application/json"
}
Authentication = "Bearer"
Token = $MgGraphToken
Method = 'GET'
}
(Invoke-RestMethod @InvokeRestMethodSplat).Value
id : Group_16477075..._member
policyId : Group_16477075...
scopeId : 16477075-0c70-...
scopeType : Group
roleDefinitionId : member
id : Group_16477075..._owner
policyId : Group_16477075...
scopeId : 16477075-0c70-...
scopeType : Group
roleDefinitionId : ownerBut no RoleManagementPolicyExpirationRule property.
I do notice in the response that I retrieve 2 roleDefinitions, but I only need the member policy, so I filter the response to this:
$Policies = $response.value | Where-Object { $_.roleDefinitionId -eq $Role }Checking the I notice that there is an $expand query parameter for rules, so let’s check that out:
$PolicyRules = foreach ($Policy in $Policies) {
$InvokeRestMethodSplat.Uri = "https://graph.microsoft.com/v1.0/policies/roleManagementPolicies/$($Policy.PolicyId)?`$expand=rules"
Invoke-RestMethod @InvokeRestMethodSplat
}
$Rules.Count
17Not pasting all rules as it’s 17, would be too much verbose.
I noticed that there were rules with the following @odata.type:@odata.type : #microsoft.graph.unifiedRoleManagementPolicyExpirationRule
By filtering on these, I found this:
$PolicyExpirationRules = $PolicyRules.Rules | Where-Object { $_.'@odata.type' -eq "#Microsoft.Graph.unifiedRoleManagementPolicyExpirationRule" }
@odata.type : #microsoft.graph.unifiedRoleManagementPolicyExpirationRule
id : Expiration_Admin_Eligibility
isExpirationRequired : True
maximumDuration : P365D
@odata.type : #microsoft.graph.unifiedRoleManagementPolicyExpirationRule
id : Expiration_Admin_Assignment
isExpirationRequired : True
maximumDuration : P180D
@odata.type : #microsoft.graph.unifiedRoleManagementPolicyExpirationRule
id : Expiration_EndUser_Assignment
isExpirationRequired : True
maximumDuration : PT8HAnd because this is about end user assignment, I knew I had to pick the one with id: Expiration_EndUser_Assignment.
$PolicyExpirationRule = $PolicyExpirationRules | Where-Object { $_.id -eq 'Expiration_EndUser_Assignment' }
@odata.type : #microsoft.graph.unifiedRoleManagementPolicyExpirationRule
id : Expiration_EndUser_Assignment
isExpirationRequired : True
maximumDuration : PT8HSo, isExpirationRequired is on True and the automation cannot overwrite this unfortunately.
This is where the workaround comes in since I cannot update this, I’ll have to be creative.
And this is what my creativity could come up with:
if ($PolicyExpirationRule.isExpirationRequired) {
Write-Warning "No expiration cannot be set for group, as policy $($PolicyExpirationRule.id) requires expiration"
}…and that wraps up this post.
I’d love to show you more solutions, but unfortunately, I don’t have full permissions in the portal to test everything.
Hopefully, this still helps you get a little further than what Microsoft’s documentation offers.
Complete error
New-MgIdentityGovernancePrivilegedAccessGroupEligibilityScheduleRequest_CreateExpanded: /mnt/vss/_work/1/s/Automation/scripts/azure/PIM/groups/New-PimForGroups.ps1:135
Line |
135 | New-MgIdentityGovernancePrivilegedAccessGroupEligibilityS …
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| The following policy rules failed: ["ExpirationRule"] Status: 400
| (BadRequest) ErrorCode: RoleAssignmentRequestPolicyValidationFailed
| Date: 2025-07-30T13:36:50 Headers: Vary :
| Accept-Encoding Strict-Transport-Security : max-age=31536000
| request-id : ac795b6b-7e5a-4471-9011-2176c96ed69d
| client-request-id : 158652b7-808f-4e6a-9cad-a5d468e3081b
| x-ms-ags-diagnostic : {"ServerInfo":{"DataCenter":"West
| Europe","Slice":"E","Ring":"5","ScaleUnit":"000","RoleInstance":"AM1PEPF0004B2A5"}} Date : Wed, 30 Jul 2025 13:36:50 GM
