39.3 Azure Active Directory Integration and Managed Identities
Right, let’s talk about identity. It’s the single most important and, let’s be honest, most frequently botched part of any cloud deployment. You can have the most beautifully architected app, but if it can’t talk to its database, it’s just a very expensive error message. In the old days, we’d be slinging secrets and connection strings into environment variables like we were throwing confetti. Don’t do that. It’s 2024, and we have better ways. Specifically, for your AKS cluster, we have Azure Active Directory (AAD) integration and the absolute game-changer that is Managed Identities.
Think of it this way: AAD integration is about who you are (authentication), and Managed Identities are about what you’re allowed to do (authorization). You need both for a complete, secure picture.
Integrating Azure AD for Kubernetes Authentication
First up, let’s make kubectl access less of a wild west show. By integrating AAD, you’re tying Kubernetes RBAC directly to your Azure AD users and groups. This means your devs, ops engineers, and CI/CD pipelines can all use their existing Azure AD credentials to authenticate with your AKS API server. No more managing a separate set of static, forever-lived tokens or certificates that someone will inevitably check into a public GitHub repo. I’ve seen it happen.
The magic happens when you create the cluster. You can’t easily add this later (a classic Azure “decision”), so you have to plan for it upfront. Here’s the crucial az aks create command:
az aks create \
--resource-group myResourceGroup \
--name myAADCluster \
--node-count 1 \
--enable-aad \
--aad-admin-group-object-ids <your-aad-group-object-id> \
--aad-tenant-id <your-azure-ad-tenant-id>
The --aad-admin-group-object-ids is the key. This is the Object ID of your Azure AD security group (e.g., aks-admins). Anyone in that group gets automatically mapped to the cluster-admin role in Kubernetes. It’s a clean, manageable, and auditable way to handle access. Once the cluster is up, you and your team just run az aks get-credentials --resource-group myResourceGroup --name myAADCluster, and it’ll seamlessly use your AAD token. When you run kubectl get nodes, it’s you doing it, not some generic service account.
Leveraging Managed Identities for Pods and Cluster Resources
Now, for the pièce de résistance: Managed Identities. This is where the real security win happens. A Managed Identity is essentially a service principal—an identity for an application or service—that is automatically managed by Azure itself. You don’t have to see, know, or rotate its password. Ever.
There are two flavors here, and the distinction is critical:
- Cluster-level Managed Identity: This is the identity used by the cluster’s infrastructure itself (e.g., the VMs in the node pool) to talk to the Azure API to do things like configure load balancers or pull from ACR. When you create a modern AKS cluster, this is the default. You can see it in the Azure portal; it’s named something like
myAADCluster-agentpool. - Pod-level Managed Identities (a.k.a. Azure Workload Identity): This is the correct, modern way for your individual application pods to get identities to access other Azure services (like a Storage Account, a SQL Database, or a Key Vault). The old way, Azure Pod Identity, was deprecated for a reason—it was complex and had some scary security edge cases. Workload Identity is the successor, and it’s brilliant.
Workload Identity uses the standard, battle-tested Kubernetes service account tokens that get projected into your pod. This token is then exchanged by a mutating webhook for an Azure AD access token specific to the Managed Identity you’ve assigned. Here’s how you set it up.
First, create a Managed Identity and grant it access to, say, a storage account.
# Create the identity
az identity create --name my-app-identity --resource-group myResourceGroup
# Get the details we need
export CLIENT_ID="$(az identity show --name my-app-identity --resource-group myResourceGroup --query 'clientId' -o tsv)"
export TENANT_ID="$(az account show --query tenantId -o tsv)"
Then, you create a Kubernetes service account annotated to bind it to that Azure identity.
# service-account.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: default
annotations:
azure.workload.identity/client-id: "${CLIENT_ID}"
Now, in your pod spec, you reference that service account and add a couple of labels so the Azure webhook can do its job.
# pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod
namespace: default
labels:
azure.workload.identity/use: "true"
spec:
serviceAccountName: my-app-sa
containers:
- name: my-app
image: my-app:latest
env:
- name: AZURE_CLIENT_ID
value: "${CLIENT_ID}"
- name: AZURE_TENANT_ID
value: "${TENANT_ID}"
- name: AZURE_FEDERATED_TOKEN_FILE
value: "/var/run/secrets/azure/tokens/azure-identity-token"
Inside your application code (e.g., using the Azure SDK for Python, .NET, etc.), it will automatically look for those environment variables and use the federated token to authenticate. Your app now has secure, credential-free access to Azure resources, and you sleep better at night.
Common Pitfalls and the “Why”
- The AAD Integration Point of No Return: You absolutely must decide on AAD integration at cluster creation. It’s a one-way door. This is annoying, but the reason is that it fundamentally changes how the API server is secured. Plan ahead.
- Mixing Up Cluster and Pod Identity: Remember, the cluster’s identity (for infrastructure) and your pod’s identity (for your app logic) are separate. Don’t grant your cluster’s identity broad permissions to your data services. That’s a huge violation of the principle of least privilege. Use a specific pod-level Workload Identity for that.
- Not Using Workload Identity: If you’re still messing around with putting client secrets in Kubernetes secrets, stop. Right now. It’s a ticking time bomb. Workload Identity is the sanctioned, secure path. The Azure SDKs have fantastic support for it.
- Permission Bloat: Just because you can give a Managed Identity the Contributor role on your entire subscription doesn’t mean you should. Be surgical. Grant access only to the specific resources it needs, with the least privileged role possible (e.g.,
Storage Blob Data Contributorinstead ofContributor). Your future security-aware self will thank you.