Production-grade platform engineering handbook — Kubernetes, Terraform, Flux CD, GitHub Actions, AWS, and more.
67
84%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Passed
No known issues
For Azure platform work, start from:
Prefer clear separation of subscriptions by environment or workload boundary instead of a flat single-subscription model.
Tags in Azure drive cost allocation, ownership tracking, and policy compliance. The specific keys an organization uses are a local decision — what matters is that the baseline is consistent, enforced at scale, and covers the tag inheritance gap that Azure has by design.
The azurerm provider does not have a default_tags equivalent. The standard pattern is a local.common_tags map that every resource merges in:
locals {
common_tags = var.common_tags
}
variable "common_tags" {
description = "Baseline tags merged into every resource. Keys are defined by the organization."
type = map(string)
}Every resource uses merge so the baseline is additive — resource-level tags extend it rather than replace it:
resource "azurerm_kubernetes_cluster" "this" {
name = var.cluster_name
location = var.location
resource_group_name = azurerm_resource_group.this.name
tags = merge(local.common_tags, {
component = "aks-control-plane"
})
}
resource "azurerm_resource_group" "this" {
name = var.resource_group_name
location = var.location
tags = local.common_tags
}If a key exists in both local.common_tags and the inline map, the inline value wins. Use this only for intentional per-resource overrides.
This is the most common tagging mistake in Azure. A resource group tagged with your baseline keys does not pass those tags to the resources inside it. Each resource must be tagged independently.
Two ways to handle this:
Option 1 — Terraform merge (recommended for Terraform-managed resources):
Pass local.common_tags explicitly to every resource. This is explicit and reliable.
Option 2 — Azure Policy with modify effect (backstop for everything else):
Use a policy that automatically appends missing tags by copying them from the resource group:
{
"policyRule": {
"if": {
"field": "tags['your-required-key']",
"exists": "false"
},
"then": {
"effect": "modify",
"details": {
"roleDefinitionIds": [
"/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"
],
"operations": [
{
"operation": "addOrReplace",
"field": "tags['your-required-key']",
"value": "[resourceGroup().tags['your-required-key']]"
}
]
}
}
}
}Assign this policy at management group scope so it covers all subscriptions.
Use deny effect to block resource creation without required tags. Start with audit during rollout, switch to deny once the existing estate is clean:
{
"mode": "Indexed",
"policyRule": {
"if": {
"field": "tags['your-required-key']",
"exists": "false"
},
"then": {
"effect": "deny"
}
}
}Assign at the management group level for broadest coverage.
After enabling a modify policy, run a remediation task to tag already-existing non-compliant resources:
az policy remediation create \
--name "tag-remediation-$(date +%Y%m%d)" \
--policy-assignment <assignment-id> \
--resource-discovery-mode ExistingNonCompliantMonitor the task until it completes — large subscriptions may take hours.
AKS creates a managed resource group (MC_*) automatically. Tags set on the azurerm_kubernetes_cluster resource do not apply to this group unless you explicitly target it.
Use node_resource_group to control the name, then tag it via a separate resource or the node_resource_group_tags argument (requires azurerm >= 3.87.0):
resource "azurerm_kubernetes_cluster" "this" {
name = var.cluster_name
location = var.location
resource_group_name = azurerm_resource_group.this.name
node_resource_group = "mc-${var.cluster_name}-nodes"
tags = merge(local.common_tags, {
component = "aks-control-plane"
})
}Tag the mc-* resource group explicitly if your policy covers resource groups:
resource "azurerm_resource_group_tag" "mc_tags" {
for_each = local.common_tags
resource_group_name = azurerm_kubernetes_cluster.this.node_resource_group
tag_key = each.key
tag_value = each.value
depends_on = [azurerm_kubernetes_cluster.this]
}Tags appear in Azure Cost Management after being present for 24–48 hours.
Tags on resource groups and individual resources both appear in cost exports. If a resource is untagged but its resource group is tagged, the group-level tag does not automatically apply to the cost record for that resource.
merge(local.common_tags, {...}) in every resource and module. Never leave tags empty.terraform plan -out=plan.json using OPA, Conftest, or a custom script..claude-plugin
.github
commands
docs
examples
agent-self-improve
argocd
awesome-docs
aws
cloudfront
functions
lambda-edge
functions
azure
compliance
conventional-commits
datadog
llm-observability
demo
documentation
dora
dynatrace
fluxcd
github-actions
composite-actions
configure-cloud
db-migrate
docker-build-push
k8s-deploy
notify-slack
pr-comment
release-tag
security-scan
setup-env
setup-terraform
terraform-plan
helm
web-service
templates
kubernetes
kyverno
mcp
observability
openshift
pr-review
ownership
runtime-security
supply-chain
terraform
references
scripts
skills
platform-skills
tests