On your kubeadm cluster (Ch 18) access was a certificate in a kubeconfig. On AKS the company wants people to log in with their Entra accounts, lose access the day they leave, and get only their team's namespace. Getting into an AKS cluster involves two separate permission checks, on two planes again (control plane and data plane, 22.23).
What you need to know already: 15.1 (kubeconfig, contexts), 17.30 (Kubernetes RBAC: Role, RoleBinding), 17.43 (users are certificates), 22.8 (Entra ID, groups), 22.13 (Azure RBAC: roles, scopes, dataActions), 22.18 (groups, PIM), 22.12 (workload identity).
1. Getting a kubeconfig (ARM, control plane)
az aks get-credentials -g rg-oncall-lab -n aks-sysop
Merged "aks-sysop" as current context in /home/learner/.kube/config
az aks get-credentials -g <rg> -n <cluster> downloads a kubeconfig for the cluster. That call is Microsoft.ContainerService/managedClusters/listClusterUserCredential/action, granted by Azure Kubernetes Service Cluster User Role (or Contributor/Owner). It merges into your existing kubeconfig - it does not replace the file - and switches the current context.
On an Entra-integrated cluster the kubeconfig contains no credential, just an exec plugin - a program kubectl runs to fetch a fresh token each time:
users:
- name: clusterUser_rg-oncall-lab_aks-sysop
user:
exec:
command: kubelogin
args: [get-token, --environment, AzurePublicCloud, --server-id, 6dae42f8-4368-4678-94ff-3960e28e3630, ...]
kubectl runs kubelogin (Microsoft's helper that gets Entra tokens for kubectl) to get a token for the AKS server app (6dae42f8-... is the same in every tenant). kubelogin convert-kubeconfig -l azurecli makes it reuse your az login; automation uses -l workloadidentity or -l spn (a service principal, 22.8).
2. What you may do inside (the Kubernetes API, data plane)
Two models:
- Kubernetes RBAC with Entra identities: Roles/ClusterRoles and RoleBindings in the cluster, whose subjects are Entra users or group object ids. Managed with kubectl or Terraform, like any Kubernetes object.
- Azure RBAC for Kubernetes authorization (
--enable-azure-rbac): the API server asks Azure whether your token may do this, using role assignments - and they can be scoped to the cluster or to a namespace:
/subscriptions/.../managedClusters/aks-sysop whole cluster
/subscriptions/.../managedClusters/aks-sysop/namespaces/shop one namespace
Azure Kubernetes Service RBAC Reader read most objects (not secrets)
Azure Kubernetes Service RBAC Writer read/write most objects (not roles)
Azure Kubernetes Service RBAC Admin everything in the scope, incl. roles
Azure Kubernetes Service RBAC Cluster Admin everything, everywhere
These are dataActions. Owner of the subscription gets Forbidden from the Kubernetes API on an Azure-RBAC cluster until given one of them - the Key Vault lesson again. Both models can run side by side; Kubernetes RBAC still applies.
So a developer on the shop team needs both: Cluster User Role (to fetch the kubeconfig) and RBAC Writer on namespaces/shop (to deploy). Grant both to the group.
3. The back door: local accounts
Every AKS cluster also has a local clusterAdmin certificate:
az aks get-credentials -g rg-oncall-lab -n aks-sysop --admin
It is cluster-admin, bypasses Entra ID and Azure RBAC entirely, is not tied to a person, and cannot be revoked without rotating the cluster certificates (az aks rotate-certs, which restarts everything). Disable it:
az aks update -g rg-oncall-lab -n aks-sysop --disable-local-accounts
# on a cluster with local accounts disabled
az aks get-credentials -g rg-oncall-lab -n aks-sysop --admin
ERROR: (BadRequest) Getting static credential is not allowed because this cluster is set to disable local accounts.
Before you do, make sure a break-glass path exists (an emergency way in, used only when everything else fails): an Entra group with RBAC Cluster Admin that a PIM activation (22.18) can put you into. With local accounts off and Entra down, there is no way in - that is the trade-off, and it is the right one for production.
Private clusters
--enable-private-cluster puts the API server behind a private endpoint (23.10) in the node resource group (privatelink.<region>.azmk8s.io zone). kubectl then only works from networks that resolve and route to it - a machine inside the VNet, a jump box, or az aks command invoke, which runs kubectl inside the cluster for you.
The two error messages, side by side
# as alice, with no Azure role on the cluster
az aks get-credentials -g rg-oncall-lab -n aks-sysop
ERROR: (AuthorizationFailed) The client 'alice@...' ... does not have authorization to perform
action 'Microsoft.ContainerService/managedClusters/listClusterUserCredential/action' ...
The ARM gate: Alice is missing Cluster User Role (or anything with that action) on the cluster.
# as alice, holding a kubeconfig but no Azure RBAC role on the cluster
kubectl get pods -n shop
Error from server (Forbidden): pods is forbidden: User "[email protected]" cannot
list resource "pods" in API group "" in the namespace "shop": User does not have access to the
resource in Azure. Update role assignment to allow access.
The Kubernetes API gate with Azure RBAC: she has a kubeconfig but no RBAC Reader/Writer at the cluster or the shop namespace scope. The last sentence ("User does not have access to the resource in Azure") is how you know it is Azure RBAC deciding and not a Kubernetes RoleBinding.
Automation
Deploy automation needs the same two things for its identity: Cluster User Role (or it runs az aks command invoke) and an RBAC role at the namespace it deploys to. Use workload identity federation for its identity (22.21, the GitHub OIDC mission) and kubelogin convert-kubeconfig -l workloadidentity (or -l azurecli after az login) - never the --admin credential.
What you can now do
- Tell the two gates apart: ARM (may you download a kubeconfig?) and the Kubernetes API (may your token do this?), and read each one's error.
- Grant a team deploy rights to one namespace with two role assignments to its group.
- Disable local accounts and rotate certificates to close the admin back door.