hashicorp / terraform-provider-azurerm

Terraform provider for Azure Resource Manager
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs
Mozilla Public License 2.0
4.54k stars 4.61k forks source link

azurerm_role_assignment must be replaced #4847

Open rjfmachado opened 4 years ago

rjfmachado commented 4 years ago

Community Note

Terraform (and AzureRM Provider) Version

Terraform v0.12.13

Affected Resource(s)

azurerm_role_assignment

Terraform Configuration Files

terraform {
  required_version = ">= 0.12"
}

provider "azurerm" {
  version = "~> 1.36.0"
}

provider "azuread" {
  version = "~> 0.6.0"
}

data "azurerm_client_config" "current" {
}

# Get the tenant root Management Group
data "azurerm_management_group" "mgTenantRoot" {
  group_id = data.azurerm_client_config.current.tenant_id
}

# Create a Management Group as a child to the tenant root
resource "azurerm_management_group" "test239857" {
  display_name               = "test239857"
  parent_management_group_id = data.azurerm_management_group.mgTenantRoot.id
  group_id                   = "test239857"
  subscription_ids = [
    data.azurerm_client_config.current.subscription_id //move the subscription under the role definition scope
  ]
}

# Create a custom role scoped to a management group
resource "azurerm_role_definition" "test239857" {
  name        = "test239857"
  scope       = azurerm_management_group.test239857.id
  description = "Role scoped to a management group"

  permissions {
    actions = [
      "Microsoft.DeploymentManager/*/read"
    ]
    not_actions = []
  }

  assignable_scopes = [
    azurerm_management_group.test239857.id
  ]
}

# create a target for the role assignment
resource "azuread_group" "test239857" {
  name = "test239857"
}

# Get the current subscription
data "azurerm_subscription" "currentSubscription" {
}

# Assignment of a Management Group scoped role to a subscription
resource "azurerm_role_assignment" "test239857" {
  scope              = data.azurerm_subscription.currentSubscription.id
  role_definition_id = azurerm_role_definition.test239857.id
  principal_id       = azuread_group.test239857.id
}

Debug Output

https://gist.github.com/rjfmachado/b8dd89e4e89fd88391e26e27ab0ff3f2

Expected Behavior

Terraform plan should have no changes after apply

Actual Behavior

Terraform wants to recreate the Role Assignment

Steps to Reproduce

  1. Terraform apply
  2. Terraform plan

Important Factoids

User should have global tenant admin role on Azure AD and Owner on the subscription

References

3450 Also hitting this when destroying

derek-burdick commented 4 years ago

Hi, I get the same behavior for built in roles, using azurerm_builtin_role_definition or azurerm_role_definition.

data azurerm_builtin_role_definition contributor { name = "Contributor" }

OR

data azurerm_role_definition contributor { name = "Contributor" }

Here is the output from plan/apply

module.service.azurerm_role_assignment.app-role must be replaced

-/+ resource "azurerm_role_assignment" "app-role" { ~ id = "/subscriptions/1234/resourceGroups/service-nexus-test1-westus2-dev/providers/Microsoft.Authorization/roleAssignments/6fea8cf5-5113-299f-aa9e-5e4be190fc2f" -> (known after apply) ~ name = "6fea8cf5-5113-299f-aa9e-5e4be190fc2f" -> (known after apply) principal_id = "7f16a9bb-a75d-4953-8c50-61b7ae694bb4" ~ principal_type = "ServicePrincipal" -> (known after apply) ~ role_definition_id = "/subscriptions/1234/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c" -> "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c" # forces replacement ~ role_definition_name = "Contributor" -> (known after apply) scope = "/subscriptions/1234/resourceGroups/service-test1-westus2-dev"

darren-johnson commented 4 years ago

Still having the same issue today 23/01/2020. It looks like it keeps wanting to switch between:

/subscriptions/GUID/providers/Microsoft.Authorization/roleDefinitions/GUID" -> "/providers/Microsoft.Authorization/roleDefinitions/GUID"

Every time it is also creating a new role_definition_id GUID.

darren-johnson commented 4 years ago

Hi I can confirm this issue still happens with terraform 0.12.21 and AzureRM 2.0

SaundersB commented 4 years ago

Same issue with Terraform v0.12.23

kensykora commented 4 years ago

I am having a similar issue that has to deal with multiple subscriptions in the same tenant. Here is my scenario:


resource "azurerm_role_definition" "resource-provider-registration" {
  name        = "Register Azure Resource Providers"
  scope       = "/subscriptions/${local.subscriptions.prod}"
  description = "Can register Azure resource providers"
  permissions {
    actions = ["*/register/action"]
  }
  assignable_scopes = [
    "/subscriptions/${local.subscriptions.prod}",
    "/subscriptions/${local.subscriptions.uat}",
    "/subscriptions/${local.subscriptions.dev}"
  ]

  provider = azurerm.prod
}

resource "azurerm_role_assignment" "ado-service-principal-provider-registration-prod" {
  scope              = "/subscriptions/${local.subscriptions.prod}"
  role_definition_id = azurerm_role_definition.resource-provider-registration.id
  principal_id       = local.azure_dev_ops_service_principals.prod.object_id

  provider = azurerm.prod
}

resource "azurerm_role_assignment" "ado-service-principal-provider-registration-uat" {
  scope              = "/subscriptions/${local.subscriptions.uat}"
  role_definition_id = azurerm_role_definition.resource-provider-registration.id
  principal_id       = local.azure_dev_ops_service_principals.uat.object_id

  provider = azurerm.uat
}

resource "azurerm_role_assignment" "ado-service-principal-provider-registration-dev" {
  scope              = "/subscriptions/${local.subscriptions.dev}"
  role_definition_id = azurerm_role_definition.resource-provider-registration.id
  principal_id       = local.azure_dev_ops_service_principals.dev.object_id

  provider = azurerm.dev
}

Subsequent runs of this produces the following terraform plans (wants to replace them due to the ID changing, when it hasn't changed.

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  ~ update in-place
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # azurerm_role_assignment.ado-service-principal-provider-registration-dev must be replaced
-/+ resource "azurerm_role_assignment" "ado-service-principal-provider-registration-dev" {
      ~ id                               = "/subscriptions/{Dev Subscription ID}/providers/Microsoft.Authorization/roleAssignments/2c2691ea-877f-cfe2-326e-6133d1ec73b9" -> (known after apply)
      ~ name                             = "2c2691ea-877f-cfe2-326e-6133d1ec73b9" -> (known after apply)
        principal_id                     = "2da99d69-b8a3-419f-a885-f6e1e5314524"
      ~ principal_type                   = "ServicePrincipal" -> (known after apply)
      ~ role_definition_id               = "/subscriptions/{Dev Subscription ID}/providers/Microsoft.Authorization/roleDefinitions/75262d2f-6665-aada-da4e-2bcafd5b2bc5" -> "/subscriptions/{Production Subscription ID}/providers/Microsoft.Authorization/roleDefinitions/75262d2f-6665-aada-da4e-2bcafd5b2bc5" # forces replacement
      ~ role_definition_name             = "Register Azure Resource Providers" -> (known after apply)
        scope                            = "/subscriptions/{Dev Subscription ID}"
      + skip_service_principal_aad_check = (known after apply)
    }

  # azurerm_role_assignment.ado-service-principal-provider-registration-uat must be replaced
-/+ resource "azurerm_role_assignment" "ado-service-principal-provider-registration-uat" {
      ~ id                               = "/subscriptions/{UAT Subscription ID}/providers/Microsoft.Authorization/roleAssignments/faf86950-c49d-061d-fd5e-142132c44441" -> (known after apply)
      ~ name                             = "faf86950-c49d-061d-fd5e-142132c44441" -> (known after apply)
        principal_id                     = "3defe365-2eb3-41c7-a025-55649d591ede"
      ~ principal_type                   = "ServicePrincipal" -> (known after apply)
      ~ role_definition_id               = "/subscriptions/{UAT Subscription ID}/providers/Microsoft.Authorization/roleDefinitions/75262d2f-6665-aada-da4e-2bcafd5b2bc5" -> "/subscriptions/{Production Subscription ID}/providers/Microsoft.Authorization/roleDefinitions/75262d2f-6665-aada-da4e-2bcafd5b2bc5" # forces replacement
      ~ role_definition_name             = "Register Azure Resource Providers" -> (known after apply)
        scope                            = "/subscriptions/{UAT Subscription ID}"
      + skip_service_principal_aad_check = (known after apply)
    }

Plan: 2 to add, 0 to change, 2 to destroy.
a30004736 commented 4 years ago

Facing exactly the issue. Was anybody able to resolve?

darren-johnson commented 4 years ago

Yes I did literally yesterday! If you add a lifecycle ignore-changes block it stops happening.

I hope this helps.

  lifecycle {
    ignore_changes = [
      role_definition_id,
    ]
  }
a30004736 commented 4 years ago

@darren-johnson : Thank you that helped.

juanjojulian commented 4 years ago

Same situation here, "ignore_changes" is a patch that works but this should be investigated and solved. In my case this happens only with custom roles, once we assign them it works but It wants to modify the resource each time we re-apply:

~ role_definition_id = "/subscriptions/SUBSCRIPTION_ID/providers/Microsoft.Authorization/roleDefinitions/d7e9b9d6-a3d1-9518-0e6b-ff17d47c9078" -> "/providers/Microsoft.Authorization/roleDefinitions/d7e9b9d6-a3d1-9518-0e6b-ff17d47c9078" # forces replacement

jibonilla03 commented 4 years ago

I have same problem for a custom role, it always try to replace the role definition even when I add the ignore changes, can anyone assist ?

Terraform v0.12.28

locals {
  role_definition_id = uuid()
}
resource "azurerm_role_definition" "custom" {
  role_definition_id = local.role_definition_id
  name               = var.role_definition_name
  scope              = data.azurerm_management_group.gbs_cloud.id
  description        = var.role_definition_description

  permissions {
    actions     = var.role_actions
    not_actions = var.role_not_actions
  }

  assignable_scopes = [
    data.azurerm_management_group.gbs_cloud.id, 
  ]

   lifecycle {
    ignore_changes = [
      role_definition_id,
    ]
  }
}
Terraform will perform the following actions:
-/+ resource "azurerm_role_definition" "custom" {
        assignable_scopes  = [
            "/providers/Microsoft.Management/managementGroups/GBSCloud",
        ]
        description        = "This is a custom role created via Terraform"
      ~ id                 = "/providers/Microsoft.Authorization/roleDefinitions/709c011c-46c1-c5bd-5431-f5b12058399b" -> (known after apply)
        name               = "Custom Log Analytics"
      ~ role_definition_id = "709c011c-46c1-c5bd-5431-f5b12058399b" -> (known after apply)
      + scope              = "/providers/Microsoft.Management/managementGroups/GBSCloud" # forces replacement

      ~ permissions {
            actions          = [
                "*/read",
                "Microsoft.OperationalInsights/workspaces/analytics/query/action",
                "Microsoft.OperationalInsights/workspaces/search/action",
                "Microsoft.Insights/AlertRules/Throttled/Action",
                "Microsoft.Insights/AlertRules/Resolved/Action",
                "Microsoft.Insights/AlertRules/Activated/Action",
                "Microsoft.Insights/AlertRules/Read",
                "Microsoft.Insights/AlertRules/Delete",
                "Microsoft.Insights/AlertRules/Write",
            ]
          - data_actions     = [] -> null
            not_actions      = [
                "Microsoft.OperationalInsights/workspaces/sharedKeys/read",
            ]
          - not_data_actions = [] -> null
        }
    }

Plan: 1 to add, 0 to change, 1 to destroy.
juanjojulian commented 4 years ago

I have same problem for a custom role, it always try to replace the role definition even when I add the ignore changes, can anyone assist ?

Terraform v0.12.28

  • provider.azurerm v2.17.0
locals {
  role_definition_id = uuid()
}
resource "azurerm_role_definition" "custom" {
  role_definition_id = local.role_definition_id
  name               = var.role_definition_name
  scope              = data.azurerm_management_group.gbs_cloud.id
  description        = var.role_definition_description

  permissions {
    actions     = var.role_actions
    not_actions = var.role_not_actions
  }

  assignable_scopes = [
    data.azurerm_management_group.gbs_cloud.id, 
  ]

   lifecycle {
    ignore_changes = [
      role_definition_id,
    ]
  }
}
Terraform will perform the following actions:
-/+ resource "azurerm_role_definition" "custom" {
        assignable_scopes  = [
            "/providers/Microsoft.Management/managementGroups/GBSCloud",
        ]
        description        = "This is a custom role created via Terraform"
      ~ id                 = "/providers/Microsoft.Authorization/roleDefinitions/709c011c-46c1-c5bd-5431-f5b12058399b" -> (known after apply)
        name               = "Custom Log Analytics"
      ~ role_definition_id = "709c011c-46c1-c5bd-5431-f5b12058399b" -> (known after apply)
      + scope              = "/providers/Microsoft.Management/managementGroups/GBSCloud" # forces replacement

      ~ permissions {
            actions          = [
                "*/read",
                "Microsoft.OperationalInsights/workspaces/analytics/query/action",
                "Microsoft.OperationalInsights/workspaces/search/action",
                "Microsoft.Insights/AlertRules/Throttled/Action",
                "Microsoft.Insights/AlertRules/Resolved/Action",
                "Microsoft.Insights/AlertRules/Activated/Action",
                "Microsoft.Insights/AlertRules/Read",
                "Microsoft.Insights/AlertRules/Delete",
                "Microsoft.Insights/AlertRules/Write",
            ]
          - data_actions     = [] -> null
            not_actions      = [
                "Microsoft.OperationalInsights/workspaces/sharedKeys/read",
            ]
          - not_data_actions = [] -> null
        }
    }

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

Your problem is related to an issue in azurerm_role_definition that was introduced in version 2.16, downgrade to version 2.15 and it will work as expected. Please don't forget to post in issue #7549 to let the developers know that there is more people affected.

jibonilla03 commented 4 years ago

@juanjojulian Thanks downgrade to 2.15 works for me

bernardmaltais commented 4 years ago

@juanjojulian 2.15 still gives the error in my case ;-(

boillodmanuel commented 4 years ago

Hi, I found a workaround for role_definition_id that should be applied in some situations only:

Manuel

juanjojulian commented 3 years ago

Thanks for the tip @boillodmanuel , in my case it doesn't work, there must be some kind of mess in azurerm_role_assignment resource in my version combination:

Terraform v0.13.5
+ provider registry.terraform.io/hashicorp/azuread v1.1.1
+ provider registry.terraform.io/hashicorp/azurerm v2.38.0

For instance, if I write:

role_definition_id = azurerm_role_definition.customRole.id

Terraform wants to set role_definition_id to:

"/providers/Microsoft.Authorization/roleDefinitions/1b7485d0-f713-e168-5a5b-0816ba0b0646|/providers/Microsoft.Management/managementGroups/myManagementGroupName"

Which according to Documentation should be the output for "role_definition_id" not for "id"

But then if I make use of role_definition_resource_id

role_definition_id = azurerm_role_definition.customRole.role_definition_resource_id

Terraform wants to set role_definition_id to:

"/providers/Microsoft.Authorization/roleDefinitions/1b7485d0-f713-e168-5a5b-0816ba0b0646"

Which initially works but in subsequent applies Terraform will want to change it again and again:

 "/subscriptions/6hsbdka-d4cf-98e6-ff0c-3114fjkds8dbhnb43/providers/Microsoft.Authorization/roleDefinitions/1b7485d0-f713-e168-5a5b-0816ba0b0646" -> "/providers/Microsoft.Authorization/roleDefinitions/1b7485d0-f713-e168-5a5b-0816ba0b0646"

So the (ugly) workaround that works for me its:

role_definition_id = "${data.azurerm_subscription.mySubscription.id}/providers/Microsoft.Authorization/roleDefinitions/${ azurerm_role_definition.customRole.role_definition_id}"

I really cannot understand what's going on with this resource in azurerm, this issue has been open for one year now.

CarlosSardo commented 3 years ago

Facing the same issue, when using Terraform v0.14.2 and azurerm provider v2.36.0

MattGarner-N commented 3 years ago

This is still happening

darren-johnson commented 3 years ago

Quick update. I have just tested this with @thedevopscat using Terraform v0.14.8 & azurerm v2.51.0

Using the role_definition_name instead of role_definition_id does not cause the issue to occur, however you cannot use a custom role_definition_name as a datasource just by specifying the name.

It would be great if this can be fixed.

nikolaifa commented 3 years ago

My understanding suggests that a role definition object is created within each scope specified within the azurerm_role_definition resource. The object referred to in azurerm_role_assignment -> role_definition_id should be the one created for the scope set in azurerm_role_assignment -> role_definition_id.

The provider tries to use the object existing in its own subscription (the one specified in the provider configuration).

The (ugly) workaround suggested by @juanjojulian worked for me.

To avoid confusion between azurerm_role_definition and azurerm_role_assignment the input should be named role_definition_resource_id, not role_definition_id.

torbendury commented 2 years ago

How is this more than 2 yrs old and still not fixed?

chris92109 commented 2 years ago

We face the same problem with TF 1.0.9 and AzureRM 2.71.0.

anarsen commented 2 years ago

I ended up setting role_definition_name instead of the role_definition_id when assigning custom roles. Since custom role definition names are unique across a tenant this will work.

AdamSir2 commented 2 years ago

@darren-johnson is right there is no role definition name in the data source - which I find very inconsistent. I would have this problem would have lead to a name reference being created...

julienym commented 11 months ago

I noticed that if my data source is missing the scope it would be missing the /sub.../XXX prefix for the role_definition_id

data "azurerm_role_definition" "this" {
  name  = "ABC"
  # scope = var.role_subscription_id
}

resource "azurerm_role_assignment" "this" {
  scope              = azurerm_storage_account.this.id
...
  role_definition_id = data.azurerm_role_definition.this.id
}

PLAN:
-/+ resource "azurerm_role_assignment" "this" {
...
      ~ role_definition_id               = "/subscriptions/XXX/providers/Microsoft.Authorization/roleDefinitions/YYY" -> "/providers/Microsoft.Authorization/roleDefinitions/YYY" # forces replacement
dcarvalh04 commented 8 months ago

Issue still occurs with source = "hashicorp/azurerm" version "3.89.0" and terraform 1.7

data "azurerm_role_definition" "builtin_owner" {
  name = "Owner"
}

resource "azurerm_pim_eligible_role_assignment" "mg_finops_owner" {
  scope              = azurerm_management_group.xxxxx.id
  role_definition_id = data.azurerm_role_definition.builtin_owner.id
  principal_id       = azuread_group.xxxxx.object_id
}

Each terraform plan leads to:

Terraform will perform the following actions:

  # azurerm_pim_eligible_role_assignment.mg_finops_owner must be replaced
-/+ resource "azurerm_pim_eligible_role_assignment" "xxxxxxx" {
      ~ id                 = "/providers/Microsoft.Management/managementGroups/xxxxx|/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635|xxxxx" -> (known after apply)
      ~ principal_type     = "Group" -> (known after apply)
        # (3 unchanged attributes hidden)

      - schedule { # forces replacement
          - start_date_time = "2024-01-29T15:46:49.0788364+00:00" -> null

          - expiration {
              - duration_days  = 0 -> null
              - duration_hours = 0 -> null
            }
        }
    }

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

Fixed by adding:

lifecycle {
    ignore_changes = [
      schedule,
    ]
  }

The resource was originally created without any schedule as it's optional.

mikeclayton commented 5 months ago

I just hit this issue again - I think there's multiple duplicate issues logged in this repo:

The root of the problem is that custom role definitions with multiple assignable scopes appear to have multiple valid resource ids - one for each assignable scope.

When you create an assignment you have to use the resource id of the definition that aligns with the scope of the assignment, otherwise the Azure API will silently "correct" the value on write, which means the next time you run a "terraform plan" the value retrieved from the Azure API doesn't match the expected value in the terraform state and you end up with eternal drift.

See my comment here for a hacky workaround that dynamically calculates the "correct" resource id based on the assignment scope and uses that in the resource assignment properties. It's truly awful and I'm not proud of it, but it stops the endless drift detection...

https://github.com/hashicorp/terraform-provider-azurerm/issues/8764#issuecomment-757992474

Satak commented 1 month ago

We have the same problem. When using custom role assignments, Terraform wants to recreate the assignment since the role_definition_id is not correct.

(this is the existing proper role definition id) role_definition_id: "/subscriptions/xxx/providers/Microsoft.Authorization/roleDefinitions/zzz"

(now Terraform want to change it to this, and after the apply it is back to correct one since Azure "fixes" the id. So Azure and Terraform are never in sync.) change to: "/providers/Microsoft.Authorization/roleDefinitions/zzz"

Forces replacement


I did also a bit of investigation and when assigning Azure custom roles to different scopes:

Management Group level assignment: "id": "/providers/Microsoft.Authorization/roleDefinitions/zzz"

Subscription level assignment: "id": "/subscriptions/xxx/providers/Microsoft.Authorization/roleDefinitions/zzz"

Resource level assignment: "id": "/subscriptions/xxx/providers/Microsoft.Authorization/roleDefinitions/zzz"

stevehipwell commented 1 month ago

This has been broken for a long time now. We've been manually adding the correct prefix to stop the churn.