AKS Platform: Deployment¶
This page describes how the solution is deployed: the Terraform layout, the pipelines, the configuration files, the roles of the pipeline identity and the order of a first deployment. The pipelines are run by the platform team (XMMS in the managed model, your own team when self-managed, see Operating models).
Reference design, your deployment may differ
Pipeline names, inputs and environment names are those of the reference repository. Your platform may run the same Terraform from another pipeline.
Terraform layout¶
terraform/
environments/
azure/ # root for AKS in Azure
config/ # <environment>.tfvars and backend-<environment>.hcl
azure-local/ # root for AKS on Azure Local, with scripts/bootstrap-aks-arc.ps1
modules/
azure/ # composition per layer for Azure
azure-local/ # composition per layer for Azure Local
network/ identity/ secrets/ cluster-baseline/ security-policy/ # building blocks
pagerduty/ # on-call setup, separate root and state
| Item | Value |
|---|---|
| Terraform version | 1.5 or higher (~> 1.5 in the pipeline) |
| Providers | azurerm 4.x, azuread 3.x (the Azure root adds an aliased azurerm provider for the hub subscription) |
| State | Azure Storage, one backend file and state key per platform and environment |
| Working directory | terraform/environments/<platform>/ |
| Variables | -var-file=config/<environment>.tfvars |
Manual run, from the working directory:
terraform init -backend-config=config/backend-<environment>.hcl
terraform plan -var-file=config/<environment>.tfvars -out=tfplan
terraform apply tfplan
Pipelines¶
| Pipeline | Platform | Inputs |
|---|---|---|
| Terraform Platform Pipeline (GitHub Actions) | azure and azure-local |
platform, environment (nonprod, prod, nonprod-integrated, prod-integrated), action, target_module, vars_ref |
| Terraform platform pipeline (Azure DevOps) | azure and azure-local; the sources describe it as used for Azure Local |
platform, environment (nonprod, prod), action, target_module |
| Terraform PagerDuty pipeline (GitHub Actions) | On-call setup | environment, action |
| AKS non-production start and stop (GitHub Actions) | Cost saving | action (scheduled and manual) |
| Load test (GitHub Actions) | Non-production | Rates, durations, failure trigger (Load testing) |
Actions¶
| Action | Effect |
|---|---|
plan |
Terraform plan only |
apply |
Plan, then apply |
destroy-plan, destroy |
Plan the destruction, or destroy |
bootstrap |
Azure Local only: create the cluster and MetalLB |
bootstrap-and-apply |
Azure Local only: bootstrap, then plan and apply |
bootstrap-and-plan |
Azure Local only, Azure DevOps pipeline: bootstrap, then plan |
Target layers¶
target_module is all for normal operation. A single layer (networking, identity, security-policy, cluster-baseline,
secrets) uses Terraform -target and is meant for bootstrap or controlled recovery only. The pipeline warns when you use
it. The GitHub pipeline adds cicd-aks-rbac, which targets only the pipeline role assignments on the cluster.
What a run does¶
- Resolves the GitHub Environment from platform and environment. The
-integratedenvironments reuse the GitHub Environments of the standalone clusters; no new environments are created. - Reads the authentication values (
auth.conf) and, for Azure Local, the bootstrap values (bootstrap.conf) of that environment from a separate private variables repository, with a GitHub App token. - Logs in to Azure with GitHub OIDC (no client secret) and sets the Terraform
ARM_*variables. - Runs
terraform fmt -check,init,validate, then plan or apply for the chosen layer. - On Azure Local, runs the bootstrap first when the action asks for it and passes
cluster_idandoidc_issuer_urlto Terraform through a generatedconfig/ci.auto.tfvars.
One run per platform and environment at a time (a concurrency group). Production approvals are enforced with the GitHub Environment protection rules, or with approval gates on the Azure DevOps environment.
The Terraform state storage account of the hub-integrated environment is in the subscription of the standalone clusters, while the resources are created in the spoke subscription. The backend file therefore pins the state subscription.
Pipeline identity roles¶
The identity of the Azure pipeline needs, to deploy the full platform:
| Role | Scope | Purpose |
|---|---|---|
| Contributor | Subscription | Create and manage the resources |
| Resource Policy Contributor | Subscription | Create Azure Policy assignments |
| Role Based Access Control Administrator | Subscription, with a condition | Create role assignments for the managed identities |
| Storage Blob Data Contributor | State storage account | Read and write the state |
Graph permission Group.ReadWrite.All (admin consent) or the Entra role Groups Administrator |
Tenant | Create and manage Entra ID groups |
Restrict the Role Based Access Control Administrator role with the condition "allow user to only assign selected roles to selected principals", and allow only: Managed Identity Operator (cluster identity to kubelet identity), Network Contributor (cluster identity on the VNet and subnets), Private DNS Zone Contributor (cluster identity on DNS zones), AcrPull (kubelet identity on the registry).
More roles in the code
The code also assigns Key Vault Secrets User to workload identities and Azure Kubernetes Service RBAC Admin to pipeline identities. Add these roles to the condition if the pipeline identity creates those assignments. In the hub-integrated mode the identity also needs access in the spoke subscription (Networking).
First deployment of an environment¶
- Prepare the configuration file and the backend file for the environment, and the prerequisites (Prerequisites).
- Run
planwithtarget_module = alland review it. - Run
apply. The layers are applied in dependency order: networking and identity first, then security policy, cluster baseline and secrets. - Read the cluster's OIDC issuer URL, set
oidc_issuer_urlin the configuration, and apply again. Only then are the workload identities and their federated credentials created. - Set the Entra ID group memberships, then let GitOps apply namespaces, RBAC, policies and platform services.
For Azure Local, run the bootstrap first (AKS on Azure Local).
Known issue: Graph API¶
A Microsoft Graph API problem breaks terraform apply with target all when role assignments on the cluster change (a
provider issue in the azuread provider that was waiting for a fix). The workaround is to change the pipeline role
assignments through the cicd-aks-rbac target. The role assignments for the pipeline sit outside the identity module for that
reason.
On-call setup (PagerDuty)¶
A separate Terraform root terraform/pagerduty has its own state and pipeline. It creates, per environment:
| Resource | Setting |
|---|---|
| Team and memberships | Users given in the configuration, with a role |
| Schedule | Weekly rotation in the Amsterdam time zone, restricted to office days and hours |
| Escalation policy | One schedule, escalation delay 30 minutes, repeated twice |
| Service | Creates alerts and incidents, auto-resolve after 4 hours, acknowledgement timeout 30 minutes, urgency by severity in support hours and low outside them |
| Alert grouping | Intelligent grouping |
| Service dependency | The service supports a business service of the provider |
| Integration | Events API v2 integration for Prometheus Alertmanager |
The hours, escalation steps and who is on call are agreed in your contract or your internal on-call process. The values in the code are the defaults.