FIX: New-MgIdentityGovernancePrivilegedAccess GroupEligibilityScheduleRequest Expirationrule

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: RoleAssignmentRequestPolicyValidationFailed

Eventually 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
FIX: The following policy rules failed: [“ExpirationRule”] Status: 400 ErrorCode: RoleAssignmentRequestPolicyValidationFailed

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 MessageExplanantionMitigation
code: RoleAssignmentRequestPolicyValidationFailed
message: The following policy rules failed: [“ExpirationRule”]
The ScheduleInfo specified in the request exceeds the maximum allowed durationYou 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.

ScopeType
providers/Microsoft.Management/managementGroups/{mg-name}Management Group
subscriptions/{subscriptionId}Subscription
subscriptions/{subscriptionId}/resourceGroups/myresourcegroup1Resource group
subscriptions/{subscriptionId}/resourceGroups/myresourcegroup1/providers/Microsoft.Web/sites/mysite1Resource

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 : owner

But 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 

17

Not 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      : PT8H

And 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      : PT8H

So, 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

Published by

Bas Wijdenes

My name is Bas Wijdenes and I work as a PowerShell DevOps Engineer. In my spare time I write about interesting stuff that I encounter during my work.

Leave a Reply

Your email address will not be published. Required fields are marked *