Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azuread_directory_role_eligibility_schedule_request resources disappear and can't be recreated #1306

Open
garretth9 opened this issue Feb 8, 2024 · 12 comments

Comments

@garretth9
Copy link

Community Note

  • Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritise this request
  • Please do not leave "+1" or "me too" comments, they generate extra noise for issue followers and do not help prioritise the request
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment

Terraform (and AzureAD Provider) Version

Terraform v1.5.7
on darwin_arm64

  • provider registry.terraform.io/hashicorp/azuread v2.45.0

Affected Resource(s)

  • azuread_directory_role_eligibility_schedule_request

Terraform Configuration Files

data "azuread_user" "example" {
  user_principal_name = "[email protected]"
}

resource "azuread_directory_role" "example" {
  display_name = "Application Administrator"
}

resource "azuread_directory_role_eligibility_schedule_request" "example" {
  role_definition_id = azuread_directory_role.example.template_id
  principal_id       = azuread_user.example.object_id
  directory_scope_id = "/"
  justification      = "Example"
}

Debug Output

Panic Output

Expected Behavior

The role eligibility assignment should be created, and in the absence of any other changes being made should remain indefinitely.

Actual Behavior

The role eligibility assignment is created but within a month or two (not positive on the exact duration), the Schedule Request seems to automatically expire and disappear. This leads terraform to attempt to recreate it on the next run, but as the actual role assignment still exists it results in an error similar to the below

│ Error: Eligibility schedule request for role "45xxx3c5-c802-45c6-b32a-1d70xxx1e86e" to principal "8e2xxe56-bacf-4c8d-87e6-5de5xxx8a453", received 400 with error: RoleEligibilityScheduleRequestClient.BaseClient.Post(): unexpected status 400 with OData error: RoleAssignmentExists: The Role assignment already exists.
│ 
│   with azuread_directory_role_eligibility_schedule_request.example,
│   on groups.tf line 70, in resource "azuread_directory_role_eligibility_schedule_request" "example":
│   70: resource "azuread_directory_role_eligibility_schedule_request" "example" {
│ 
│ RoleEligibilityScheduleRequestClient.BaseClient.Post(): unexpected status
│ 400 with OData error: RoleAssignmentExists: The Role assignment already
│ exists.

Steps to Reproduce

  1. terraform apply
  2. wait a month or two
  3. terraform apply

I'm not sure this is an issue with the resource itself per se, but given that the behavior of Entra seems to be

  1. User creates an eligibility schedule request
  2. An eligible role assignment is created FROM this schedule request
  3. The request eventually rolls off leaving the assignment behind

I do wonder whether it's even useful to have this as a resource in the provider. Perhaps there's a way to skip step 1 and create the schedule request directly? or alternately once the request is created in terraform have it tie the resource ID to the actual eligible role assignment somehow instead of tying it to the schedule request, which seems to not be a permanent resource?

Important Factoids

References

  • #0000
@nbaju1
Copy link

nbaju1 commented Feb 9, 2024

Did a test of this and the eligibility schedule request was created with no end time, i.e. "End Time -> Permanent" in "PIM -> Eligible assignments" in the Azure portal.

Given that I don't see how this can be an issue with the TF provider. Most likely the request was removed manually.

@garretth9
Copy link
Author

garretth9 commented Feb 9, 2024 via email

@garretth9
Copy link
Author

To add some more detail, i believe the issue here has to do with the distinction between a roleEligibilitySchedule and a roleEligibilityScheduleRequest. It looks like when you create an eligible role assignment using an eligibilityScheduleRequest the Schedule itself is created with the permanent lifespan that was requested, but the eligibilityScheduleRequest itself appears to have a separate lifecycle.

When i search with powershell using the Get-MgRoleManagementDirectoryRoleEligibilitySchedule cmdlet i still see my eligible role assignments with a status of "provisioned", however when i search with the Get-MgRoleManagementDirectoryRoleEligibilityScheduleRequest the requests all show a status of "Revoked" The same problem happened after roughly the same duration in both our production and test tenants. I'm the only one managing role managements in our test tenant so i know for a fact no manual action was done to remove them.

The eligible assignments were deployed using terraform near the end of December, and i was deploying new eligible role assignments with this template successfully until a couple weeks ago. Then when i tried to update it again yesterday the requests had all rolled off and terraform wanted to recreate them all. So i suspect the requests have a one month lifecycle, but i'm not positive.

Unfortunately that means if you want to try and replicate it i think you'll have to deploy the resources and wait a month or so then try generating a new plan, you should see that the role assignments are still there but terraform thinks the schedule requests need to be recreated, which will fail.

@nbaju1
Copy link

nbaju1 commented Feb 16, 2024

I agree, seems like Entra has separate resources for the request and the actual schedule. Checked in our test tenant and most requests were Revoked, while the schedules were Provisioned. I'm not savvy enough in Go or Terraform to understand how the check to see if a request needs to be recreated is made, but if the status field on the roleEligibilityScheduleRequests object is the one being checked, then this will cause unnecessary attempts to recreate the resource when it automatically changes to Revoked. Note that I haven't tested myself to see if it automatically changes, but from what @garretth9 reports and the status in our tentant this seems likely.

The only way, using the MS Graph API at least, to create a schedule is to create the request. There is no POST method for the schedule resource.

A solution might be to change the read resource method in the provider to look at the schedule rather than the request to determine if the request must be recreated.

Refs:
Request: https://learn.microsoft.com/en-us/graph/api/resources/unifiedroleeligibilityschedulerequest?view=graph-rest-1.0
Schedule: https://learn.microsoft.com/en-us/graph/api/resources/unifiedroleeligibilityschedule?view=graph-rest-1.0

@garretth9
Copy link
Author

Looking in my environment today i see that all of the "revoked" schedule requests have now completely vanished. It really seems like the only path forward to make this resource even viable for use is to somehow link it to the created schedule instead of the request.

In fact, i'd go a step further and say it might be more appropriate to

  1. remove the azuread_directory_role_eligibility_schedule_request resource entirely
  2. replace it with an azuread_directory_role_eligibility_schedule resource
  3. have the provider logic manage the steps of creating a schedule request and linking the terraform resource to the id of the created schedule instead of the schedule request so we can actually see if the schedule needs to be recreated

In any case it doesn't seem particularly useful to have the terraform state pointing to a resource that is going to self-destruct automatically

@Darkfogel
Copy link

Just to give this some additionnal light, I also have the same issue as mentionned here. The request actually expires but the assignment is still active.

@kenchan0130
Copy link
Contributor

  1. remove the azuread_directory_role_eligibility_schedule_request resource entirely
  2. replace it with an azuread_directory_role_eligibility_schedule resource
  3. have the provider logic manage the steps of creating a schedule request and linking the terraform resource to the id of the created schedule instead of the schedule request so we can actually see if the schedule needs to be recreated

I agree with these proposed changes.
First, we need to add client implementation related to eligibility schedules in the Go Graph API SDK (Hamilton).

@Darkfogel
Copy link

@manicminer Would that issue be the same one that was happening in the azurerm provider that you fixed in PR #25956?

@Darkfogel
Copy link

@kenchan0130 Any news on this? Just checking out as I would really like to be able to keep all my resources in the state. I actually have to remove them after 45 days as a workaround.

@paul-hugill
Copy link

To add another bit of information, if you manually delete the assignment in the Azure Portal, Terraform does not see any changes on the next run and does not try to recreate it, so state and actual no longer match.

@leuthelt
Copy link

I've same behavior like @Darkfogel mentioned. After 45 days our drift detection shows creation of all Terraform created permanent "Eligible assignments". Because Terraform cannot match the state with real world anymore, my workaround was also manual removal of these assignments and execution of our Terraform workflow again.

Azure Portal:
image
All assignments were created on 8th of July 2024. And today (23rd of August) Terraform cannot match state to real world. So date diff is 46 days, means the issue starts after 45 days.

Drift detection shows:
image

Terraform plan:

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # module.blueprint_iam_entra_roles.module.pim_entra_roles["Application Administrator"].azuread_directory_role_eligibility_schedule_request.this will be created
  + resource "azuread_directory_role_eligibility_schedule_request" "this" {
      + directory_scope_id = "/"
      + id                 = (known after apply)
      + justification      = "Terraform created assignment"
      + principal_id       = "cd2d9f08-7f84-4479-be4b-8124e7f6fa28"
      + role_definition_id = "9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3"
    }

  # module.blueprint_iam_entra_roles.module.pim_entra_roles["Conditional Access Administrator"].azuread_directory_role_eligibility_schedule_request.this will be created
  + resource "azuread_directory_role_eligibility_schedule_request" "this" {
      + directory_scope_id = "/"
      + id                 = (known after apply)
      + justification      = "Terraform created assignment"
      + principal_id       = "e97be21a-4e3b-48b3-b8e2-459ab7704817"
      + role_definition_id = "b1be1c3e-b65d-4f19-8427-f6fa0d97feb9"
    }

  # module.blueprint_iam_entra_roles.module.pim_entra_roles["Global Administrator"].azuread_directory_role_eligibility_schedule_request.this will be created
  + resource "azuread_directory_role_eligibility_schedule_request" "this" {
      + directory_scope_id = "/"
      + id                 = (known after apply)
      + justification      = "Terraform created assignment"
      + principal_id       = "bbaff55c-102b-45ea-bbb3-859eea0225bc"
      + role_definition_id = "62e90394-69f5-4237-9190-012177145e10"
    }

  # module.blueprint_iam_entra_roles.module.pim_entra_roles["Groups Administrator"].azuread_directory_role_eligibility_schedule_request.this will be created
  + resource "azuread_directory_role_eligibility_schedule_request" "this" {
      + directory_scope_id = "/"
      + id                 = (known after apply)
      + justification      = "Terraform created assignment"
      + principal_id       = "e6a73948-a522-49a8-bc26-af134a1bf4a3"
      + role_definition_id = "fdd7a751-b60b-444a-984c-02652fe8fa1c"
    }

  # module.blueprint_iam_entra_roles.module.pim_entra_roles["Guest Inviter"].azuread_directory_role_eligibility_schedule_request.this will be created
  + resource "azuread_directory_role_eligibility_schedule_request" "this" {
      + directory_scope_id = "/"
      + id                 = (known after apply)
      + justification      = "Terraform created assignment"
      + principal_id       = "fb6db157-993d-49ce-9ae7-46f61bf29481"
      + role_definition_id = "95e79109-95c0-4d8e-aee3-d01accf2d47b"
    }

  # module.blueprint_iam_entra_roles.module.pim_entra_roles["Identity Governance Administrator"].azuread_directory_role_eligibility_schedule_request.this will be created
  + resource "azuread_directory_role_eligibility_schedule_request" "this" {
      + directory_scope_id = "/"
      + id                 = (known after apply)
      + justification      = "Terraform created assignment"
      + principal_id       = "433787dc-758f-4265-a81c-60141325e18a"
      + role_definition_id = "45d8d3c5-c802-45c6-b32a-1d70b5e1e86e"
    }

  # module.blueprint_iam_entra_roles.module.pim_entra_roles["Intune Administrator"].azuread_directory_role_eligibility_schedule_request.this will be created
  + resource "azuread_directory_role_eligibility_schedule_request" "this" {
      + directory_scope_id = "/"
      + id                 = (known after apply)
      + justification      = "Terraform created assignment"
      + principal_id       = "31d2d388-02e7-43f7-b1b4-1bfbb6b83700"
      + role_definition_id = "3a2c62db-5318-420d-8d74-23affee5d9d5"
    }

  # module.blueprint_iam_entra_roles.module.pim_entra_roles["License Administrator"].azuread_directory_role_eligibility_schedule_request.this will be created
  + resource "azuread_directory_role_eligibility_schedule_request" "this" {
      + directory_scope_id = "/"
      + id                 = (known after apply)
      + justification      = "Terraform created assignment"
      + principal_id       = "0c30a4ff-6e07-447c-b65e-09c2d4adf422"
      + role_definition_id = "4d6ac14f-3453-41d0-bef9-a3e0c569773a"
    }

  # module.blueprint_iam_entra_roles.module.pim_entra_roles["Network Administrator"].azuread_directory_role_eligibility_schedule_request.this will be created
  + resource "azuread_directory_role_eligibility_schedule_request" "this" {
      + directory_scope_id = "/"
      + id                 = (known after apply)
      + justification      = "Terraform created assignment"
      + principal_id       = "7ec21459-8a60-4e62-bec1-80a1a269f3d4"
      + role_definition_id = "d37c8bed-0711-4417-ba38-b4abe66ce4c2"
    }

  # module.blueprint_iam_entra_roles.module.pim_entra_roles["Privileged Role Administrator"].azuread_directory_role_eligibility_schedule_request.this will be created
  + resource "azuread_directory_role_eligibility_schedule_request" "this" {
      + directory_scope_id = "/"
      + id                 = (known after apply)
      + justification      = "Terraform created assignment"
      + principal_id       = "b5e4[520](https://github.com/bmwblueteam/t0-launchpad/actions/runs/10519683985/job/29147449755#step:18:521)e-ca63-48b9-b066-a11615d68cad"
      + role_definition_id = "e8611ab8-c189-46e8-94e1-60213ab1f814"
    }

  # module.blueprint_iam_entra_roles.module.pim_entra_roles["Security Administrator"].azuread_directory_role_eligibility_schedule_request.this will be created
  + resource "azuread_directory_role_eligibility_schedule_request" "this" {
      + directory_scope_id = "/"
      + id                 = (known after apply)
      + justification      = "Terraform created assignment"
      + principal_id       = "7097dbe3-179f-481e-9fc9-5c1abc7faba0"
      + role_definition_id = "194ae4cb-b126-40b2-bd5b-6091b380977d"
    }

  # module.blueprint_iam_entra_roles.module.pim_entra_roles["User Administrator"].azuread_directory_role_eligibility_schedule_request.this will be created
  + resource "azuread_directory_role_eligibility_schedule_request" "this" {
      + directory_scope_id = "/"
      + id                 = (known after apply)
      + justification      = "Terraform created assignment"
      + principal_id       = "119e2[534](https://github.com/bmwblueteam/t0-launchpad/actions/runs/10519683985/job/29147449755#step:18:535)-f88b-4a37-b476-84a128bb1733"
      + role_definition_id = "fe930be7-5e62-47db-91af-98c3a49a38b1"
    }

Plan: 12 to add, 0 to change, 0 to destroy.

Now, when I run Terraform workflow it fails because it's saying that the same resources already exists:

module.blueprint_iam_entra_roles.module.pim_entra_roles["Guest Inviter"].azuread_directory_role_eligibility_schedule_request.this: Creating...
╷
│ Error: Eligibility schedule request for role "fdd7a751-b60b-444a-984c-02652fe8fa1c" to principal "e6a73948-a522-49a8-bc26-af134a1bf4a3", received 400 with error: RoleEligibilityScheduleRequestClient.BaseClient.Post(): unexpected status 400 with OData error: RoleAssignmentExists: The Role assignment already exists.
│ 
│   with module.blueprint_iam_entra_roles.module.pim_entra_roles["Groups Administrator"].azuread_directory_role_eligibility_schedule_request.this,
│   on ../../modules/pim_entra_roles/v1.2.0/main.tf line 34, in resource "azuread_directory_role_eligibility_schedule_request" "this":
│   34: resource "azuread_directory_role_eligibility_schedule_request" "this" {
│ 
│ RoleEligibilityScheduleRequestClient.BaseClient.Post(): unexpected status
│ 400 with OData error: RoleAssignmentExists: The Role assignment already
│ exists.
╵
╷
│ Error: Eligibility schedule request for role "45d8d3c5-c802-45c6-b32a-1d70b5e1e86e" to principal "43[37](https://github.com/bmwblueteam/t0-launchpad/actions/runs/10521504706/job/29152440577#step:14:38)87dc-758f-4265-a81c-60141325e18a", received 400 with error: RoleEligibilityScheduleRequestClient.BaseClient.Post(): unexpected status 400 with OData error: RoleAssignmentExists: The Role assignment already exists.
│ 
│ exists.
╵
╷
│ Error: Eligibility schedule request for role "4d6ac14f-3453-41d0-bef9-a3e0c569773a" to principal "0c30a4ff-6e07-447c-b65e-09c2d4adf422", received 400 with error: RoleEligibilityScheduleRequestClient.BaseClient.Post(): unexpected status 400 with OData error: RoleAssignmentExists: The Role assignment already exists.
│ 
│   with module.blueprint_iam_entra_roles.module.pim_entra_roles["License Administrator"].azuread_directory_role_eligibility_schedule_request.this,
│   on ../../modules/pim_entra_roles/v1.2.0/main.tf line 34, in resource "azuread_directory_role_eligibility_schedule_request" "this":
│   34: resource "azuread_directory_role_eligibility_schedule_request" "this" {
│ 
│ RoleEligibilityScheduleRequestClient.BaseClient.Post(): unexpected status
│ 400 with OData error: RoleAssignmentExists: The Role assignment already
│ exists.
╵
╷
│ Error: Eligibility schedule request for role "e8611ab8-c189-46e8-94e1-60213ab1f814" to principal "b5e4520e-ca63-48b9-b066-a11615d68cad", received 400 with error: RoleEligibilityScheduleRequestClient.BaseClient.Post(): unexpected status 400 with OData error: RoleAssignmentExists: The Role assignment already exists.
│ 
│   with module.blueprint_iam_entra_roles.module.pim_entra_roles["Privileged Role Administrator"].azuread_directory_role_eligibility_schedule_request.this,
│   on ../../modules/pim_entra_roles/v1.2.0/main.tf line 34, in resource "azuread_directory_role_eligibility_schedule_request" "this":
│   34: resource "azuread_directory_role_eligibility_schedule_request" "this" {
│ 
│ RoleEligibilityScheduleRequestClient.BaseClient.Post(): unexpected status
│ 400 with OData error: RoleAssignmentExists: The Role assignment already
│ exists.
╵
╷
│ Error: Eligibility schedule request for role "95e79109-95c0-4d8e-aee3-d01accf2d47b" to principal "fb6db157-993d-49ce-9ae7-46f61bf29481", received 400 with error: RoleEligibilityScheduleRequestClient.BaseClient.Post(): unexpected status 400 with OData error: RoleAssignmentExists: The Role assignment already exists.
│ 
│   with module.blueprint_iam_entra_roles.module.pim_entra_roles["Guest Inviter"].azuread_directory_role_eligibility_schedule_request.this,
│   on ../../modules/pim_entra_roles/v1.2.0/main.tf line 34, in resource "azuread_directory_role_eligibility_schedule_request" "this":
│   34: resource "azuread_directory_role_eligibility_schedule_request" "this" {
│ 
│ RoleEligibilityScheduleRequestClient.BaseClient.Post(): unexpected status
│ 400 with OData error: RoleAssignmentExists: The Role assignment already
│ exists.
╵
╷
│ Error: Eligibility schedule request for role "62e90394-69f5-4237-9190-012177145e10" to principal "bbaff55c-102b-45ea-bbb3-859eea0225bc", received 400 with error: RoleEligibilityScheduleRequestClient.BaseClient.Post(): unexpected status 400 with OData error: RoleAssignmentExists: The Role assignment already exists.
│ 
│   with module.blueprint_iam_entra_roles.module.pim_entra_roles["Global Administrator"].azuread_directory_role_eligibility_schedule_request.this,
│   on ../../modules/pim_entra_roles/v1.2.0/main.tf line 34, in resource "azuread_directory_role_eligibility_schedule_request" "this":
│   34: resource "azuread_directory_role_eligibility_schedule_request" "this" {
│ 
│ RoleEligibilityScheduleRequestClient.BaseClient.Post(): unexpected status
│ 400 with OData error: RoleAssignmentExists: The Role assignment already
│ exists.
╵
╷
│ Error: Eligibility schedule request for role "194ae4cb-b126-40b2-bd5b-6091b[38](https://github.com/bmwblueteam/t0-launchpad/actions/runs/10521504706/job/29152440577#step:14:39)0977d" to principal "7097dbe3-179f-481e-9fc9-5c1abc7faba0", received 400 with error: RoleEligibilityScheduleRequestClient.BaseClient.Post(): unexpected status [40](https://github.com/bmwblueteam/t0-launchpad/actions/runs/10521504706/job/29152440577#step:14:41)0 with OData error: RoleAssignmentExists: The Role assignment already exists.
│ 
│   with module.blueprint_iam_entra_roles.module.pim_entra_roles["Security Administrator"].azuread_directory_role_eligibility_schedule_request.this,
│   on ../../modules/pim_entra_roles/v1.2.0/main.tf line 34, in resource "azuread_directory_role_eligibility_schedule_request" "this":
│   34: resource "azuread_directory_role_eligibility_schedule_request" "this" {
│ 
│ RoleEligibilityScheduleRequestClient.BaseClient.Post(): unexpected status
│ 400 with OData error: RoleAssignmentExists: The Role assignment already
│ exists.
╵
╷
│ Error: Eligibility schedule request for role "9b895d92-2cd3-[44](https://github.com/bmwblueteam/t0-launchpad/actions/runs/10521504706/job/29152440577#step:14:45)c7-9d02-a6ac2d5ea5c3" to principal "cd2d9f08-7f84-4479-be4b-8124e7f6fa28", received 400 with error: RoleEligibilityScheduleRequestClient.BaseClient.Post(): unexpected status 400 with OData error: RoleAssignmentExists: The Role assignment already exists.
│ 
│   with module.blueprint_iam_entra_roles.module.pim_entra_roles["Application Administrator"].azuread_directory_role_eligibility_schedule_request.this,
│   on ../../modules/pim_entra_roles/v1.2.0/main.tf line 34, in resource "azuread_directory_role_eligibility_schedule_request" "this":
│   34: resource "azuread_directory_role_eligibility_schedule_request" "this" {
│ 
│ RoleEligibilityScheduleRequestClient.BaseClient.Post(): unexpected status
│ 400 with OData error: RoleAssignmentExists: The Role assignment already
│ exists.
╵
Releasing state lock. This may take a few moments...
Error: Process completed with exit code 1.

Are there any news regarding this topic?

@celsocoutinho-tangany
Copy link

We are being affected by this as well. We have around 100 Entra Eligible role assignments managed by TF, and they just started to show up as inexistent in the plan. Moreover, we expect new Entra Eligible role assignments to be required, more and more often.

This issue was identified in February, and is a major blocker for a significant amount of people, who have already commented on this Issue. But there's still no progress whatsoever. We cannot be constantly blocked and have to manually delete these resources, or import them into the state.

Please give us an update on this. If you don't plan to fix this, we will need to consider an alternative.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants