CtrlK
BlogDocsLog inGet started
Tessl Logo

nitinjain999/platform-skills

Production-grade platform engineering handbook — Kubernetes, Terraform, Flux CD, GitHub Actions, AWS, and more.

77

Quality

97%

Does it follow best practices?

Impact

No eval scenarios have been run

SecuritybySnyk

Advisory

Suggest reviewing before use

Overview
Quality
Evals
Security
Files

azure.mdcommands/

name:
azure
description:
Azure identity (Workload Identity, OIDC, Entra ID), resource tagging, AKS platform patterns, RBAC scoping, and production-readiness review — with Terraform generation.
argument-hint:
[identity|tagging|aks|rbac|review] [description or Terraform snippet]
title:
Azure Command
sidebar_label:
azure
custom_edit_url:
null

Azure Command

Structured guidance for Azure identity, resource governance, AKS platform patterns, and production-readiness review.

Activation

/platform-skills:azure identity   # Workload Identity, OIDC federation, managed identities, Entra ID
/platform-skills:azure tagging    # common_tags pattern, Azure Policy enforce/remediate, AKS MC_ group
/platform-skills:azure aks        # AKS provisioning, add-ons, Flux/Argo bootstrap, node pools
/platform-skills:azure rbac       # role assignment scoping, custom roles, audit over-permissioned identities
/platform-skills:azure review     # production-readiness checklist for an Azure environment

Interactive Wizard (fires when no mode is provided)

When invoked with no arguments, ask before proceeding:

Q1 — Mode?

What do you need?
  1. identity  — Workload Identity, OIDC federation for GitHub Actions, managed identities, Entra ID
  2. tagging   — common_tags baseline, Azure Policy enforcement, MC_ resource group, cost analysis
  3. aks       — AKS cluster provisioning, node pools, workload identity, Flux/Argo bootstrap
  4. rbac      — role assignment scoping, custom roles, audit over-permissioned identities
  5. review    — production-readiness checklist (tagging, RBAC, OIDC, protected environments)

Enter 1–5 or mode name:

Q2 — Context (after mode selected):

  • identity: What needs Azure access — a GitHub Actions workflow, an in-cluster workload, or a human team?
  • tagging: Paste your Terraform module or describe the resource types you need to tag.
  • aks: New cluster or modifying existing? What add-ons are needed (Flux, Argo CD, ESO, Linkerd)?
  • rbac: Describe the identity (user, group, managed identity) and what it needs to do.
  • review: Describe the environment — how many subscriptions, which workloads, any compliance requirements?

Mode: identity

Triggers: Workload Identity, OIDC, managed identity, service principal, federated credential, GitHub Actions Azure login, Entra ID

Read references/azure.md before responding.

GitHub Actions OIDC federation (no long-lived secrets)

# Create a user-assigned managed identity
az identity create \
  --name github-actions-deploy \
  --resource-group platform-rg \
  --location northeurope

# Get the identity's client ID
CLIENT_ID=$(az identity show \
  --name github-actions-deploy \
  --resource-group platform-rg \
  --query clientId -o tsv)

# Add a federated credential — scoped to a specific repo and branch
az identity federated-credential create \
  --name github-main-branch \
  --identity-name github-actions-deploy \
  --resource-group platform-rg \
  --issuer https://token.actions.githubusercontent.com \
  --subject repo:org/repo:ref:refs/heads/main \
  --audience api://AzureADTokenExchange

GitHub Actions workflow:

permissions:
  id-token: write
  contents: read

steps:
  - uses: azure/login@a65d910e8af852a8061c627c456678983e180302  # v2.2.0
    with:
      client-id: ${{ secrets.AZURE_CLIENT_ID }}
      tenant-id: ${{ secrets.AZURE_TENANT_ID }}
      subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

AKS Workload Identity (in-cluster pods)

# Terraform — user-assigned managed identity for a workload
resource "azurerm_user_assigned_identity" "app" {
  name                = "app-workload-identity"
  resource_group_name = azurerm_resource_group.this.name
  location            = var.location
}

# Federated credential — links the Kubernetes service account to the managed identity
resource "azurerm_federated_identity_credential" "app" {
  name                = "app-k8s-sa"
  resource_group_name = azurerm_resource_group.this.name
  parent_id           = azurerm_user_assigned_identity.app.id
  audience            = ["api://AzureADTokenExchange"]
  issuer              = azurerm_kubernetes_cluster.this.oidc_issuer_url
  subject             = "system:serviceaccount:app-team:app-sa"
}

Kubernetes service account with workload identity annotation:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-sa
  namespace: app-team
  annotations:
    azure.workload.identity/client-id: "<managed-identity-client-id>"

Pod label to opt in:

spec:
  template:
    metadata:
      labels:
        azure.workload.identity/use: "true"

Common identity mistakes

MistakeFix
Using service principal secrets for GitHub ActionsReplace with OIDC federated credential
Forgetting azure.workload.identity/use: "true" label on podsAdd the label to the pod template spec
subject in federated credential does not match the SA namespace/namesystem:serviceaccount:<namespace>:<sa-name> — must match exactly
Identity scoped at subscription level for app workloadsScope role assignment to the resource group or specific resource

Mode: tagging

Triggers: tags, tagging, common_tags, Azure Policy, MC_, cost allocation, chargeback, compliance tags

Read references/azure.md → Tagging resources section before responding.

Shared locals pattern (Terraform)

# variables.tf
variable "common_tags" {
  description = "Baseline tags merged into every resource. Keys are defined by the organization."
  type        = map(string)
}

# locals.tf
locals {
  common_tags = var.common_tags
}

# Every resource uses merge — resource-level tags extend the baseline
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"
  })
}

AKS managed resource group tagging

resource "azurerm_kubernetes_cluster" "this" {
  node_resource_group = "mc-${var.cluster_name}-nodes"

  tags = merge(local.common_tags, {
    component = "aks-control-plane"
  })
}

# Tag the MC_ group explicitly — AKS does not inherit tags automatically
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]
}

Azure Policy — enforce required tags

# Start with audit during rollout, then switch to deny once estate is clean
az policy assignment create \
  --name "require-team-tag" \
  --display-name "Require team tag on all resources" \
  --policy "/providers/Microsoft.Authorization/policyDefinitions/<built-in-id>" \
  --scope "/subscriptions/<subscription-id>" \
  --enforcement-mode DoNotEnforce   # change to Default when ready to enforce

# Remediate existing non-compliant resources
az policy remediation create \
  --name "tag-remediation-$(date +%Y%m%d)" \
  --policy-assignment <assignment-id> \
  --resource-discovery-mode ExistingNonCompliant

Validation:

# Check compliance state
az policy state summarize --subscription <subscription-id> \
  --filter "policyDefinitionName eq '<policy-id>'"

Mode: aks

Triggers: AKS, cluster, node pool, provision, bootstrap, Flux on AKS, Argo on AKS, add-ons

Read references/azure.md before responding.

Terraform AKS baseline

resource "azurerm_kubernetes_cluster" "this" {
  name                = var.cluster_name
  location            = var.location
  resource_group_name = azurerm_resource_group.this.name
  dns_prefix          = var.cluster_name
  kubernetes_version  = var.kubernetes_version
  node_resource_group = "mc-${var.cluster_name}-nodes"

  # System node pool
  default_node_pool {
    name                = "system"
    vm_size             = "Standard_D4s_v5"
    node_count          = 3
    os_disk_size_gb     = 128
    type                = "VirtualMachineScaleSets"
    zones               = ["1", "2", "3"]
    only_critical_addons_enabled = true   # system pool: control-plane add-ons only
  }

  # Workload identity + OIDC issuer (required for managed identity federation)
  workload_identity_enabled = true
  oidc_issuer_enabled       = true

  # Managed identity for the cluster itself
  identity {
    type = "SystemAssigned"
  }

  # Azure CNI Overlay — scales further than kubenet, simpler than full CNI
  network_profile {
    network_plugin      = "azure"
    network_plugin_mode = "overlay"
    network_policy      = "calico"
    service_cidr        = "10.96.0.0/16"
    dns_service_ip      = "10.96.0.10"
  }

  tags = merge(local.common_tags, {
    component = "aks-control-plane"
  })
}

# User node pool for workloads
resource "azurerm_kubernetes_cluster_node_pool" "workload" {
  name                  = "workload"
  kubernetes_cluster_id = azurerm_kubernetes_cluster.this.id
  vm_size               = "Standard_D8s_v5"
  node_count            = 3
  zones                 = ["1", "2", "3"]
  mode                  = "User"

  tags = merge(local.common_tags, {
    component = "aks-workload-nodes"
  })
}

Post-provision bootstrap sequence

# Get credentials
az aks get-credentials \
  --resource-group <rg> \
  --name <cluster> \
  --overwrite-existing

# Verify nodes are ready
kubectl get nodes

# Bootstrap Flux (if using GitOps)
flux bootstrap github \
  --owner=<org> \
  --repository=<gitops-repo> \
  --branch=main \
  --path=clusters/<cluster-name>

For Flux patterns → /platform-skills:gitops For Argo CD → /platform-skills:gitops

Node pool strategy

PoolPurposeNode type
systemkube-system, CoreDNS, metrics-serverSmall, stable, zonal
workloadApplication podsRight-sized for workload type
spotBatch jobs, non-criticalSpot VMs for cost reduction

Taint system pool to prevent workload scheduling:

node_taints = ["CriticalAddonsOnly=true:NoSchedule"]

Mode: rbac

Triggers: role assignment, contributor, owner, custom role, over-permissioned, least privilege, audit RBAC

Read references/azure.md → RBAC and identity patterns before responding.

Role assignment scoping rules

Identity typeAssignment scope
Platform teamManagement group or subscription
Workload teamResource group
Automation / managed identitySpecific resource or resource group
NeverOwner or Contributor to a service principal
# Assign a built-in role scoped to a resource group
az role assignment create \
  --assignee <managed-identity-client-id-or-user-upn> \
  --role "Contributor" \
  --scope "/subscriptions/<sub>/resourceGroups/<rg>"

# Scoped to a specific resource
az role assignment create \
  --assignee <managed-identity-client-id> \
  --role "Key Vault Secrets User" \
  --scope "/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.KeyVault/vaults/<vault-name>"

Audit over-permissioned identities

# Find all Owner and Contributor assignments on a subscription
az role assignment list \
  --subscription <subscription-id> \
  --query "[?roleDefinitionName=='Owner' || roleDefinitionName=='Contributor']" \
  -o table

# Find service principals with Owner
az role assignment list \
  --subscription <subscription-id> \
  --query "[?principalType=='ServicePrincipal' && roleDefinitionName=='Owner']" \
  -o table

Custom role — when built-ins are too permissive

{
  "Name": "AKS Metrics Reader",
  "Description": "Can read AKS metrics and node pools. Cannot modify.",
  "Actions": [
    "Microsoft.ContainerService/managedClusters/read",
    "Microsoft.ContainerService/managedClusters/agentPools/read",
    "Microsoft.Insights/metrics/read"
  ],
  "NotActions": [],
  "AssignableScopes": ["/subscriptions/<sub>"]
}
az role definition create --role-definition custom-role.json

Mode: review

Triggers: review, production ready, production checklist, audit my Azure config

Structured production-readiness review. Ask for:

  1. Subscription and resource group structure
  2. AKS cluster config (or Terraform)
  3. Identity model (managed identities, OIDC, service principals in use)
  4. Tag baseline enforcement approach

Then evaluate against:

CRITICAL (must fix before production)

❌ Long-lived service principal secrets for automation — replace with managed identity + OIDC
❌ Owner or Contributor assigned to a service principal — violates least privilege
❌ No resource tagging — blocks cost allocation, ownership tracking, and policy compliance
❌ AKS without workload_identity_enabled = true — forces static credentials in pods
❌ No protected environments in GitHub Actions for production subscription applies

WARNING (should fix)

⚠️  Tags missing from MC_ resource group — AKS does not inherit automatically
⚠️  Single subscription for all environments — mix prod and non-prod increases blast radius
⚠️  No Azure Policy enforce tagging — relies on developer discipline
⚠️  ClusterRoleBinding to workload identity — scope to RoleBinding unless cross-namespace access is required
⚠️  System and workload nodes in same pool — system pool should have CriticalAddonsOnly taint

INFORMATIONAL

ℹ️  Cost Management tags not validated in CI — add OPA/Conftest gate on terraform plan JSON
ℹ️  No Management Group hierarchy — direct subscription assignment makes policy rollout harder
ℹ️  AKS node pool not zone-redundant — add zones: ["1","2","3"] for HA

Handoffs:

  • Terraform generation → /platform-skills:terraform
  • In-cluster secrets strategy → /platform-skills:secrets
  • GitOps bootstrap on AKS → /platform-skills:gitops

Common mistakes

  • Tags not in every resourceazurerm provider has no default_tags; use merge(local.common_tags, {...}) in every resource and module. The merge pattern is mandatory.
  • Tags on resource group do not propagate — Azure does not inherit tags from resource groups. Each resource must be tagged independently.
  • AKS MC_ group untaggedazurerm_resource_group_tag or node_resource_group_tags must be added explicitly.
  • Service principal secrets for GitHub Actions — OIDC federated credentials eliminate the need; rotate secrets before migrating.
  • azurerm version below 3.87 for node_resource_group_tags — check provider version in versions.tf.

Reference

Full guidance: references/azure.md

For Terraform module generation: /platform-skills:terraform

For in-cluster secrets (Azure Key Vault via ESO): /platform-skills:secrets

Examples:

  • examples/azure/ — AKS cluster, workload identity, resource group baseline

BEFORE_AFTER.md

CHANGELOG.md

CODE_OF_CONDUCT.md

COMMANDS.md

CONTRIBUTING.md

EDITOR_INTEGRATIONS.md

GETTING_STARTED.md

HOW_IT_WORKS.md

install.sh

INSTALLATION.md

LAUNCH.md

PROMPTS.md

QUICKSTART.md

README.md

renovate.json

SECURITY.md

SKILL.md

tessl.json

tile.json