4. Getting Started
The smallest thing that proves a cluster works: create it, deploy one container, reach it from the internet, delete everything. Three ways, because interviews and real jobs use all three.
This page is deliberately throwaway. Hard-coded names, a public API server, one node pool, no variables, no state backend, no pipeline. It exists to build intuition and be deleted twenty minutes later. Everything production-shaped — modules, remote state, private clusters, OIDC pipelines, rollback — lives in Deployment. If a snippet here would embarrass you in a pull request, that's the point.
Before you start
az login
az account set --subscription "<your-subscription>"
az account show -o table
# kubectl and the Entra credential plugin, installed into your PATH
az aks install-cli
Two things to check first, because both bite beginners:
- vCPU quota. A cluster needs enough of the VM family you pick in that region.
az vm list-usage -l uksouth -o tableand look for the family you're about to use — aQuotaExceededfailure halfway through cluster creation is the most common first experience of AKS. - Region. Pick one close to you that has availability zones. Everything below uses
uksouth; substitute freely.
The task
One AKS cluster with a single small node pool → deploy an NGINX pod → expose it with a
LoadBalancer Service → hit it in a browser → delete the resource group.
1. Azure Portal
Short click-path; the portal's navigation changes often, so this describes destinations rather than exact button labels.
- Create a resource → Kubernetes service, then choose the cluster preset — pick Dev/Test for this exercise so it doesn't provision a production-sized cluster.
- On Basics, create a new resource group
rg-aks-demo, name the clusteraks-demo, choose your region, leave the Kubernetes version at default, and set the pricing tier to Free (it's a demo; in production this is Standard). - On Node pools, reduce the default system pool to 1 node of a small burstable or general size, and leave autoscaling off.
- Skip Networking, Integrations, and Monitoring — the defaults are fine for a throwaway, and Container Insights on a demo cluster generates a small bill for logs nobody reads.
- Review + create. Provisioning takes several minutes; the portal's Connect button on the
cluster blade gives you the
az aks get-credentialsline for the next step.
Note what the portal just did that it didn't tell you: it created a second resource group named
MC_rg-aks-demo_aks-demo_uksouth holding the scale set, the disks, and the load balancer.
2. Azure CLI
The whole exercise, copy-pasteable.
# --- Create ---------------------------------------------------------------
az group create -n rg-aks-demo -l uksouth
az aks create \
-g rg-aks-demo \
-n aks-demo \
--tier free \
--node-count 1 \
--node-vm-size Standard_B2s \
--network-plugin azure \
--network-plugin-mode overlay \
--enable-managed-identity \
--generate-ssh-keys
# --- Connect --------------------------------------------------------------
az aks get-credentials -g rg-aks-demo -n aks-demo
kubectl get nodes -o wide
# --- Deploy ---------------------------------------------------------------
kubectl create deployment hello --image=mcr.microsoft.com/cbl-mariner/base/nginx:1.22
kubectl expose deployment hello --type=LoadBalancer --port=80 --target-port=80
# --- Watch Azure provision a load balancer on your behalf -----------------
kubectl get service hello --watch # EXTERNAL-IP moves from <pending> to an IP
When EXTERNAL-IP appears, curl it. That IP is an Azure public IP resource that was created in
the node resource group by the cloud controller manager because you asked Kubernetes for a
LoadBalancer Service. Look at it:
NODE_RG=$(az aks show -g rg-aks-demo -n aks-demo --query nodeResourceGroup -o tsv)
az resource list -g "$NODE_RG" -o table
That listing — a scale set, a disk, a load balancer, a public IP, an NSG — is the single most useful five seconds in this page. Your Kubernetes objects create Azure resources.
A few commands worth running while it's alive:
kubectl get pods -o wide # which node did it land on?
kubectl describe pod -l app=hello # events are where failures explain themselves
kubectl logs -l app=hello
kubectl get events --sort-by=.lastTimestamp # the cluster's own narration
az aks show -g rg-aks-demo -n aks-demo --query "{version:kubernetesVersion, tier:sku.tier, rg:nodeResourceGroup}" -o yaml
PowerShell equivalent
Included here because AKS clusters are commonly created from Windows admin workstations and by Azure-DevOps-centric shops:
New-AzResourceGroup -Name rg-aks-demo -Location uksouth
New-AzAksCluster -ResourceGroupName rg-aks-demo -Name aks-demo -NodeCount 1 -NodeVmSize Standard_B2s
Import-AzAksCredential -ResourceGroupName rg-aks-demo -Name aks-demo
3. Terraform — minimal
Hello-world only: one file, hard-coded, no variables, no backend. The parameterised module is in Deployment.
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
}
}
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "demo" {
name = "rg-aks-demo"
location = "uksouth"
}
resource "azurerm_kubernetes_cluster" "demo" {
name = "aks-demo"
location = azurerm_resource_group.demo.location
resource_group_name = azurerm_resource_group.demo.name
dns_prefix = "aksdemo"
sku_tier = "Free"
default_node_pool {
name = "system" # 1-12 lowercase alphanumeric characters, must start with a letter
node_count = 1
vm_size = "Standard_B2s"
}
identity {
type = "SystemAssigned"
}
network_profile {
network_plugin = "azure"
network_plugin_mode = "overlay"
}
}
output "kube_config_command" {
value = "az aks get-credentials -g ${azurerm_resource_group.demo.name} -n ${azurerm_kubernetes_cluster.demo.name}"
}
terraform init
terraform plan
terraform apply
Two things to notice in the plan output, both of which matter later:
- The node resource group is not in your Terraform state. Terraform created one resource; Azure created a dozen. Terraform will never manage those, and that asymmetry is the root of several deployment-time surprises.
- The
default_node_poolblock is inside the cluster resource. Additional pools are separateazurerm_kubernetes_cluster_node_poolresources — the first pool is special, and changing itsvm_sizeis the change that historically forced the whole cluster to be replaced.
[Image Prompt: 2D minimalistic diagram comparing three provisioning paths — Azure Portal, az CLI, and Terraform — all converging on the same AKS cluster resource which in turn creates a node resource group, flat design, clean vector art style, white background]
Teardown
Do this now, not later. An idle single-node demo cluster still bills for the node VM, its disk, the load balancer, and the public IP.
# Terraform path
terraform destroy
# CLI path — one command removes the cluster AND its node resource group
az group delete -n rg-aks-demo --yes --no-wait
Deleting the resource group is the cleanest teardown Azure offers and it's a genuine advantage over AWS: because every resource lives in exactly one resource group, one delete removes the cluster, the node resource group, the scale set, the disks, the load balancer, and the public IP. Two things it will not remove: anything you created in a different resource group (an ACR, a Key Vault), and anything protected by a resource lock or soft delete.
Verify:
az group list --query "[?starts_with(name,'rg-aks-demo') || starts_with(name,'MC_rg-aks-demo')].name" -o tsv
Empty output means you're done.
What you should be able to do now
Create a cluster three ways, get credentials, deploy a container, expose it, and — most importantly — point at the node resource group and explain that the Kubernetes objects you typed became Azure resources you're being billed for.
Next: Deployment →
← Back to the Azure Kubernetes Service overview · ← Previous: Architecture