This tutorial takes you from an empty resource group to a production-grade AKS cluster: private API server, workload identity, Azure CNI Overlay, autoscaling, and Container Insights — all in Bicep, all reproducible.
Prerequisites
- Azure CLI ≥ 2.60 with the
aks-previewextension - Bicep CLI ≥ 0.28
- Owner rights on a subscription (or a vended landing-zone subscription)
Step 1 — Parameters and naming
@description('Workload name used in resource naming')
param workload string = 'shop'
param location string = resourceGroup().location
param kubernetesVersion string = '1.30'
var clusterName = 'aks-${workload}-${location}-prod'Step 2 — The cluster resource
The decisions that matter: system/user node pool separation, Azure CNI Overlay (pod IPs stop eating your VNet), workload identity instead of secrets, and a private API server.
resource aks 'Microsoft.ContainerService/managedClusters@2024-05-01' = {
name: clusterName
location: location
identity: { type: 'SystemAssigned' }
properties: {
kubernetesVersion: kubernetesVersion
dnsPrefix: workload
apiServerAccessProfile: { enablePrivateCluster: true }
networkProfile: {
networkPlugin: 'azure'
networkPluginMode: 'overlay'
networkPolicy: 'cilium'
}
securityProfile: {
workloadIdentity: { enabled: true }
}
oidcIssuerProfile: { enabled: true }
agentPoolProfiles: [
{
name: 'system'
mode: 'System'
count: 3
vmSize: 'Standard_D4ds_v5'
availabilityZones: ['1', '2', '3']
onlyCriticalAddonsEnabled: true
}
{
name: 'apps'
mode: 'User'
vmSize: 'Standard_D8ds_v5'
enableAutoScaling: true
minCount: 2
maxCount: 12
availabilityZones: ['1', '2', '3']
}
]
}
}Step 3 — Monitoring
resource insights 'Microsoft.ContainerService/managedClusters/providers/diagnosticSettings@2021-05-01-preview' = {
name: '${clusterName}/Microsoft.Insights/default'
properties: {
workspaceId: logAnalyticsId
logs: [
{ category: 'kube-audit-admin', enabled: true }
{ category: 'guard', enabled: true }
]
}
}Enable Container Insights and Prometheus metrics through the portal toggle or the azureMonitorProfile block — then actually build the two alerts that matter first: node NotReady > 5 min, and pending pods > 10 min.
Step 4 — Deploy and verify
az deployment group create \
--resource-group rg-shop-prod \
--template-file main.bicep
az aks command invoke -g rg-shop-prod -n aks-shop-westeurope-prod \
--command "kubectl get nodes -o wide"az aks command invoke is the private-cluster-friendly way to run kubectl without VPN plumbing — perfect for CI smoke tests.
Where to go next
- Wire deployments through GitOps (Flux is a first-class AKS extension).
- Add Azure Policy for AKS to enforce pod security standards.
- Load-test the autoscaler before your traffic does it for you.
The complete repository for this tutorial — pipeline included — is linked in the resources section below.
Discussion (0)
Sign in to join the discussion. Comments are moderated.