Skip to main content
Easier Azure IAM in Terraform
  1. Posts/

Easier Azure IAM in Terraform

·636 words·3 mins·
Table of Contents

When you are creating role assignments in terraform using azurerm, it can quickly become literally unreadable. Unless you place comments to which principal you are granting, you will surely forget what that string of numbers represents.

An example of a standard role assignment
#

resource "azurerm_role_assignment" "example" {
  scope                = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-testing/providers/Microsoft.KeyVault/vaults/kv-testing"
  role_definition_name = "Key Vault Secrets User"
  principal_id         = "00000000-0000-0000-0000-000000000001"
}

Here it is clear that you are granting Key Vault Secrets User on kv-testing, but to who? The principal_id tells you nothing.

Ive created a module which makes it more reader friendly.

My solution
#

This module supports granting roles to Security Groups and Service Principals. Why no users? Managing role assignments at the group level is simpler and more maintainable than granting them to individual users.

Terraform Check it out on the terraform registry!

Configuration
#

To configure this module, you give it a role_assignments list. This is a list of objects. The objects look like this:

{
  scope = ""
  role_name = ""
  principal = {
    type = ""
    name = ""
  }
  description = ""
}
  • scope: still the same as before: the Resource ID
  • role_name: the name of the role (e.g. Reader, Key Vault Secrets User)
  • principal.type: either Security Group or Service Principal
  • principal.name: the exact name of the security group or service principal
  • description: optional, defaults to “Assigned through Terraform”

principal.type gets converted to lowercase and spaces get replaced with _, so security_group is also valid.

Module usage example
#

module "smart_iam" {
  source = "nietarne/smart-iam/azurerm"
  version = "0.1.0" # optional

  # or git source:
  # source = "git::https://codeberg.org/nietarne/terraform-azure-smart-iam"
  # if you want to pin a version / commit: add "?rev={tag or commit sha-1}"

  role_assignments = [
    {
      role_name = "Role name"
      scope     = "Resource ID"
      principal = {
        type = "Service Principal | Security Group"
        name = "name of SP / SG"
      }
    }
  ]
}

Now, this is a lot better already in my opinion. But I don’t usually configure the roles in my .tf files. I’d rather use the .tfvars for the configuration, this way you can easily place multiple environment configurations next to eachother. The simplest way of doing this:

variable "role_assignments" {
  description = "List of role assignments"
  default     = []
}

module "smart_iam" {
  source = "nietarne/smart-iam/azurerm"
  version = "0.1.0" # optional

  # or git source:
  # source = "git::https://codeberg.org/nietarne/terraform-azure-smart-iam"
  # if you want to pin a version / commit: add "?rev={tag or commit sha-1}"

  role_assignments = var.role_assignments
}

In the repository README there also is an example of a fully typed variable config for the role_assignments. This is what I would recommend to add instead of the bare variable above.

If you’ve added the variable, you can add the following into your .tfvars:

role_assignments = [
  {
    role_name = "Key Vault Secrets User"
    scope     = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-testing/providers/Microsoft.KeyVault/vaults/kv-testing"
    principal = {
      type = "Security Group"
      name = "SG-TERRAFORM-TESTING"
    }
  },
  {
    role_name = "Key Vault Secrets Officer"
    scope     = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-testing/providers/Microsoft.KeyVault/vaults/kv-testing"
    principal = {
      type = "Service Principal"
      name = "SP-TERRAFORM-PRD"
    }
  },
]

As you can see, no weird ID’s. The scope is still something I would want to edit to be easier to read, but for now this is perfect for me.

How it works
#

If you have ever played with or used terraform, it basically works exactly like you would think it works. Based on the type of the principal, it will query azuread for the security group or service principal with that exact name using a data resource.

Once we have the object from azuread, we just create a standard role assignment using the principal_id that is in the returned object.

Arne Van Looveren
Author
Arne Van Looveren
IaC & DevOps enthousiast. I also make open source stuff.