AKS Hardening: Azure Policy, Gatekeeper, node images and the AKS-specific settings
For: Azure Kubernetes Service (AKS) clusters that enforce policy with Gatekeeper (through the Azure Policy add-on or Deployment Safeguards) Read after: Container and Kubernetes Hardening (the generic guide)
Facts checked 2026-09-29 against Microsoft Learn pages (dated mid-2026) and the upstream Kubernetes docs. AKS changes quickly and several features are in preview: always check the AKS release notes (github.com/Azure/AKS/releases) and az <command> --help for your CLI version. Flag names below come from Microsoft's documentation where marked "documented"; the others are standard az aks options that you should confirm with --help.
Only run commands on clusters you are authorised to administer. Try policy changes in a test cluster or in audit/warn mode first.
1. HOW THE PIECES FIT (and what Gatekeeper is and is not)
Azure layer Microsoft Entra ID (who you are), Azure RBAC (what you may do), Azure Policy (rules for Azure and, through an add-on, for Kubernetes)
AKS control plane Managed by Microsoft: API server, etcd, scheduler. You do not patch it, and you cannot edit its flags.
Gatekeeper An admission controller that runs as PODS in your cluster. It checks (and can mutate) Kubernetes API requests such as "create this pod". On AKS it is normally installed by the Azure Policy add-on or by Deployment Safeguards.
Node images The VMs (nodes) run a Microsoft-built, hardened image: Ubuntu, Azure Linux, Azure Container Linux (ACL) or Windows. It is patched by upgrading the node image, not by Gatekeeper.
Your pods What you deploy, and what the rest of this sheet is trying to keep safe.
The key point: Gatekeeper and the node image are DIFFERENT layers. Gatekeeper decides what may be created through the Kubernetes API. It does not configure or harden the node operating system, and it does not stop someone with shell access to a node or a kernel exploit. The node image hardening is AKS's job (and your job to keep upgraded). Both are needed.
See what you have:
kubectl get pods -n gatekeeper-system # Gatekeeper controller and audit pods
kubectl get pods -n kube-system | grep azure-policy # the Azure Policy add-on pod that feeds Gatekeeper
kubectl get constrainttemplates # templates starting with k8sazure came from the Azure Policy add-on
kubectl get constraints # the individual rules; add-on rules start with azurepolicy-
kubectl get nodes -o wide # node OS image and kernel version
az aks show -g <resource-group> -n <cluster> --query "addonProfiles.azurepolicy"
If the templates do NOT start with k8sazure, someone installed Gatekeeper themselves: then policy is managed with your own YAML and Git, not through Azure Policy. Ask your platform team which model you have before changing anything.
2. THE AZURE POLICY ADD-ON AND GATEKEEPER (documented behaviour)
- Enable it (Linux node pools only): az aks enable-addons --addons azure-policy --name <cluster> --resource-group <resource-group>
- It installs an azure-policy pod in kube-system and the Gatekeeper pods in gatekeeper-system.
- Policy flow: you assign an Azure Policy definition or initiative to the cluster or resource group. The add-on checks for changes every 15 minutes, then creates, updates or deletes the matching constraint templates and constraints in the cluster. It also runs a full scan of the cluster every 15 minutes and reports back. Compliance reports show violations from the last 45 minutes.
So a new policy is NOT instant: wait for the sync before you conclude it does not work.
- Effects: audit (report only), deny (block at admission), disabled, and mutation policies that change resources on the way in.
- EXCLUDED NAMESPACES: kube-system and gatekeeper-system are automatically excluded from evaluation. Microsoft recommends also excluding azure-arc, and you can set your own list in the policy assignment's "Namespace exclusions" parameter.
- Limits: Linux node pools only; up to 10,000 pods per cluster; a maximum number of non-compliant records reported per policy.
- FAIL-OPEN: Gatekeeper is documented to run in a fail-open model. If the Gatekeeper webhook cannot answer, the request is ALLOWED, so the deny does not block your deployments. That is a sensible availability choice, and it means policy is a guardrail, not your only line of defence. Keep the audit results, and monitor that the gatekeeper pods are healthy.
- A denied request looks like: admission webhook "validation.gatekeeper.sh" denied the request: [azurepolicy-<template>-<id>] <message>
- Troubleshoot:
kubectl logs -n kube-system <azure-policy-pod-name> # policy ingestion and compliance reporting
kubectl logs -n gatekeeper-system <gatekeeper-pod-name> # admission decisions
kubectl get constrainttemplates <name> -o yaml # annotations show the Azure policy definition it came from
- Roll out policy in two steps: assign with effect audit, review the compliance results (Azure Policy dashboard or kubectl get constraints), fix or exempt what is legitimate, THEN switch to deny.
3. WHY THE DEBUG POD WORKS IN kube-system (and what to protect because of it)
The "Logging In to Kubernetes Nodes with kubectl debug (and what to do when Gatekeeper blocks it)" sheet on the Cloud Tutorials tab uses -n kube-system when a policy refuses the privileged debug pod. It works because kube-system is excluded from Azure Policy evaluation (section 2). That is by design, so AKS's own system pods can run. It also means:
- Anyone who can create pods in kube-system can start a privileged pod that Gatekeeper never checks. Treat "create pods in kube-system" like "root on the nodes".
- Scope Azure RBAC for Kubernetes narrowly: give the AKS RBAC roles (Reader, Writer, Admin, Cluster Admin) at namespace scope, and keep kube-system write access to a small platform group. A cluster-wide Writer or Admin role includes kube-system.
- Log it and alert on it: pods created in kube-system by humans, pods named node-debugger-*, and any privileged pod creation. Send the API audit logs (kube-audit-admin and kube-audit categories) to Log Analytics.
- An alternative some teams use: a dedicated "platform-debug" namespace that is deliberately excluded from Gatekeeper (through the policy's Namespace exclusions), allowed to run privileged pods, restricted by RBAC to a few named admins, and cleaned up automatically. That keeps ordinary debugging away from the namespace AKS itself relies on. Agree the design with your security team, and remember any namespace-level Pod Security setting must also allow privileged pods there.
- Always delete the debug pod when finished (the sheet shows how).
- Nodes accept SSH by default on their INTERNAL IPs only. Turning SSH off per node pool is available in preview (see Microsoft's "Manage SSH access" article).
4. DEPLOYMENT SAFEGUARDS AND POD SECURITY STANDARDS ON AKS
Deployment Safeguards is an AKS feature that enforces a fixed set of best-practice rules through Azure Policy and Gatekeeper. It needs the Azure Policy add-on (and the Microsoft.PolicyInsights resource provider registered).
Levels: Warn (messages, request still goes through) Enforce (denies AND mutates non-compliant deployments)
Documented commands:
az aks safeguards create --resource-group <rg> --name <cluster> --level Warn # start here
az aks safeguards update --resource-group <rg> --name <cluster> --level Enforce
az aks safeguards update --resource-group <rg> --name <cluster> --level Enforce --excluded-ns ns1 ns2
az aks safeguards update --resource-group <rg> --name <cluster> --level Warn --pss-level Baseline # Baseline, Restricted or Privileged
az aks safeguards delete --resource-group <rg> --name <cluster>
The rules it turns on (all or nothing: you cannot pick individual ones): cannot edit individual nodes, CPU and memory requests must be defined, anti-affinity or topology spread for multi-replica deployments, no AKS-specific labels, only allowed images, reserved system pool taints, readiness/liveness probes, CSI driver StorageClass, unique service selectors, and no :latest image tag.
Things that surprise people:
- At Enforce, some rules MUTATE: containers with no resources get defaults of 500m CPU and 2048Mi memory (request and limit), values under 100m CPU or 100Mi memory are raised, and a preferred anti-affinity plus topology spread is added to workloads that have none. Check that your workloads still fit.
- On AKS Standard the default Pod Security Standard level is PRIVILEGED, which enforces nothing. Baseline or Restricted only apply when you set --pss-level. AKS Automatic clusters start at Baseline with safeguards on Enforce, and you can exclude namespaces but not lower the cluster-wide level.
- It is Gatekeeper underneath, so it is fail-open (section 2) and kube-system is excluded.
Two ways to enforce Pod Security Standards exist on AKS: Deployment Safeguards' --pss-level (Azure Policy and Gatekeeper) and the upstream Pod Security Admission namespace labels (see the generic sheet). Pick one as the documented standard, and if you use both, learn to tell their messages apart: Gatekeeper's start with admission webhook "validation.gatekeeper.sh".
The Restricted rules Deployment Safeguards checks are the same ones as upstream: run as non-root, no privilege escalation, drop ALL capabilities (only NET_BIND_SERVICE may be added), seccomp RuntimeDefault or Localhost, and the limited list of volume types.
For your own rules, Azure Policy has built-in definitions for AKS (categories such as pod security baseline and restricted standards for Linux workloads, allowed images, no privileged containers, no host networking, and more): browse them in Azure Policy under Definitions, category Kubernetes. Custom Gatekeeper constraint templates can be delivered as custom Azure Policy definitions.
5. POD SETTINGS THAT ARE SPECIFIC TO AKS
seccomp: on AKS the kubelet default seccomp profile is Unconfined (it shows up as CIS Kubernetes Benchmark 4.2.14, "Depends on Environment"). A pod that does not ask for a profile runs with NO syscall filtering. Two fixes:
a) In every pod spec (also required by the Restricted standard):
securityContext:
seccompProfile:
type: RuntimeDefault
b) Make RuntimeDefault the default for all pods on a node pool, with custom node configuration (this kubelet setting is in PREVIEW and needs the KubeletDefaultSeccompProfilePreview feature flag):
az feature register --namespace "Microsoft.ContainerService" --name "KubeletDefaultSeccompProfilePreview"
az provider register --namespace "Microsoft.ContainerService"
# linuxkubeletconfig.json: { "seccompDefault": "RuntimeDefault" }
az aks nodepool add --name hardened --cluster-name <cluster> --resource-group <rg> --kubelet-config ./linuxkubeletconfig.json
Microsoft warns that restricted syscalls then FAIL: test your workloads on a separate node pool first. Custom node configuration is reapplied on node image upgrades.
Identity for pods: use Microsoft Entra Workload ID instead of secrets in pods. Enable it on the cluster (az aks update --enable-oidc-issuer --enable-workload-identity), then federate a Kubernetes service account with a managed identity. In the pod: the service account annotated with azure.workload.identity/client-id and the pod labelled azure.workload.identity/use: "true". No credentials in environment variables, and tokens are short-lived.
Secrets: prefer the Azure Key Vault provider for the Secrets Store CSI Driver (az aks enable-addons --addons azure-keyvault-secrets-provider) over plain Kubernetes Secrets, and mount as files.
Images: run only images from your own Azure Container Registry. The Deployment Safeguards "allowed images" rule and Azure Policy's allowed-images definition enforce it. Copy needed public images into ACR (az acr import), let Microsoft Defender for Containers scan the registry and the running workloads, and turn on Image Cleaner so stale vulnerable images do not linger on nodes.
Also from the generic sheet, unchanged on AKS: automountServiceAccountToken false by default, readOnlyRootFilesystem, drop all capabilities, resource limits, no hostPath, no docker.sock.
6. IDENTITY AND ACCESS TO THE API SERVER
- Use Microsoft Entra ID integration and Azure RBAC for Kubernetes authorization, with the built-in roles Azure Kubernetes Service RBAC Reader, Writer, Admin and Cluster Admin at the narrowest scope (namespace, not cluster).
- Disable local Kubernetes accounts (--disable-local-accounts). Otherwise the static cluster-admin credential can bypass Entra-backed authorization. Microsoft lists this against CIS 3.1.1.
- Require MFA and compliant devices for the people who can administer clusters (Conditional Access).
- Do not expose the API server to the internet if you can avoid it: a private cluster, or API server VNet integration, or at least authorized IP ranges (--api-server-authorized-ip-ranges).
- Pipelines: use workload identity federation for the pipeline identity (no long-lived secrets), a role limited to what the deployment needs (for example Azure Kubernetes Service RBAC Writer on ONE namespace), never cluster-admin. Microsoft-hosted Azure DevOps agents do not work with private AKS clusters, so use self-hosted or a Managed DevOps Pool inside the network.
- Node authorization is on by default since AKS 1.24.
Check what a cluster is set to:
az aks show -g <rg> -n <cluster> --query "{k8s:kubernetesVersion, aad:aadProfile, localAccountsDisabled:disableLocalAccounts, apiServer:apiServerAccessProfile}"
kubectl auth can-i --list # what can I do here? (add -n kube-system to test that namespace)
kubectl auth can-i create pods -n kube-system --as <user> # who can reach the excluded namespace?
7. NODES: IMAGES, PATCHING AND THE KERNEL
Node operating systems on AKS: Ubuntu, Azure Linux, Azure Container Linux (ACL: immutable, container-optimised, generally available from AKS 1.34) and Windows Server. Nodes can use Trusted Launch (secure boot and vTPM) on Generation 2 VMs. The AKS node image is built and maintained for AKS: some unnecessary kernel module drivers are disabled, and the image is not supported outside AKS.
How patches reach the nodes (documented; for AKS Standard, where YOU pick the channel):
NodeImage Default for new Standard clusters. A newly patched VHD (security AND bug fixes) each week; upgrading is disruptive (reimage) and follows your maintenance window and surge settings. Building, testing and rolling out a new VHD can take around two weeks, faster for serious CVEs.
SecurityPatch AKS-tested, security-only patches, usually faster than weekly. AKS live-patches nodes when it can and reimages only when needed (for example some kernel packages). Nodes must reach snapshot.ubuntu.com. It does not work with an Azure Policy DENY that requires customer-managed keys on OS disks: the patch fails with RequestDisallowedByPolicy.
Unmanaged The OS patches itself about once a day (around 06:00 UTC). New nodes start unpatched, and YOU must handle reboots (for example with kured). Microsoft does not recommend it.
None No automatic OS updates at all.
az aks show -g <rg> -n <cluster> --query autoUpgradeProfile # current channels
az aks update -g <rg> -n <cluster> --node-os-upgrade-channel NodeImage # or SecurityPatch (CLI 2.61.0+); allow up to 24 hours for a change to apply
Schedule with the aksManagedNodeOSUpgradeSchedule maintenance window (Microsoft advises four hours or more).
kubectl get nodes -L kubernetes.azure.com/node-image-version # which node image each node runs (the date at the end shows the patch level)
az aks nodepool list -g <rg> --cluster-name <cluster> --query "[].{pool:name, image:nodeImageVersion, k8s:orchestratorVersion}" -o table
az aks nodepool upgrade -g <rg> --cluster-name <cluster> -n <pool> --node-image-only # upgrade a pool to the latest node image now
AKS clusters on the auto-upgrade channel for the Kubernetes version get control plane and node pool patches automatically: keep both cluster and node OS upgrades on a channel.
KERNEL BUGS (Dirty Frag, Copy Fail, and the next one): your nodes are only fixed when they run a node image that contains the patched kernel. So:
1. Watch the AKS release tracker and the AKS GitHub issues labelled CVE (Microsoft posts CVE issues there when user action is needed).
2. Check the node kernel: kubectl debug node/<node> -it --image=<image> then chroot /host ; uname -r (see the node-access sheet)
3. Compare the kernel with your vendor's fixed version. AKS Ubuntu nodes typically run an Azure-tuned kernel, so compare with the linux-azure numbers on Ubuntu's tracker (the Dirty Frag sheet lists examples), not with the generic kernel numbers. Azure Linux has its own kernel versions: use Microsoft's Azure Linux advisories.
4. Upgrade the node image (above) and confirm afterwards. Mitigations such as modprobe blocks belong in the node image process or custom node configuration, not in hand edits (see below).
DO NOT HARDEN NODES BY HAND. Changes made over SSH or a debug pod are lost when a node is reimaged or upgraded, and unsupported changes can void support. To customise nodes use custom node configuration (kubelet and sysctl settings per node pool) or, for anything else, a DaemonSet (Microsoft's guidance for keeping AKS support). Limit who can change node pool configuration.
Time-sensitive node OS notices (per Microsoft, checked 2026-09-29):
- Azure Linux 2.0: no security updates since November 30, 2025. On OCTOBER 31, 2026 node images are removed and node pools cannot scale. Migrate pools to Azure Linux 3 (osSku AzureLinux3) or another supported OS. Find them: kubectl get nodes -o wide and look at the OS-IMAGE column.
- Azure Linux with OS Guard (preview): support ends December 10, 2026; images removed March 10, 2027. Migrate to Azure Container Linux.
Scan the cluster's node and control-plane settings against the CIS Kubernetes Benchmark with kube-bench using the AKS job file (job-aks.yaml). Microsoft publishes how AKS scores against CIS Kubernetes Benchmark v1.12.0 for AKS 1.32 to 1.34: many control-plane items are "N/A because AKS is a managed solution" and the policy and RBAC items are marked "Depends on Environment", meaning they are YOURS to do.
8. NETWORK AND INGRESS ON AKS
- By default all pods can talk to all pods and outbound traffic is unrestricted. Apply default-deny network policies (see the generic sheet) and restrict egress (Azure Firewall / user-defined routing).
- Engines: Cilium (recommended by Microsoft, with Azure CNI Powered by Cilium: eBPF, Layer 7 policy and FQDN filtering through Advanced Container Networking Services), Calico, and Azure Network Policy Manager (NPM).
az aks create ... --network-plugin azure --network-plugin-mode overlay --network-dataplane cilium --network-policy cilium # confirm flags with az aks create --help
- RETIREMENTS (documented): Azure NPM on WINDOWS nodes ends September 30, 2026 (tomorrow, if you read this on 2026-09-29); Azure NPM on LINUX nodes ends September 30, 2028: migrate to Cilium Network Policy; kubenet networking ends March 31, 2028: move to Azure CNI Overlay. Find NPM clusters: az aks show ... --query networkProfile.networkPolicy (a value of azure means NPM).
- Network policy caveat with load balancers: traffic through a LoadBalancer service is rewritten, so policies may not match it. Restrict sources with spec.loadBalancerSourceRanges on the Service.
- Ingress: the community ingress-nginx project retired in March 2026. AKS's managed NGINX (application routing add-on) gets security patches from Microsoft through NOVEMBER 2026. Plan the move to a Gateway API implementation: the application routing add-on's Gateway API, Application Gateway for Containers, or Gateway API with the Istio add-on. Find out what you run: kubectl get ingressclass (webapprouting.kubernetes.azure.com is the managed NGINX class)
- The Open Service Mesh add-on is unsupported from September 30, 2027: migrate to the Istio add-on.
9. SECRETS AND DATA ON AKS
- You cannot set --encryption-provider-config on AKS: the API server is managed. Turn on KMS data encryption instead. For new clusters on Kubernetes 1.33 or later Microsoft recommends the newer KMS data encryption experience (platform-managed or customer-managed keys, with automatic rotation for customer-managed). The older Azure Key Vault KMS commands are documented as legacy:
az aks update -g <rg> -n <cluster> --enable-azure-keyvault-kms --azure-keyvault-kms-key-vault-network-access "Public" --azure-keyvault-kms-key-id <key-id>
kubectl get secrets --all-namespaces -o json | kubectl replace -f - # re-encrypt existing secrets (the "object has been modified" message is safe to ignore)
Legacy caveats: never delete or expire the key or vault (secrets become unrecoverable), it does not work with a system-assigned cluster identity, and rotation needs both old and new keys valid until the next rotation.
- Node disks are encrypted at rest by the platform. Add host-based encryption or customer-managed disk keys if your rules require them.
- Use Key Vault and Workload ID for application secrets (section 5). Enable AKS backup if you need to restore cluster state.
10. LOGGING, MONITORING AND DETECTION
- Diagnostic settings: send the kube-audit-admin, kube-audit and guard categories to Log Analytics (kube-audit-admin is the smaller, admin-actions view; AKS keeps audit log for about 14 days on the control plane).
- Container insights for metrics and container logs. Microsoft Defender for Containers for runtime threat detection on nodes, workloads and the control plane, plus registry and cluster vulnerability scanning.
- Alert on: pods created in kube-system by people, node-debugger pods, privileged pod creation, changes to role bindings, secret reads by unusual identities, and gatekeeper-system pods that are unhealthy (fail-open means an unhealthy Gatekeeper silently allows requests).
- Azure Policy compliance dashboard for the policy view, and kubectl get constraints for the cluster view.
11. COMPLIANCE ON AKS (short)
- Microsoft states AKS complies with SOC, ISO, PCI DSS and HIPAA standards for its part of the shared-responsibility model, and includes AKS in the FedRAMP High audit scope. Your workloads, RBAC, policies and configuration are still your responsibility.
- Azure Policy has regulatory-compliance initiatives and AKS built-in definitions that map to controls. Use them as evidence, along with scan reports and the audit log. See the CIS, PCI DSS, HIPAA, NIST and ISO/SOC 2 sheets on this tab.
- PCI DSS note: the PCI Council's information supplement on containers applies (see the generic sheet), and the AKS-specific "node debug access" and "kube-system" points above are the ones assessors tend to ask about.
CHECK YOUR WORK ON AKS
kubectl get pods -n gatekeeper-system ; kubectl get pods -n kube-system | grep azure-policy # Gatekeeper and the add-on healthy?
kubectl get constrainttemplates | head ; kubectl get constraints # which rules are actually active?
kubectl auth can-i create pods -n kube-system --as <someone> # who can use the excluded namespace?
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.containers[]?.securityContext?.privileged==true) | "\(.metadata.namespace)/\(.metadata.name)"'
kubectl get pods -A -o json | jq -r '.items[] | select((.spec.securityContext.seccompProfile // null) == null) | select([.spec.containers[] | (.securityContext.seccompProfile // null)] | all(. == null)) | "\(.metadata.namespace)/\(.metadata.name)"' | head # pods with NO seccomp profile (Unconfined on AKS)
az aks show -g <rg> -n <cluster> --query "{localAccountsDisabled:disableLocalAccounts, upgrade:autoUpgradeProfile, netpol:networkProfile.networkPolicy}"
kubectl get nodes -L kubernetes.azure.com/node-image-version -o wide
kubectl get netpol -A ; kubectl get ingressclass
kube-bench with the AKS job file (job-aks.yaml from the kube-bench repository)
System pods will show up in the first lists (CNI, CSI, monitoring). Understand each one and document it as an exception.
ROLLOUT PLAN FOR AN AKS CLUSTER THAT ALREADY USES GATEKEEPER
1. Inventory: which model do you have (add-on, Safeguards, own Gatekeeper)? Which policies are audit, which are deny? Which namespaces are excluded and why?
2. Fix the exposure that policy cannot see: who can create pods in kube-system (RBAC), local accounts, API server exposure, Azure Linux 2.0 pools, retired network plugins.
3. Patch model: put node OS upgrades on NodeImage or SecurityPatch with a maintenance window, and know how you confirm a kernel fix on your nodes.
4. Pod baseline: seccomp RuntimeDefault, non-root, no privilege escalation, drop ALL. Start Pod Security at warn/audit, then enforce namespace by namespace.
5. Policies: keep new rules in audit until the compliance view is clean, then deny. Add "allowed images" (ACR only) and "no privileged containers".
6. Network policy: default deny per namespace, DNS allowed, then real flows. Plan the Cilium migration if you use NPM.
7. Secrets: KMS data encryption, Key Vault CSI, workload identity.
8. Ingress: decide the Gateway API path before November 2026.
9. Detection: audit logs to Log Analytics, Defender for Containers, alerts on kube-system and debug pods.
10. Write down every exception with an owner and a review date (namespaces excluded from policy are the important ones).
Common problems
"Gatekeeper denied my pod: admission webhook validation.gatekeeper.sh denied the request": read the bracketed constraint name and message. Find the rule: kubectl get constraints and kubectl describe <kind> <name>. Fix the manifest, or ask for an exclusion/exemption with a reason.
"I changed the policy in Azure and nothing happened": the add-on syncs every 15 minutes. Wait, then check kubectl get constrainttemplates. If nothing arrives, check the azure-policy pod logs, that the add-on is enabled, and the node pools are Linux.
"My debug pod is blocked": see section 3 and the node-access sheet. Check the debug image is allowed by the images policy: copy it into your ACR if the policy restricts registries.
"Deployment Safeguards changed my resource limits": that is the mutator at Enforce (default 500m CPU / 2048Mi memory when none are set, minimums of 100m and 100Mi). Set explicit values in your manifests.
"Non-compliant pods were admitted": Gatekeeper is fail-open and the rule may be in audit. Check the gatekeeper pods' health and the effect (audit or deny).
"Policy applies to a namespace it should not, or not to one it should": check the assignment's namespace exclusions and scope, and that kube-system and gatekeeper-system are always excluded.
"Pods break after enabling seccompDefault": a restricted syscall is failing. Test on a separate node pool, then either fix the workload or give that pod an explicit profile.
"SecurityPatch upgrades fail with RequestDisallowedByPolicy": an Azure Policy deny (customer-managed keys for OS disks) blocks the temporary scale set. Use NodeImage on those clusters.
"I hardened a node over SSH and the change disappeared": node upgrades reimage nodes. Use custom node configuration or a DaemonSet.
"Network policy has no effect": the cluster may have no policy engine. Check networkProfile.networkPolicy. A value of none means policies are not enforced.
"Cannot scale a node pool on Azure Linux 2.0 after October 31, 2026": the node images were removed. Migrate the pool before the deadline.
"Azure Policy shows non-compliant but kubectl shows nothing wrong": compliance data lags (15-minute scans, 45-minute window) and the scope may be a resource group. Compare timestamps.
Tips
Treat namespaces excluded from policy like production admin accounts: few, named, reviewed.
Move node upgrades to a schedule you control (maintenance windows) and confirm kernel CVEs actually landed: a weekly image you never roll out helps nobody.
Keep audit rules in audit until you understand the noise; a deny rule that blocks a critical fix on a Friday makes enemies.
Prefer AKS's own mechanisms (custom node configuration, policies, safeguards, channels) over hand changes. They survive upgrades and keep you supported.
Run one small test cluster where you try every policy and every AKS preview change first.
Gatekeeper decides what may be created. The node image decides what is underneath it. Patching decides how long the door stays open.
Sources (checked 2026-09-29)
Azure Policy for Kubernetes (add-on, Gatekeeper, exclusions): https://learn.microsoft.com/en-us/azure/governance/policy/concepts/policy-for-kubernetes
AKS Deployment Safeguards: https://learn.microsoft.com/en-us/azure/aks/deployment-safeguards
Security concepts for AKS: https://learn.microsoft.com/en-us/azure/aks/concepts-security
Secure your AKS deployment (Microsoft checklist): https://learn.microsoft.com/en-us/azure/aks/secure-aks
AKS and the CIS Kubernetes Benchmark: https://learn.microsoft.com/en-us/azure/aks/cis-kubernetes
AKS vulnerability management: https://learn.microsoft.com/en-us/azure/aks/concepts-vulnerability-management
Node OS auto-upgrade channels: https://learn.microsoft.com/en-us/azure/aks/auto-upgrade-node-os-image
Custom node configuration and its parameter reference: https://learn.microsoft.com/en-us/azure/aks/custom-node-configuration
https://learn.microsoft.com/en-us/azure/aks/custom-node-configuration-reference
Network policies on AKS (engines and retirements): https://learn.microsoft.com/en-us/azure/aks/use-network-policies
Application routing add-on (managed NGINX) and Gateway API notice: https://learn.microsoft.com/en-us/azure/aks/app-routing
KMS etcd encryption (legacy) on AKS: https://learn.microsoft.com/en-us/azure/aks/use-kms-etcd-encryption
AKS release notes and CVE issues: https://github.com/Azure/AKS/releases
Related sheets: Container and Kubernetes Hardening (Hardening tab); Kubernetes node access with kubectl debug (Cloud Tutorials tab); Dirty Frag and Copy Fail (Security tab); Popeye, Kubernetes and Helm (DevOps tab)