A New AKS Cluster Baseline for 2022: What Changed in Late 2021
Most AKS clusters I review were created from a template someone wrote in 2019 or 2020, and it shows: a service principal secret that expires, a static admin kubeconfig shared in a password manager, and upgrades that happen when somebody remembers. Over 2021, AKS shipped GA replacements for nearly every one of those habits, several in the last two months of the year. If you’re creating a cluster this month, the defaults you copy from an old repo are now the wrong defaults.
This isn’t a list of every release note. It’s the short list of changes that should alter a new cluster’s baseline, the Bicep I’d start from, and the places I’d still hold back.
What reached GA in 2021 that matters for a new cluster
The AKS release notes on GitHub are the primary source here, and they’re worth reading weekly if you run clusters. These are the items I consider baseline-changing, with their status as of early January 2022:
| Capability | Status (Jan 2022) | Why it changes the baseline |
|---|---|---|
| Azure RBAC for Kubernetes authorization | GA (May 2021) | Cluster permissions become Azure role assignments, audited and managed like everything else |
| Disable local accounts | GA (December 2021) | Removes the static cluster-admin credential |
| Cluster auto-upgrade channels | GA (November 2021) | Patch and minor upgrades on a schedule instead of on memory |
| Secrets Store CSI driver add-on (Key Vault provider) | GA (2 November 2021) | Supported, managed way to mount Key Vault secrets |
| Open Service Mesh add-on | GA (2 November 2021, regions rolling out) | A supported mesh if you genuinely need mTLS and traffic splitting |
| Kubernetes 1.22 | GA on AKS (December 2021) | Removes a batch of long-deprecated beta APIs |
| GitOps (Flux v2) extension | Preview | Worth testing, not yet a production dependency |
| Microsoft Defender for Containers | Preview | Evaluate alongside your existing Defender for Kubernetes setup |
Managed identity for the cluster itself and AKS-managed Azure AD integration aren’t new; both have been GA since 2020. What’s new is that the pieces built on top of them are now GA too, so there’s no remaining reason to start a cluster with a service principal or legacy AAD integration.
Identity: managed identity plus Azure RBAC, and no local accounts
The combination I’d now treat as the default is a system-assigned managed identity for the control plane, AKS-managed Azure AD integration, and Azure RBAC for Kubernetes authorization. With Azure RBAC enabled, a role assignment such as “Azure Kubernetes Service RBAC Reader” scoped to a namespace controls who can do what, and it shows up in the same access reviews as the rest of your subscription. Kubernetes service accounts still go through normal Kubernetes RBAC, which is what you want.
The piece people skip is disableLocalAccounts. Without it, anyone with permission to call az aks get-credentials --admin gets a certificate-based cluster-admin kubeconfig that bypasses Azure AD entirely. It’s the finding I’d fix first in any AKS review. Disabling local accounts closes it. The trade-off is that your break-glass path now depends on Azure AD: make sure the admin group in adminGroupObjectIDs contains accounts that are excluded from the Conditional Access policies that could lock everyone out.
Disabling local accounts also breaks any CI/CD pipeline that runs az aks get-credentials --admin today, not at some future version. Those pipelines need to authenticate as a service principal or managed identity through kubelogin’s non-interactive modes (kubelogin convert-kubeconfig -l spn or -l msi), and that identity needs an Azure RBAC role on the cluster, such as “Azure Kubernetes Service RBAC Writer” scoped to the namespaces it deploys to.
Upgrades: pick a channel and stop doing it by hand
The auto-upgrade channels are none, patch, stable, rapid and node-image. My rule of thumb: patch for production clusters, so you get security patches within the minor version automatically while minor upgrades stay a deliberate, tested change; stable for dev and test clusters, so they stay ahead of production and surface breaking changes first. I’d avoid rapid for anything shared. Pair auto-upgrade with Pod Disruption Budgets on every workload that matters, or the drain will either stall or take down more replicas than you expected. I covered the drain mechanics in more detail in Azure Kubernetes Service Upgrades: A Practical Guide.
The reason this matters now: Kubernetes 1.19 was officially deprecated on AKS on 30 November 2021, and the limited holiday courtesy extension ends on 31 January 2022, after which 1.19 is removed. AKS supports only a window of minor versions. Clusters that drift out of that window are the ones that end up doing two or three minor upgrades under pressure.
Secrets: Key Vault through the CSI driver add-on
The Secrets Store CSI driver add-on (azureKeyvaultSecretsProvider) moved from a Helm chart you installed and patched yourself to a managed add-on, and went GA in November 2021. It mounts Key Vault secrets, keys and certificates as files, can optionally sync them into Kubernetes secrets, and supports rotation polling. If you’re still storing connection strings in Kubernetes secrets created by a pipeline, this is the upgrade path.
Pod identity: still the awkward gap
The weak point in the baseline is how a pod authenticates to Azure. There are three options today, and none of them is a clean answer:
- AAD pod-managed identity on AKS is still in preview, and its NMI DaemonSet intercepts IMDS traffic with privileged node access. I wrote about how it works in Secure Azure Access with AKS Pod Identity.
- Azure AD Workload Identity is the better design: Kubernetes projects a service account token and Azure AD exchanges it through a federated credential, with no node-level interception. But the open-source project is pre-1.0 (v0.7.0 shipped in December 2021), and running it against AKS relies on preview capabilities. The details are in Azure AD Workload Identity for AKS.
- The kubelet’s managed identity, shared by every pod on the node. Fine for pulling from ACR; wrong for application data access, because every workload inherits the same permissions.
My position for January 2022: use the Key Vault CSI driver for secrets, keep application-level Azure access to as few workloads as possible, and design those workloads so the credential source is swappable (the Azure Identity SDKs’ DefaultAzureCredential chain helps). I’d rather migrate a handful of deployments to workload identity once it matures than build pod identity into every namespace now.
The Bicep I’d start from
This template uses the 2021-10-01 API version, which includes disableLocalAccounts, the managed AAD profile and autoUpgradeProfile. It assumes an existing Log Analytics workspace and an Azure AD group for cluster admins.
@description('Name of the AKS cluster.')
param clusterName string
@description('Azure region for the cluster.')
param location string = resourceGroup().location
@description('Object ID of the Azure AD group that gets cluster admin.')
param adminGroupObjectId string
@description('Resource ID of an existing Log Analytics workspace.')
param logAnalyticsWorkspaceId string
resource aks 'Microsoft.ContainerService/managedClusters@2021-10-01' = {
name: clusterName
location: location
sku: {
name: 'Basic'
tier: 'Paid' // Uptime SLA; use 'Free' for dev/test
}
identity: {
type: 'SystemAssigned'
}
properties: {
dnsPrefix: clusterName
kubernetesVersion: '1.22.4'
enableRBAC: true
disableLocalAccounts: true
aadProfile: {
managed: true
enableAzureRBAC: true
adminGroupObjectIDs: [
adminGroupObjectId
]
}
autoUpgradeProfile: {
upgradeChannel: 'patch'
}
networkProfile: {
networkPlugin: 'azure'
networkPolicy: 'calico'
loadBalancerSku: 'standard'
}
agentPoolProfiles: [
{
name: 'system'
mode: 'System'
vmSize: 'Standard_DS3_v2'
osDiskType: 'Ephemeral'
osDiskSizeGB: 100
count: 3
enableAutoScaling: true
minCount: 3
maxCount: 5
availabilityZones: [
'1'
'2'
'3'
]
nodeTaints: [
'CriticalAddonsOnly=true:NoSchedule'
]
}
{
name: 'apps'
mode: 'User'
vmSize: 'Standard_DS3_v2'
osDiskType: 'Ephemeral'
osDiskSizeGB: 100
count: 3
enableAutoScaling: true
minCount: 3
maxCount: 10
availabilityZones: [
'1'
'2'
'3'
]
}
]
addonProfiles: {
azureKeyvaultSecretsProvider: {
enabled: true
config: {
enableSecretRotation: 'true'
}
}
azurepolicy: {
enabled: true
}
omsagent: {
enabled: true
config: {
logAnalyticsWorkspaceResourceID: logAnalyticsWorkspaceId
}
}
}
}
}
output kubeletIdentityObjectId string = aks.properties.identityProfile.kubeletidentity.objectId
Deploy it and connect with your own Azure AD identity, not the admin credential:
az deployment group create \
--resource-group <your-resource-group> \
--template-file aks.bicep \
--parameters clusterName=<your-cluster-name> \
adminGroupObjectId=<your-admin-group-object-id> \
logAnalyticsWorkspaceId=<your-workspace-resource-id>
az aks get-credentials --resource-group <your-resource-group> --name <your-cluster-name>
kubectl get nodes
The account running az aks get-credentials needs the “Azure Kubernetes Service Cluster User Role” (which grants listClusterUserCredential), plus either membership of the admin group or an Azure RBAC Kubernetes role on the cluster. The kubeconfig it writes uses the in-tree azure auth provider, so the first kubectl get nodes prompts for a device-code sign-in.
A few decisions in that template are worth explaining:
- Separate system and user pools. The
CriticalAddonsOnlytaint keeps application pods off the system pool, so a noisy workload can’t starve CoreDNS or the metrics server. The trade-off is a minimum of six nodes; for a small dev cluster, a single pool is fine. More on pool design in Designing AKS Node Pools for Production Workloads. - Ephemeral OS disks. Faster node reimage and scale-out, no managed disk cost for the OS. The disk has to fit in the VM’s cache, which is why the size is set explicitly; check the cache size of whatever SKU you choose.
- Azure CNI with Calico. I default to Azure CNI for enterprise networks because pods get routable VNet IPs, at the cost of IP address planning. If your address space is tight, kubenet is the honest alternative.
- Azure Policy add-on. Pod Security Policy is deprecated upstream, and AKS’s preview PSP support is on the way out. Azure Policy with the built-in pod security initiatives is the supported replacement on AKS today.
Before you upgrade to 1.22
Kubernetes 1.22 removed a set of beta APIs that many Helm charts still used, including extensions/v1beta1 and networking.k8s.io/v1beta1 Ingress, apiextensions.k8s.io/v1beta1 CustomResourceDefinitions, and the v1beta1 admission webhook configurations. New clusters on 1.22 will simply reject those manifests. For existing clusters, scan your charts and rendered manifests before the upgrade, not after. Older ingress-nginx and cert-manager releases are the usual offenders.
Two further changes are announced in the release notes and worth planning for now: containerd becomes the default runtime for Windows node pools from 1.23, and in-tree Azure AD authentication in kubeconfig is deprecated from 1.23 in favour of the exec-based kubelogin plugin. If your CI pipelines authenticate to AKS with non-interactive kubeconfigs, that second one will break them unless you prepare.
When not to use this baseline
This template is for a shared production or pre-production cluster. I’d cut it down in a few cases:
- Short-lived dev clusters. Drop the Uptime SLA, use one pool, and accept the
stablechannel. - Teams without an Azure AD admin story. Disabling local accounts without a tested break-glass group is how you lock yourself out of a cluster. Fix the identity process first.
- Workloads that don’t need Kubernetes. If you’re running a few HTTP APIs and queue workers, Azure Container Apps (public preview since November) or App Service may be a better fit. I made that argument in Azure in 2022: Six Bets I’m Making on New Year’s Day.
The short version
If I had to fit the 2022 AKS baseline on a sticky note: managed identity, Azure AD with Azure RBAC, local accounts off, an auto-upgrade channel, Key Vault through the CSI add-on, and Azure Policy instead of PSP. All of that is GA today. Pod-level identity is the one area where I’d keep the design flexible and wait for workload identity to mature. Once the baseline is in place, the next conversation is usually cost, which I cover in AKS Cost Review: Fix Requests First, Then Nodes, Then Billing.