Skip to main content
Cloudorial
Tutorials3 min read8.5K views436

Deploy a Production AKS Cluster with Bicep, Step by Step

From empty resource group to a hardened, monitored, autoscaling cluster — every file included

Published

Updated

Abstract cover illustration for “Deploy a Production AKS Cluster with Bicep, Step by Step”

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-preview extension
  • Bicep CLI ≥ 0.28
  • Owner rights on a subscription (or a vended landing-zone subscription)

Step 1 — Parameters and naming

text
@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.

text
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

text
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

bash
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.