Simon Emms

Software Engineer, Technical Leader, Solutions Designer

Setting Terraform Service Principal Permissions to Work With Azure Active Directory

Published:

There are certain things that, no matter how many times I do them, I always have to look up. Symbolic links, for instance, are one thing I must have done at least once a week for the past 10 years, but I just can't remember whether the /path/to/file or the /path/to/symlink comes first (it's ln -s /path/to/file /path/to/symlink for the record).

Configuring the permissions for a service principal to work with Azure Active Directory is a close second. Unlike the symlink though, the documentation for this is dreadful; all the commands are there, but it's just so verbose and wordy that I always miss something.

For anyone just interested in the answer (including Future Simon), the tl;dr is here

What's a Service Principal

From the Azure documentation:

An Azure service principal is an identity created for use with applications, hosted services, and automated tools to access Azure resources. This access is restricted by the roles assigned to the service principal, giving you control over which resources can be accessed and at which level. For security reasons, it's always recommended to use service principals with automated tools rather than allowing them to log in with a user identity.

What's So Special About Active Directory

In the topology of Azure, the AzureRM resources and the AzureAD resources occupy a different place. AzureRM (short for "Azure Resource Manager") lives under a subscription. In normal circumstances, and by default, Azure will only have a single subscription per account. All the resources (such as a virtual machine) will live in here. When you want to manage one of these resources with Terraform, simply give the service principal the appropriate permissions (I usually go with Owner) on the subscription and everything will work fine.

Active Directory is different. Logically, the Active Directory resources sit outside the subscription. These are on the account itself. That means that, if you have multiple subscriptions on your Azure account, you would still only have a single Active Directory which manages things for both subscriptions.

This means that the permissions model is different in Active Directory to the subscriptions.

Subscription IAM

This is the Identity and Access Management (IAM) page for a subscription. As you can see, the "Terraform" Service Principal has the "Owner" role. The "Access control (IAM)" blade doesn't exist for the Active Directory.

Why Would You Ever Want To Mess About With The Active Directory

There are plenty of legitimate reasons to work with the Active Directory in Terraform. One of my most regular reasons is, when setting up a Kubernetes cluster, I also set up an "admins" and a "users" group for managing which users can get access to the cluster. Those in the "admins" group have full admin access to the cluster, those in the "users" group can only get limited access via the role-based access control (RBAC) settings. The configuration for that is fairly simple:

data "azurerm_client_config" "current" {}

resource "azuread_group" "admin" {
  name = "k8s-admin"
  description = "Admin-level members for Kubernetes"
  owners = [data.azurerm_client_config.current.object_id]
  prevent_duplicate_names = true
}

resource "azuread_group" "user" {
  name = "k8s-user"
  description = "User-level members for Kubernetes"
  owners = [data.azurerm_client_config.current.object_id]
  prevent_duplicate_names = true
}

resource "azurerm_kubernetes_cluster" "k8s" {
  role_based_access_control {
    enabled = true
    azure_active_directory {
      tenant_id = data.azurerm_client_config.current.tenant_id
      managed = true
      admin_group_object_ids = [azuread_group.admin.id]
    }
  }

  # Additional configuration - see Terraform docs for details
}

resource "azurerm_role_assignment" "admin" {
  principal_id = azuread_group.admin.id
  scope = azurerm_resource_group.k8s.id
  role_definition_name = "Azure Kubernetes Service Cluster Admin Role"
}

resource "azurerm_role_assignment" "user" {
  principal_id = azuread_group.user.id
  scope = azurerm_resource_group.k8s.id
  role_definition_name = "Azure Kubernetes Service Cluster User Role"
}

Any user added to either of these groups will get the appropriate permissions on the Kubernetes cluster.

How To Set It Up

We'll be setting up the service principal as a Group Administrator and also giving the service principal the appropriate API access.

Setting as a Group Administrator

This gives the service principal to administer groups. This is needed to add and remove groups and assign members to it.

Granting API Access

Important. The Terraform provider still uses the old (and deprecated) API rather than the new Microsoft Graph API. Work is happening to move over to that, but at the time of writing, is still incomplete.

  • Log into portal.azure.com and navigate to Azure Active Directory.
  • Select the App Registrations blade
  • Select your service principal
  • Select "API permissions" from the blade on the left
  • Select "Add a permission" and select the legacy "Azure Active Directory Graph" at the very bottom of the page.
  • Under "Delegated permissions", select "Directory.ReadWrite.All" and "Group.ReadWrite.All". Then click "Add permissions" to save.
  • Select "Add a permission", select the "Azure Active Directory Directory Graph" again
  • Under "Application permissions", select "Application.ReadWrite.All". Then click "Add permissions" to save

That's it. Now when you run terraform apply, it will have the permissions to create the groups with your desired configuration. Importantly, if you terraform destroy, it will also have the permissions to delete the configuration.

Delegation Permissions

Delegate Permissions

Application Permissions

Application Permissions


Credits

Photo by Nichols Dmitrichev

Do you like this article?