Edit

Use Kubernetes RBAC with Microsoft Entra ID in AKS

Azure Kubernetes Service (AKS) can be configured to use Microsoft Entra ID for user authentication. In this configuration, you sign in to an AKS cluster using a Microsoft Entra authentication token. Once authenticated, you can use the built-in Kubernetes role-based access control (RBAC) to manage access to namespaces and cluster resources based on a user's identity or group membership.

This article shows you how to:

  • Control access using Kubernetes RBAC in an AKS cluster based on Microsoft Entra group membership.

  • Create example groups and users in Microsoft Entra ID.

  • Create Roles and RoleBindings in an AKS cluster granting the appropriate permissions, such as to create and view resources.

Prerequisites

Use the Azure portal or Azure CLI to verify Microsoft Entra integration with Kubernetes RBAC is enabled.

To verify using the Azure portal:

  1. Sign-in to the Azure portal and navigate to your AKS cluster resource.
  2. In the service menu, under Settings, select Security configuration.
  3. Under the Authentication and Authorization section, verify the Microsoft Entra authentication with Kubernetes RBAC option is selected.

If you plan to use Terraform to configure Kubernetes RBAC for this article, you also need:

  • Terraform version 1.6.0 or later installed.
  • kubectl installed and configured to connect to your AKS cluster. kubectl is automatically installed when you run az aks install-cli or as part of the Azure Cloud Shell.
  • Permission to create Azure role assignments at the scope of your AKS cluster, and permission to manage Kubernetes resources on the cluster.
  • Two existing Microsoft Entra groups and, optionally, two existing Microsoft Entra test users to validate access with. If you don't have these resources yet, complete Create groups in Microsoft Entra ID and Create users in Microsoft Entra ID before continuing. However, skip the az role assignment create command in that section, since the Terraform configuration in this article creates the role assignments for you.

Note

If you don't already have an AKS cluster with Microsoft Entra integration and Kubernetes RBAC enabled to test with, this article's Terraform sample includes a prequisite configuration that creates one for you. For more information, see Deploy the prerequisite AKS cluster with Terraform.

Create groups in Microsoft Entra ID

This section teaches you how to create two user roles to show how Kubernetes RBAC and Microsoft Entra ID control access cluster resources. The following two example roles are:

  • Application developer

    • A user named aksdev that's part of the appdev group.
  • Site reliability engineer (SRE)

    • A user named akssre that's part of the opssre group.

In production environments, you can use existing users and groups within a Microsoft Entra tenant.

  1. First, get the resource ID of your AKS cluster using the az aks show command. Then, assign the resource ID to a variable named AKS_ID so it can be referenced in other commands.

    AKS_ID=$(az aks show \
        --resource-group myResourceGroup \
        --name myAKSCluster \
        --query id -o tsv)
    
  2. Create the first example group in Microsoft Entra ID for the application developers using the az ad group create command. The following example creates a group named appdev:

    APPDEV_ID=$(az ad group create --display-name appdev --mail-nickname appdev --query id -o tsv)
    
  3. Create an Azure role assignment for the appdev group using the az role assignment create command. This assignment lets any member of the group use kubectl to interact with an AKS cluster by granting them the Azure Kubernetes Service Cluster User Role.

    az role assignment create \
      --assignee $APPDEV_ID \
      --role "Azure Kubernetes Service Cluster User Role" \
      --scope $AKS_ID
    

    Important

    The role name you specify must exactly match the Azure role definition name, including capitalization and spacing.

    Tip

    If you receive an error such as Principal 35bfec9328bd4d8d9b54dea6dac57b82 doesn't exist in the directory a5443dcd-cd0e-494d-a387-3039b419f0d5., wait a few seconds for the Microsoft Entra group object ID to propagate through the directory then try the az role assignment create command again.

    Skip this command. The Terraform configuration in Deploy Kubernetes RBAC with Terraform creates this role assignment for you.

Create users in Microsoft Entra ID

After you create the example Microsoft Entra ID groups for application developers and SREs, the next step is to create two corresponding user accounts. These users are used to sign in to the AKS cluster and validate the Kubernetes RBAC integration described later in this article.

Before you begin, you must set the user principal name (UPN) and password for the application developers. The UPN must include the verified domain name of your tenant. For example, an application developer user, aksdev@contoso.com. In order to figure out (or set) the verified domain names in your tenant, see Managing custom domain names in your Microsoft Entra ID.

The following command prompts you for the UPN and sets it to AAD_DEV_UPN so it can be used in a later command:

echo "Please enter the UPN for application developers: " && read AAD_DEV_UPN

The following command prompts you for the password and sets it to AAD_DEV_PW for use in a later command:

echo "Please enter the secure password for application developers: " && read AAD_DEV_PW

Create user accounts

  1. Create the first user account in Microsoft Entra ID using the az ad user create command. The following example creates a user with the display name AKS Dev, the UPN, and secure password using the values in AAD_DEV_UPN and AAD_DEV_PW:

    AKSDEV_ID=$(az ad user create \
      --display-name "AKS Dev" \
      --user-principal-name $AAD_DEV_UPN \
      --password $AAD_DEV_PW \
      --query id -o tsv)
    
  2. Add the user to the appdev group created in the previous section using the az ad group member add command:

    az ad group member add --group appdev --member-id $AKSDEV_ID
    

Create AKS cluster resources

We have our Microsoft Entra groups, users, and Azure role assignments created. Now, you configure the AKS cluster to allow these different groups access to specific resources.

  1. Get the cluster admin credentials using the az aks get-credentials command. In one of the following sections, you get the regular user cluster credentials to see the Microsoft Entra authentication flow in action.

    az aks get-credentials --resource-group myResourceGroup --name myAKSCluster --admin
    
  2. Create a namespace in the AKS cluster using the kubectl create namespace command. The following example creates a namespace name dev:

    kubectl create namespace dev
    

    Note

    In Kubernetes, Roles define the permissions to grant, and RoleBindings apply them to desired users or groups. These assignments can be applied to a given namespace, or across the entire cluster. For more information, see Using Kubernetes RBAC authorization.

    If the user you grant the Kubernetes RBAC binding for is in the same Microsoft Entra tenant, assign permissions based on the UPN. If the user is in a different Microsoft Entra tenant, query for and use the objectId property instead.

  3. Create a Role for the dev namespace, which grants full permissions to the namespace. In production environments, you can specify more granular permissions for different users or groups. Create a file named role-dev-namespace.yaml and paste the following YAML manifest:

    kind: Role
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: dev-user-full-access
      namespace: dev
    rules:
    - apiGroups: ["", "extensions", "apps"]
      resources: ["*"]
      verbs: ["*"]
    - apiGroups: ["batch"]
      resources:
      - jobs
      - cronjobs
      verbs: ["*"]
    
  4. Create the Role using the kubectl apply command and specify the filename of your YAML manifest.

    kubectl apply -f role-dev-namespace.yaml
    
  5. Get the resource ID for the appdev group using the az ad group show command. This group is set as the subject of a RoleBinding in the next step.

    az ad group show --group appdev --query id -o tsv
    
  6. Create a RoleBinding for the appdev group to use the previously created Role for namespace access. Create a file named rolebinding-dev-namespace.yaml and paste the following YAML manifest. On the last line, replace groupObjectId with the group object ID output from the previous command.

    kind: RoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: dev-user-access
      namespace: dev
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: Role
      name: dev-user-full-access
    subjects:
    - kind: Group
      # Replace the placeholder below with the group's objectId (GUID)
      name: groupObjectId
    

    Tip

    If you want to create the RoleBinding for a single user, specify kind: User and replace groupObjectId with the UPN in the previous sample.

  7. Create the RoleBinding using the kubectl apply command and specify the filename of your YAML manifest:

    kubectl apply -f rolebinding-dev-namespace.yaml
    

Review the Terraform code

Note

The sample code for this article is located in the Azure Terraform GitHub repo. You can view the log file containing the test results from current and previous versions of Terraform.

See more articles and sample code showing how to use Terraform to manage Azure resources.

The sample uses two Terraform configurations, each in its own directory with its own state:

  • The prequisite configuration creates a resource group and an AKS cluster with Microsoft Entra integration and Kubernetes RBAC enabled, which the repository's own end-to-end tests use to validate the scenario configuration against a real cluster. If you already have an AKS cluster that meets this article's prerequisites, skip this configuration and use your own resource group name, cluster name, and Microsoft Entra group object IDs with the scenario configuration instead.
  • The scenario configuration, in the root of the sample, assigns the Azure Kubernetes Service Cluster User Role to the appdev and opssre groups at the cluster scope. It also creates the dev and sre Kubernetes namespaces with namespace-scoped Roles and RoleBindings that grant each group access only to its own namespace.

Terraform authenticates by using your Azure CLI sign-in context. Before you continue, ensure you're signed in by using az login and have selected the correct subscription by using az account set --subscription .

Deploy the prerequisite AKS cluster with Terraform

Note

This stage is optional. Complete it only if you don't already have an AKS cluster with Microsoft Entra integration and Kubernetes RBAC enabled, and Azure RBAC for Kubernetes Authorization disabled, to test with.

  1. Create a directory named prequisite and make it your current directory.

  2. Create a file named versions.tf and insert the following code:

    terraform {
      required_version = ">= 1.6.0"
    
      required_providers {
        azurerm = {
          source  = "hashicorp/azurerm"
          version = "~> 4.0"
        }
        random = {
          source  = "hashicorp/random"
          version = "~> 3.6"
        }
      }
    }
    
    provider "azurerm" {
      features {
        resource_group {
          prevent_deletion_if_contains_resources = false
        }
      }
    }
    
  3. Create a file named variables.tf and add the following code:

    variable "location" {
      type        = string
      default     = "eastus"
      description = "Location of the resources."
    }
    
    variable "node_count" {
      type        = number
      default     = 1
      description = "Number of nodes in the default node pool of the AKS cluster."
    }
    
  4. Create a file named main.tf and add the following code:

    data "azurerm_client_config" "current" {}
    
    resource "random_string" "suffix" {
      length  = 6
      special = false
      upper   = false
    }
    
    resource "azurerm_resource_group" "rg" {
      location = var.location
      name     = "rg-101-aks-entra-k8s-rbac-${random_string.suffix.result}"
    }
    
    resource "azurerm_kubernetes_cluster" "aks" {
      location            = azurerm_resource_group.rg.location
      name                = "aks-101-entra-k8s-rbac-${random_string.suffix.result}"
      resource_group_name = azurerm_resource_group.rg.name
      dns_prefix          = "aks-${random_string.suffix.result}"
      # Kubernetes RBAC is required by the example, Azure RBAC for Kubernetes
      # Authorization must stay disabled.
      role_based_access_control_enabled = true
    
      default_node_pool {
        name       = "agentpool"
        node_count = var.node_count
        vm_size    = "Standard_D2s_v3"
      }
    
      identity {
        type = "SystemAssigned"
      }
    
      azure_active_directory_role_based_access_control {
        azure_rbac_enabled = false
        tenant_id          = data.azurerm_client_config.current.tenant_id
      }
    }
    
  5. Create a file named outputs.tf and insert the following code:

    output "resource_group_name" {
      description = "Name of the resource group that contains the AKS cluster."
      value       = azurerm_resource_group.rg.name
    }
    
    output "aks_cluster_name" {
      description = "Name of the AKS cluster with Microsoft Entra integration and Kubernetes RBAC enabled."
      value       = azurerm_kubernetes_cluster.aks.name
    }
    
    output "appdev_group_object_id" {
      description = "Object ID of the existing test principal used for developer access."
      value       = data.azurerm_client_config.current.object_id
    }
    
    output "opssre_group_object_id" {
      description = "Object ID of the existing test principal used for SRE access."
      value       = azurerm_kubernetes_cluster.aks.identity[0].principal_id
    }
    
  6. Initialize, format, and validate the configuration.

    terraform init
    terraform fmt
    terraform validate
    
  7. Review and apply the configuration.

    terraform plan -out main.tfplan
    terraform apply main.tfplan
    
  8. Get the outputs. You use these values as input for the scenario configuration in the next section.

    terraform output -raw resource_group_name
    terraform output -raw aks_cluster_name
    terraform output -raw appdev_group_object_id
    terraform output -raw opssre_group_object_id
    

Important

The appdev_group_object_id and opssre_group_object_id outputs from the prequisite configuration are test principal IDs, not real Microsoft Entra groups. They resolve to the object ID of your currently signed-in Azure CLI account and the AKS cluster's managed identity principal ID, and exist only so the repository's automated tests can validate that the scenario configuration applies successfully. To test the Microsoft Entra group and user experience described in this article, use the group object IDs from Create groups in Microsoft Entra ID instead.

Deploy Kubernetes RBAC with Terraform

  1. Create a new directory, separate from prequisite, and make it your current directory. This configuration has its own Terraform state and doesn't manage or depend on the state of the prequisite configuration.

  2. Create a file named main.tf and add the following code:

    terraform {
      required_version = ">= 1.6.0"
    
      required_providers {
        azurerm = {
          source  = "hashicorp/azurerm"
          version = "~> 4.0"
        }
        kubernetes = {
          source  = "hashicorp/kubernetes"
          version = "~> 2.30"
        }
      }
    }
    
    provider "azurerm" {
      features {}
    }
    
    variable "resource_group_name" {
      type        = string
      description = "Name of the resource group that contains the existing AKS cluster."
    }
    
    variable "aks_cluster_name" {
      type        = string
      description = "Name of the existing AKS cluster with Microsoft Entra integration and Kubernetes RBAC enabled."
    }
    
    variable "appdev_group_object_id" {
      type        = string
      description = "Object ID of an existing Microsoft Entra group used for developer access to the dev namespace."
    }
    
    variable "opssre_group_object_id" {
      type        = string
      description = "Object ID of an existing Microsoft Entra group used for SRE access to the sre namespace."
    }
    
    data "azurerm_kubernetes_cluster" "aks" {
      name                = var.aks_cluster_name
      resource_group_name = var.resource_group_name
    }
    
    provider "kubernetes" {
      host                   = data.azurerm_kubernetes_cluster.aks.kube_admin_config[0].host
      client_certificate     = base64decode(data.azurerm_kubernetes_cluster.aks.kube_admin_config[0].client_certificate)
      client_key             = base64decode(data.azurerm_kubernetes_cluster.aks.kube_admin_config[0].client_key)
      cluster_ca_certificate = base64decode(data.azurerm_kubernetes_cluster.aks.kube_admin_config[0].cluster_ca_certificate)
    }
    
    resource "azurerm_role_assignment" "appdev_cluster_user" {
      scope                = data.azurerm_kubernetes_cluster.aks.id
      role_definition_name = "Azure Kubernetes Service Cluster User Role"
      principal_id         = var.appdev_group_object_id
    }
    
    resource "azurerm_role_assignment" "opssre_cluster_user" {
      scope                = data.azurerm_kubernetes_cluster.aks.id
      role_definition_name = "Azure Kubernetes Service Cluster User Role"
      principal_id         = var.opssre_group_object_id
    }
    
    resource "kubernetes_namespace" "dev" {
      metadata {
        name = "dev"
      }
    }
    
    resource "kubernetes_namespace" "sre" {
      metadata {
        name = "sre"
      }
    }
    
    resource "kubernetes_role" "dev_full_access" {
      metadata {
        name      = "dev-user-full-access"
        namespace = kubernetes_namespace.dev.metadata[0].name
      }
    
      rule {
        api_groups = ["", "extensions", "apps"]
        resources  = ["*"]
        verbs      = ["*"]
      }
    
      rule {
        api_groups = ["batch"]
        resources  = ["jobs", "cronjobs"]
        verbs      = ["*"]
      }
    }
    
    resource "kubernetes_role" "sre_full_access" {
      metadata {
        name      = "sre-user-full-access"
        namespace = kubernetes_namespace.sre.metadata[0].name
      }
    
      rule {
        api_groups = ["", "extensions", "apps"]
        resources  = ["*"]
        verbs      = ["*"]
      }
    
      rule {
        api_groups = ["batch"]
        resources  = ["jobs", "cronjobs"]
        verbs      = ["*"]
      }
    }
    
    resource "kubernetes_role_binding" "dev_user_access" {
      metadata {
        name      = "dev-user-access"
        namespace = kubernetes_namespace.dev.metadata[0].name
      }
    
      role_ref {
        api_group = "rbac.authorization.k8s.io"
        kind      = "Role"
        name      = kubernetes_role.dev_full_access.metadata[0].name
      }
    
      subject {
        kind      = "Group"
        name      = var.appdev_group_object_id
        api_group = "rbac.authorization.k8s.io"
      }
    }
    
    resource "kubernetes_role_binding" "sre_user_access" {
      metadata {
        name      = "sre-user-access"
        namespace = kubernetes_namespace.sre.metadata[0].name
      }
    
      role_ref {
        api_group = "rbac.authorization.k8s.io"
        kind      = "Role"
        name      = kubernetes_role.sre_full_access.metadata[0].name
      }
    
      subject {
        kind      = "Group"
        name      = var.opssre_group_object_id
        api_group = "rbac.authorization.k8s.io"
      }
    }
    
  3. Create a file named terraform.tfvars and provide the resource group name and cluster name for the AKS cluster you want to configure, and the object IDs of the appdev and opssre groups.

    resource_group_name    = ""
    aks_cluster_name       = ""
    appdev_group_object_id = ""
    opssre_group_object_id = ""
    
  4. Initialize, format, and validate the configuration.

    terraform init
    terraform fmt
    terraform validate
    
  5. Review and apply the configuration.

    terraform plan -out main.tfplan
    terraform apply main.tfplan
    

    The configuration creates the Azure role assignments for both groups, the dev and sre Kubernetes namespaces, and namespace-scoped Roles and RoleBindings that grant the appdev group access to dev and the opssre group access to sre.

Verify namespace access

  1. Get the cluster admin credentials by using the az aks get-credentials command.

    az aks get-credentials --resource-group myResourceGroup --name myAKSCluster --admin
    
  2. Verify that both namespaces exist by using the kubectl get namespaces command.

    kubectl get namespaces
    

    The output shows the dev and sre namespaces, along with the default namespaces created for the cluster.

To test that the appdev and opssre groups can access only their assigned namespace, continue to Access AKS cluster resources with Microsoft Entra identities.

Clean up Terraform resources

Important

Destroy the scenario configuration before the prequisite configuration. The scenario configuration's Kubernetes provider needs the AKS cluster to still exist so Terraform can remove the namespaces, Roles, and RoleBindings it created.

  1. From the scenario configuration directory, remove the Azure role assignments, namespaces, Roles, and RoleBindings it created.

    terraform destroy
    
  2. If you deployed the prequisite AKS cluster only to test this sample and no longer need it, remove it from the prequisite directory after the scenario configuration finishes destroying its resources.

    terraform destroy
    

Access AKS cluster resources with Microsoft Entra identities

Now, test that the expected permissions work when you create and manage resources in an AKS cluster. In these examples, you schedule and view pods in the user's assigned namespace, and try to schedule and view pods outside of the assigned namespace.

  1. Reset the kubeconfig context using the az aks get-credentials command. In a previous section, you set the context using the cluster admin credentials. The admin user bypasses Microsoft Entra sign-in prompts. Without the --admin parameter, the user context is applied that requires all requests to be authenticated using Microsoft Entra ID.

    az aks get-credentials --resource-group myResourceGroup --name myAKSCluster --overwrite-existing
    
  2. Schedule a basic NGINX pod using the kubectl run command in the dev namespace:

    kubectl run nginx-dev --image=mcr.microsoft.com/oss/nginx/nginx:1.15.5-alpine --namespace dev
    
  3. Enter the credentials for the appdev group account (enter your own credentials) at the sign-in prompt. Once you're successfully signed in, the account token is cached for future kubectl commands. The NGINX is successfully scheduled as shown in the following example output:

    $ kubectl run nginx-dev --image=mcr.microsoft.com/oss/nginx/nginx:1.15.5-alpine --namespace dev
    
    To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code B24ZD6FP8 to authenticate.
    
    pod/nginx-dev created
    
  4. Use the kubectl get pods command to view pods in the dev namespace:

    kubectl get pods --namespace dev
    
  5. Ensure the status of the NGINX pod is Running. The output looks like the following output:

    $ kubectl get pods --namespace dev
    
    NAME        READY   STATUS    RESTARTS   AGE
    nginx-dev   1/1     Running   0          4m
    

Test SRE access to AKS cluster resources

To confirm that our Microsoft Entra group membership and Kubernetes RBAC work correctly between different users and groups, try the previous commands when signed in as the akssre user.

  1. Reset the kubeconfig context using the az aks get-credentials command that clears the previously cached authentication token for the aksdev user.

    az aks get-credentials --resource-group myResourceGroup --name myAKSCluster --overwrite-existing
    
  2. Schedule and view pods in the assigned SRE namespace. When prompted, sign in with the opssre group account credentials (enter your own credentials).

    kubectl run nginx-sre --image=mcr.microsoft.com/oss/nginx/nginx:1.15.5-alpine --namespace sre
    kubectl get pods --namespace sre
    

    As shown in the following example output, you can successfully create and view the pods:

    $ kubectl run nginx-sre --image=mcr.microsoft.com/oss/nginx/nginx:1.15.5-alpine --namespace sre
    
  3. To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code BM4RHP3FD to authenticate.

    pod/nginx-sre created
    
    $ kubectl get pods --namespace sre
    
    NAME        READY   STATUS    RESTARTS   AGE
    nginx-sre   1/1     Running   0
    
  4. Try to view or schedule pods outside of assigned SRE namespace.

    kubectl get pods --all-namespaces
    kubectl run nginx-sre --image=mcr.microsoft.com/oss/nginx/nginx:1.15.5-alpine --namespace dev
    

    These kubectl commands fail, as shown in the following example output. The user's group membership and Kubernetes Role and RoleBindings don't grant permissions to create or manager resources in other namespaces.

    $ kubectl get pods --all-namespaces
    Error from server (Forbidden): pods is forbidden: User "akssre@contoso.com" cannot list pods at the cluster scope
    
    $ kubectl run nginx-sre --image=mcr.microsoft.com/oss/nginx/nginx:1.15.5-alpine --namespace dev
    Error from server (Forbidden): pods is forbidden: User "akssre@contoso.com" cannot create pods in the namespace "dev"
    

Create and view cluster resources outside of the assigned namespace

To view pods outside of the dev namespace. Use the kubectl get pods command using --all-namespaces:

kubectl get pods --all-namespaces

The user's group membership doesn't have a Kubernetes Role that allows this action, as shown in the following example output:

Error from server (Forbidden): pods is forbidden: User "aksdev@contoso.com" cannot list resource "pods" in API group "" at the cluster scope

In the same way, schedule a pod in a different namespace, such as the SRE namespace. The user's group membership doesn't align with a Kubernetes Role and RoleBinding to grant these permissions, as shown in the following example output:

$ kubectl run nginx-dev --image=mcr.microsoft.com/oss/nginx/nginx:1.15.5-alpine --namespace sre

Error from server (Forbidden): pods is forbidden: User "akssre@contoso.com" cannot create resource "pods" in API group "" in the namespace "sre"

Clean up cluster resources

To clean up all of the resources, run the following commands.

Get the admin kubeconfig context and delete the dev and sre namespaces. This action also deletes the pods, Roles, and RoleBindings.

az aks get-credentials --resource-group myResourceGroup --name myAKSCluster --admin

kubectl delete namespace dev
kubectl delete namespace sre

If you used Terraform to create the dev and sre namespaces, Roles, and RoleBindings, see Clean up Terraform resources instead of running kubectl delete namespace.

Delete the Microsoft Entra ID user accounts for aksdev and akssre.

az ad user delete --upn-or-object-id $AKSDEV_ID
az ad user delete --upn-or-object-id $AKSSRE_ID

Delete the Microsoft Entra ID groups for appdev and opssre. This action also deletes any remaining Azure role assignments.

az ad group delete --group appdev
az ad group delete --group opssre

Next steps