Writing Idempotent Ansible Playbooks for Kubernetes Clusters
How I built a reliable WhatsApp AI shopping assistant for Clickmothercare that survives hallucinated products, silent save failures, and multi-agent handoff bugs.
The Idempotency Challenge in Kubernetes with Ansible
While Kubernetes is inherently declarative, using Ansible to manage external configurations, secrets, or complex orchestrations around a cluster often leads to imperative anti-patterns. A common pitfall is using the command or shell modules with kubectl, which breaks idempotency by executing every time the playbook runs, regardless of the cluster's state.
Moving to the kubernetes.core.k8s Module
To achieve true idempotence, we must leverage the kubernetes.core.k8s module. This module speaks directly to the Kubernetes API, allowing Ansible to compare the desired state (defined in your YAML or dict) against the live state in the cluster, only making changes when they differ.
- name: Ensure Nginx deployment is in the desired state
kubernetes.core.k8s:
state: present
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.24.0 # Specific version pinning for idempotence
ports:
- containerPort: 80
Notice how we explicitly pin the image: nginx:1.24.0. Using latest is an idempotency killer; the playbook cannot guarantee the underlying image hash hasn't changed, leading to unpredictable updates.
Handling Secrets Intelligently
Generating secrets on the fly often triggers constant changes. Let's look at an idempotent approach to handling a TLS certificate secret, ensuring the secret is only regenerated if the certificate file actually changes on disk.
- name: Read TLS certificate contents
ansible.builtin.slurp:
src: /etc/ssl/certs/app.crt
register: tls_cert
- name: Read TLS key contents
ansible.builtin.slurp:
src: /etc/ssl/private/app.key
register: tls_key
- name: Apply TLS Secret to Kubernetes
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Secret
type: kubernetes.io/tls
metadata:
name: app-tls
namespace: production
data:
tls.crt: "{{ tls_cert['content'] }}" # Slurp returns b64 encoded data, perfect for k8s secrets
tls.key: "{{ tls_key['content'] }}"
Because slurp returns Base64 encoded content and Kubernetes expects Base64, we inject it directly. The kubernetes.core.k8s module computes the hash of the live secret and the desired secret. If the disk files haven't changed, the playbook reports ok rather than changed.
Is your AI agent's infrastructure secure and reliable?
Book a Free 15-Min Technical Audit