From a49f3a46571533194592a78be252dab5f484ce46 Mon Sep 17 00:00:00 2001 From: RebeccaCalixte <262454636+Rebecca-Calixte@users.noreply.github.com> Date: Thu, 13 Aug 2026 17:01:33 -0400 Subject: [PATCH] Add AKS Entra Kubernetes RBAC quickstart --- quickstart/101-aks-entra-k8s-rbac/README.md | 123 ++++++++++++ quickstart/101-aks-entra-k8s-rbac/main.tf | 204 ++++++++++++++++++++ 2 files changed, 327 insertions(+) create mode 100644 quickstart/101-aks-entra-k8s-rbac/README.md create mode 100644 quickstart/101-aks-entra-k8s-rbac/main.tf diff --git a/quickstart/101-aks-entra-k8s-rbac/README.md b/quickstart/101-aks-entra-k8s-rbac/README.md new file mode 100644 index 000000000..4dbcd67a5 --- /dev/null +++ b/quickstart/101-aks-entra-k8s-rbac/README.md @@ -0,0 +1,123 @@ +# Use Microsoft Entra ID Groups with Kubernetes RBAC in AKS + +This template uses Microsoft Entra ID groups with Kubernetes role-based access control (Kubernetes RBAC) in an existing Azure Kubernetes Service (AKS) cluster. + +The example creates two Microsoft Entra groups and scopes each group to a namespace: + +- The `appdev` group can manage resources in the `dev` namespace. +- The `opssre` group can manage resources in the `sre` namespace. + +The AKS cluster must already have Microsoft Entra integration and Kubernetes RBAC enabled. Azure RBAC for Kubernetes Authorization must be disabled for this example. + +## Prerequisites + +- An Azure subscription +- An existing AKS cluster with Microsoft Entra integration and Kubernetes RBAC enabled +- Azure RBAC for Kubernetes Authorization disabled on the cluster +- Terraform `>= 1.6.0` installed +- Azure CLI and `kubectl` installed +- Permission to create Microsoft Entra users and groups +- Permission to assign Azure roles at the AKS cluster scope +- Permission to manage Kubernetes resources on the AKS cluster + +Sign in to Azure and select the subscription to use: + +```console +az login +az account set --subscription +``` + +## Terraform providers and variables + +This sample uses the AzureRM provider to reference the existing AKS cluster and assign Azure permissions, the AzureAD provider to create Microsoft Entra users and groups, and the Kubernetes provider to create namespaces, Roles, and RoleBindings. + +The Terraform variables require values for the existing cluster and the example users: + +```hcl +resource_group_name = "" +aks_cluster_name = "" +appdev_user_principal_name = "" +opssre_user_principal_name = "" +temporary_password = "" +``` + +`temporary_password` is a sensitive Terraform variable. Replace `` with a strong temporary password supplied through a secure `terraform.tfvars` file or another protected input method. Do not commit that value. + +## Example + +Create a `terraform.tfvars` file with values for the existing cluster and test users, then initialize, format, and validate the configuration: + +```console +terraform init +terraform fmt +terraform validate +``` + +Review and apply the configuration: + +```console +terraform plan +terraform apply +``` + +The configuration creates the following Microsoft Entra and Kubernetes resources: + +- `appdev` and `opssre` Microsoft Entra security groups +- One example Microsoft Entra user in each group +- Cluster User Role assignments for both groups +- `dev` and `sre` Kubernetes namespaces +- Namespace-scoped Kubernetes Roles and RoleBindings + +## Verify namespace access + +Get credentials for the AKS cluster: + +```console +az aks get-credentials \ + --resource-group \ + --name +``` + +Verify that both namespaces exist: + +```console +kubectl get namespaces +``` + +The output should include `dev` and `sre`. + +## Test appdev access + +Authenticate as the `appdev` test user and create a pod in the `dev` namespace: + +```console +kubectl run nginx-dev \ + --image=nginx \ + --restart=Never \ + --namespace dev +kubectl get pods --namespace dev +``` + +Listing pods across all namespaces or creating a pod in the `sre` namespace should return a `Forbidden` error because the `appdev` group is scoped to `dev`. + +## Test opssre access + +Authenticate as the `opssre` test user and create a pod in the `sre` namespace: + +```console +kubectl run nginx-sre \ + --image=nginx \ + --restart=Never \ + --namespace sre +kubectl get pods --namespace sre +``` + +Creating a pod in the `dev` namespace should return a `Forbidden` error because the `opssre` group is scoped to `sre`. + +## Clean up + +Remove the namespaces, RoleBindings, Roles, groups, users, and role assignments created by this configuration: + +```console +terraform destroy +``` diff --git a/quickstart/101-aks-entra-k8s-rbac/main.tf b/quickstart/101-aks-entra-k8s-rbac/main.tf new file mode 100644 index 000000000..1236beeae --- /dev/null +++ b/quickstart/101-aks-entra-k8s-rbac/main.tf @@ -0,0 +1,204 @@ +terraform { + required_version = ">= 1.6.0" + + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~> 4.0" + } + azuread = { + source = "hashicorp/azuread" + version = "~> 3.0" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = "~> 2.30" + } + } +} + +provider "azurerm" { + features {} +} + +provider "azuread" {} + +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_user_principal_name" { + type = string + description = "User principal name for the application developer test user." +} + +variable "opssre_user_principal_name" { + type = string + description = "User principal name for the SRE test user." +} + +variable "temporary_password" { + type = string + description = "Temporary password used for the example Microsoft Entra users." + sensitive = true +} + +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 "azuread_group" "appdev" { + display_name = "appdev" + security_enabled = true +} + +resource "azuread_group" "opssre" { + display_name = "opssre" + security_enabled = true +} + +resource "azuread_user" "aksdev" { + user_principal_name = var.appdev_user_principal_name + display_name = "AKS Dev" + mail_nickname = "aksdev" + password = var.temporary_password +} + +resource "azuread_user" "akssre" { + user_principal_name = var.opssre_user_principal_name + display_name = "AKS SRE" + mail_nickname = "akssre" + password = var.temporary_password +} + +resource "azuread_group_member" "appdev_member" { + group_object_id = azuread_group.appdev.object_id + member_object_id = azuread_user.aksdev.object_id +} + +resource "azuread_group_member" "opssre_member" { + group_object_id = azuread_group.opssre.object_id + member_object_id = azuread_user.akssre.object_id +} + +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 = azuread_group.appdev.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 = azuread_group.opssre.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 = azuread_group.appdev.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 = azuread_group.opssre.object_id + api_group = "rbac.authorization.k8s.io" + } +} + +output "appdev_group_object_id" { + value = azuread_group.appdev.object_id +} + +output "opssre_group_object_id" { + value = azuread_group.opssre.object_id +}