AKS Platform: Cluster Baseline¶
The cluster baseline is the standard configuration of every AKS cluster of the solution. It is the Terraform layer
cluster-baseline (modules aks-cluster, node-pools, cluster-identities, diagnostic-settings). This page lists the
settings that matter and the values of the reference configuration.
Reference design, your deployment may differ
The values below are read from the Terraform modules and the nonprod and prod configuration files of the Azure
variant. The design document describes a few other values; the differences are called out where they matter.
Fixed in the module¶
These settings are set in the module and cannot be switched off by a variable.
| Setting | Value | Why |
|---|---|---|
| Entra ID integration | On, with Azure RBAC for Kubernetes authorisation | Humans sign in with Entra ID. |
| Local accounts | Disabled | No shared admin credential (local_account_disabled = true). |
| API server access | VNet integration in a dedicated subnet | The API server has a private address in the spoke, no public endpoint. |
| OIDC issuer and workload identity | Enabled | Pods get Azure access without stored secrets (Identity and access). |
| Azure Policy add-on | Enabled | Azure Policy for Kubernetes (Security policy). |
| System node pool | Name system, autoscaling, managed OS disk, only critical add-ons, label node-pool=system |
Keeps application pods off the system nodes. |
| Cluster identity | One user-assigned managed identity, used for both the control plane and the kubelet | See Cluster identities. |
| Network plugin | Azure CNI in overlay mode with the Calico network policy engine | See Networking. |
Environment values¶
| Setting | Production | Non-production |
|---|---|---|
| Cluster name | aks-prod |
aks-nonprod |
| Kubernetes version | Minor version pinned in the configuration (1.34 in the reference files) | Same |
| SKU tier | Standard (SLA-backed) |
Free |
| Upgrade channel | none (manual) |
patch |
| System pool VM size | Standard_D4s_v5 |
Standard_D2s_v5 |
| System pool nodes (min to max) | 3 to 5 | 1 to 2 |
| System pool zones | 1, 2, 3 | 1 |
| System pool max pods per node | 30 | 100 |
User pool general VM size |
Standard_D4s_v5 |
Standard_D4s_v5 |
User pool general nodes (min to max) |
3 to 20 | 1 to 5 |
User pool general zones |
1, 2, 3 | 1 |
User pool general max pods per node |
30 | 100 |
| User pool label | workload-type=general |
workload-type=general |
| OS disk | 128 GB, managed | 128 GB, managed |
| Surge during upgrades (user pool) | 33% | 33% |
| Resource group | rg-prod-aks (nodes in rg-prod-aks-nodes) |
rg-nonprod-aks (nodes in rg-nonprod-aks-nodes) |
Design document versus code
The design document describes non-production as 2 to 3 system nodes and 2 to 10 user nodes over zones 1 and 2, with
30 pods per node. The nonprod configuration file sets the smaller values shown above, to save cost. The hub-integrated
non-production file uses the same sizes as nonprod.
User node pools are a map in the configuration. Adding a pool (for example a dedicated ingress, platform, spot or GPU pool) is a configuration change: name, VM size, min and max count, zones, disk size, max pods, labels, taints and surge. The pool module enables autoscaling on every pool.
Upgrades and maintenance windows¶
| Production | Non-production | |
|---|---|---|
| Kubernetes version upgrades | Manual, in the window, after non-production has been validated | patch channel applies patch versions automatically; minor upgrades stay manual |
| Auto-upgrade window | Saturday 03:00, 4 hours, UTC+1 | Tuesday 20:00, 4 hours, UTC+1 |
| Node OS window | Saturday 03:00, 4 hours, UTC+1 | Tuesday 20:00, 4 hours, UTC+1 |
Both windows are weekly. The offset is fixed, so the window moves by an hour against local time when daylight saving time starts or ends. Change the offset in the configuration if that matters.
The design document gives the node image window for production as Sunday and for non-production as Wednesday. The configuration files set it to the same day as the Kubernetes upgrade window.
Rules from the design:
- non-production is upgraded first and production follows after a validation period, so that both clusters run the same minor version at steady state;
- a minor version cannot be rolled back on AKS. The remedy after a bad upgrade is a forward fix or a workload-level rollback;
- check for removed Kubernetes APIs (the design names
kubentandpluto) before a minor upgrade; - Terraform tracks the version, so a manual version change outside Terraform shows up as drift.
Add-ons and options¶
| Option | Reference configuration | Notes |
|---|---|---|
| Azure Policy add-on | On | Fixed in the module. |
| Key Vault Secrets Provider (CSI driver) | On in nonprod, prod and nonprod-integrated, with secret rotation every 2 minutes |
The design document says the CSI driver is not used and that applications read Key Vault with the SDK. The code switch is on in the reference files. |
| Azure Monitor (Container Insights) | Off | Needs a Log Analytics workspace ID. |
| Image cleaner | Off | When on, runs every 168 hours (7 days). |
| Managed Prometheus | Not in the code | The design says to evaluate it later. |
| AKS diagnostic settings | Off (deploy_aks_diagnostics = false) |
Production file lists the categories to enable before go-live: kube-apiserver, kube-audit-admin, kube-controller-manager, guard, cloud-controller-manager. Needs a Log Analytics workspace. |
| Defender for Containers | Off | See Security policy. |
The design says diagnostic settings are mandatory before production go-live, with retention of 90 days (production) and 30 days (non-production) in Log Analytics. Retention is a property of the workspace, not set by this solution.
Cluster identities¶
The identity layer creates one user-assigned managed identity per cluster (mi-kubelet-<env>). The cluster baseline uses it
as the control plane identity and as the kubelet identity. The cluster-identities module gives it these Azure roles:
| Role | Scope | Purpose |
|---|---|---|
| Network Contributor | Node subnet | Join nodes to the subnet |
| Network Contributor | API server subnet | API server VNet integration |
| Private DNS Zone Contributor | Each private DNS zone in aks_private_dns_zone_ids |
Manage private DNS records (none set in the reference files) |
| Managed Identity Operator | The identity itself | Assign the kubelet identity to the node scale sets |
| AcrPull | Each registry in aks_acr_ids |
Pull images without pull secrets (none set in the production and nonprod files) |
The environment root also assigns Network Contributor on the spoke VNet to the same identity.
Design document versus code
The IAM design document describes a system-assigned control plane identity and a separate user-assigned kubelet identity. The code uses one user-assigned identity for both. Treat the code as authoritative.
Bootstrap for GitOps¶
The cluster baseline gives GitOps a reachable, authenticated cluster with workload identity and a network policy engine. It does not deploy namespaces, RBAC or platform services. The design lists these as GitOps bootstrap resources:
| Resource | Purpose |
|---|---|
Namespaces platform-system, gateway-system, monitoring, policy-system |
Homes of platform components |
Priority class platform-critical (value 1000000) |
Core platform pods that must not be evicted |
Priority class platform-high (value 900000) |
GitOps controller, ingress gateway, certificate management |
Priority class workload-default (value 100000) |
Default for application pods, preempted first |
| Cluster role bindings for the platform groups | Platform admin, operator, auditor, break-glass |
Recovery model¶
The design treats a cluster as disposable and rebuildable: a destroyed cluster is rebuilt with Terraform (the design expects 30 to 60 minutes) and GitOps then restores platform services, RBAC, policies and applications. Persistent volume data is not covered. Plan Azure Backup for AKS or Velero per workload.
The design also names resource locks (CanNotDelete on the production resource group, and on the state storage account) and
state storage with versioning and soft delete. The Terraform code of the solution does not create locks. Confirm with the
platform team how these are set in your environment.