What a Bicep File Is + How This One Deploys AKS

Definitions first (Bicep + Resource Group), then a precise step-by-step walkthrough of how this Bicep deploys an Azure Container Registry (ACR), an AKS cluster, and the ACR pull role assignment into a Resource Group scope.

Bicep Resource Group AKS ACR RBAC
Critical scope clarification
This Bicep file is declared as targetScope = 'resourceGroup'. That means it deploys resources into an existing Resource Group. It does not create the Resource Group itself. Creating the RG happens outside the template (for example via az group create), then you run az deployment group create to deploy this file into that RG.

1) What is a Bicep file?

Azure Bicep is a declarative Infrastructure-as-Code (IaC) language for Azure. You describe desired state (resources, properties, dependencies), and Azure Resource Manager (ARM) performs the deployment.

  • Declarative: you describe what you want, not step-by-step imperatively
  • Compiled to ARM JSON: Bicep is a higher-level authoring format
  • Idempotent: rerunning produces the same intended state (converges)
  • Parameterized: supports parameters, outputs, and expressions
Where it fits in your flow
  1. Create/choose an Azure subscription
  2. Create a Resource Group (RG)
  3. Deploy Bicep into the RG (this file)
  4. Use outputs (ACR login server, AKS name) for next steps

Why Bicep for interviews
It demonstrates repeatability, governance alignment, and production-grade provisioning patterns.

2) What is an Azure Resource Group?

A Resource Group (RG) is an Azure logical container that holds related resources (AKS, ACR, public IPs, VNets, Key Vaults, etc.) as a unit of lifecycle and governance.
  • Lifecycle boundary: deploy, update, or delete a related set together
  • RBAC boundary: assign roles (Contributor/Reader) to a group
  • Policy boundary: enforce tags, allowed SKUs/regions, encryption standards
  • Billing boundary: costs can be queried and segmented by RG
RG scope vs Subscription scope
Resource Groups live inside a subscription. Many organizations use multiple RGs per environment (dev/qa/prod) and per application domain.
Comparison with AWS and GCP

AWS doesn’t have a single direct equivalent to an RG. The closest operational grouping patterns are:
  • AWS Account (strong boundary) + Tags (grouping)
  • CloudFormation stack (lifecycle grouping for a set of resources)
  • Resource Groups feature in AWS (tag-based views; not the same as Azure RG)

GCP’s primary boundary is the Project: billing, IAM, quotas, and many resources are scoped to projects. Common grouping patterns:
  • GCP Project ≈ stronger than Azure RG in many ways
  • Folders and Organizations for hierarchy
  • Labels for grouping/cost allocation (like tags)

Simple mapping mental model
Azure RG ≈ “a governance + lifecycle container inside a subscription”. AWS often uses “account + tags + CloudFormation stack”. GCP often uses “project + labels”.

3) The Bicep file (full)

Scope: resourceGroup
targetScope = 'resourceGroup'

@description('AKS cluster name')
param aksName string = 'aks-spring-demo'

@description('ACR name (must be globally unique, 5-50 lowercase alphanumerics)')
param acrName string

@description('Azure region')
param location string = resourceGroup().location

@description('Kubernetes version (optional). Leave empty for default.')
param kubernetesVersion string = ''

@description('AKS node VM size')
param nodeVmSize string = 'Standard_B2s'

@minValue(1)
@maxValue(3)
@description('Initial node count (keep small)')
param nodeCount int = 1

@description('DNS prefix for AKS')
param dnsPrefix string = 'springdemo'

resource acr 'Microsoft.ContainerRegistry/registries@2023-07-01' = {
  name: acrName
  location: location
  sku: {
    name: 'Basic'
  }
  properties: {
    adminUserEnabled: false
  }
}

resource aks 'Microsoft.ContainerService/managedClusters@2024-01-01' = {
  name: aksName
  location: location
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    dnsPrefix: dnsPrefix
    kubernetesVersion: empty(kubernetesVersion) ? null : kubernetesVersion
    agentPoolProfiles: [
      {
        name: 'system'
        mode: 'System'
        count: nodeCount
        vmSize: nodeVmSize
        osType: 'Linux'
        type: 'VirtualMachineScaleSets'
        enableAutoScaling: false
      }
    ]
    networkProfile: {
      networkPlugin: 'azure'
      loadBalancerSku: 'standard'
    }
  }
}

// Give AKS kubelet identity pull access to ACR
resource acrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(acr.id, aks.id, 'acrpull')
  scope: acr
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') // AcrPull
    principalId: aks.properties.identityProfile.kubeletidentity.objectId
    principalType: 'ServicePrincipal'
  }
}

output acrLoginServer string = acr.properties.loginServer
output aksNameOut string = aks.name

4) Step-by-step: what happens during deployment

Step 0 — Scope & location
Because targetScope is resourceGroup, Azure evaluates resourceGroup().location and uses it as the default region unless you override location.

Result: your ACR and AKS are placed in the same region as the RG by default.
Step 1 — Parameters resolved
Parameters are bound from defaults or from the CLI --parameters input. The key required one is acrName (no default).

  • aksName: default aks-spring-demo
  • acrName: required, must be globally unique
  • nodeCount: constrained 1..3 by decorators
  • kubernetesVersion: optional; empty means platform default
Step 2 — ACR is created
The template creates an Azure Container Registry with Basic SKU and disables admin user.

  • Resource type: Microsoft.ContainerRegistry/registries
  • SKU: Basic (cost-effective for demos)
  • adminUserEnabled: false (prefer RBAC / identities)
Step 3 — AKS cluster is created
The template provisions an AKS managed cluster using a SystemAssigned identity and a single system node pool.

Identity
type: SystemAssigned means Azure creates a managed identity for the cluster resource. Separately, AKS also creates a kubelet identity used by nodes to pull images and talk to Azure.
Agent pool
  • mode: System → system pool for core Kubernetes components
  • count: nodeCount → defaults to 1 (keeps costs small)
  • vmSize: Standard_B2s → economical for demos
  • type: VirtualMachineScaleSets → standard AKS scaling mechanism
  • enableAutoScaling: false → fixed node count
Networking profile
networkPlugin: azure uses Azure CNI networking. loadBalancerSku: standard selects the recommended Azure Load Balancer tier.
Step 4 — RBAC: grant AKS pull access to ACR
This creates a role assignment at the ACR scope so AKS nodes can pull private images from your registry.

What it does
Assigns the built-in AcrPull role to the AKS kubelet identity, scoped to the registry (scope: acr).
How uniqueness is ensured
Role assignments require a unique name (GUID). This uses: guid(acr.id, aks.id, 'acrpull') to generate a deterministic ID based on the resource IDs.
Why this matters
Without this permission, your Pods often fail with image pull errors when referencing private images in ACR.
Step 5 — Outputs returned to the caller
Outputs are values printed in the deployment result so scripts can chain the next steps (e.g., tagging images, configuring manifests).

acrLoginServer
The ACR login server (e.g., myregistry.azurecr.io) used in image references and docker login flows.
aksNameOut
The AKS cluster name as deployed (useful for scripts running az aks get-credentials later).

5) The exact deployment sequence (RG + Bicep)

A) Create the Resource Group (outside the template)
az group create \
  --name rg-aks-spring-demo \
  --location eastus
This creates the container boundary. Now the Bicep deployment can target it.
B) Deploy the Bicep into that RG
# acrName must be globally unique
az deployment group create \
  --name aks-spring-deploy \
  --resource-group rg-aks-spring-demo \
  --template-file main.bicep \
  --parameters acrName=<uniqueacrname>
You can override other params too (node size/count, kubernetes version, dns prefix).
Outcome
After the deployment completes, your Resource Group contains: ACR, AKS, and a role assignment granting ACR pull access. The outputs give you the registry login server and cluster name for next steps.