🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

Build an AI Kubernetes Secret Rotation Assistant with Claude API

Secret rotation gets skipped because nobody wants to be the one who breaks production by rotating a credential something still depends on. Build a tool that maps secret usage across your cluster and safely sequences rotation with Claude API.

Shubham4 min read
Share:Tweet

Secret rotation policy exists on paper everywhere and gets executed almost nowhere, because the real risk isn't rotating the secret — it's not knowing everything that reads it, and finding out the hard way when a service starts failing auth mid-rotation. This tool maps actual secret consumption before proposing a rotation sequence, so "what depends on this" stops being a guess.

Setup

bash
pip install anthropic kubernetes

Secret Usage Mapper

python
import anthropic
from kubernetes import client, config
 
client_ai = anthropic.Anthropic()
 
 
def find_secret_consumers(secret_name: str, namespace: str) -> dict:
    """Find every workload that references this secret — as an env var,
    a mounted volume, or an imagePullSecret."""
    config.load_kube_config()
    v1 = client.CoreV1Api()
    apps_v1 = client.AppsV1Api()
 
    consumers = {"deployments": [], "statefulsets": [], "cronjobs": [], "service_accounts": []}
 
    for deploy in apps_v1.list_namespaced_deployment(namespace).items:
        if references_secret(deploy.spec.template.spec, secret_name):
            consumers["deployments"].append(deploy.metadata.name)
 
    for sts in apps_v1.list_namespaced_stateful_set(namespace).items:
        if references_secret(sts.spec.template.spec, secret_name):
            consumers["statefulsets"].append(sts.metadata.name)
 
    for sa in v1.list_namespaced_service_account(namespace).items:
        if sa.secrets and any(s.name == secret_name for s in sa.secrets):
            consumers["service_accounts"].append(sa.metadata.name)
 
    return consumers
 
 
def references_secret(pod_spec, secret_name: str) -> bool:
    for container in pod_spec.containers:
        for env in (container.env or []):
            if env.value_from and env.value_from.secret_key_ref and env.value_from.secret_key_ref.name == secret_name:
                return True
    for volume in (pod_spec.volumes or []):
        if volume.secret and volume.secret.secret_name == secret_name:
            return True
    return False

External Consumer Detection

python
def check_external_references(secret_name: str, namespace: str) -> list[str]:
    """Secrets referenced from outside the cluster (CI systems pulling
    from Vault-synced secrets, external services reading via API) are
    the ones that break rotation silently — check what you can."""
    warnings = []
 
    # Check for external-secrets operator syncing this from a source
    result = subprocess.run(
        ["kubectl", "get", "externalsecret", "-n", namespace, "-o", "json"],
        capture_output=True, text=True
    )
    import json
    for es in json.loads(result.stdout).get("items", []):
        if es["spec"]["target"]["name"] == secret_name:
            warnings.append(f"Synced by ExternalSecret from {es['spec']['secretStoreRef']['name']} — rotation must originate at the source, not the K8s secret")
 
    return warnings

Rotation Plan Generation

python
PLAN_PROMPT = """Generate a safe rotation plan for this Kubernetes secret.
 
Secret: {secret_name} in namespace {namespace}
Known in-cluster consumers: {consumers}
External sync warnings: {external_warnings}
 
Generate a step-by-step rotation plan that avoids downtime:
1. Whether this credential type supports dual validity (old and new both
   work simultaneously during a grace period) — most API keys and OAuth
   secrets do, most database passwords do NOT unless you create a second
   user first
2. The safe order of operations: create new credential -> update secret
   with BOTH old and new (if dual-validity) or a rolling restart sequence
   (if not) -> verify consumers healthy -> revoke old credential
3. Specific kubectl commands for restarting each consumer type to pick
   up the new secret (Deployments don't auto-restart on Secret change
   unless using something like Reloader)
4. Rollback plan if a consumer fails to authenticate with the new credential
 
Respond as a step-by-step runbook."""
 
 
def generate_rotation_plan(secret_name: str, namespace: str, consumers: dict, external_warnings: list) -> str:
    response = client_ai.messages.create(
        model="claude-sonnet-5",
        max_tokens=2000,
        messages=[{
            "role": "user",
            "content": PLAN_PROMPT.format(
                secret_name=secret_name, namespace=namespace,
                consumers=consumers, external_warnings=external_warnings,
            )
        }]
    )
    return response.content[0].text

Example Output

Rotation Plan: database-credentials (namespace: production)

Consumers found:
- Deployments: api-server, worker-pool, report-generator
- StatefulSets: none
- External: none detected

Credential type: PostgreSQL password — does NOT support dual validity
by default (single password per user).

Safe rotation sequence:
1. Create a second Postgres role with identical grants: `CREATE ROLE
   app_user_v2 WITH PASSWORD 'new-password' LOGIN;` then grant it the
   same permissions as the current role
2. Update the Kubernetes secret with the NEW role's credentials
3. Rolling-restart consumers one at a time, verifying health before
   proceeding: `kubectl rollout restart deployment/api-server -n production`
   then `kubectl rollout status deployment/api-server -n production`
4. Repeat for worker-pool, then report-generator
5. Once all 3 consumers confirmed healthy on the new credential
   (check logs for auth errors), revoke the OLD role:
   `DROP ROLE app_user_v1;`

Rollback: if any consumer fails auth after restart, revert the secret
to the old credential and restart that consumer immediately — the old
role remains valid until step 5, so this is a safe rollback window.

Why the Dual-Validity Check Matters Most

The single most common rotation failure is treating every credential type the same — API keys often support having two valid keys simultaneously (rotate calmly), while a database password swap is often instant and all-or-nothing (requires careful sequencing or a second user). Getting this distinction wrong is what turns a routine rotation into an incident. This is the one judgment call worth having an LLM reason through explicitly rather than applying a generic rotation script to every secret type uniformly.


More AI security tooling? Read our Build AI secret scanner with Claude API and Build AI compliance auditor with Claude API.

🔧

Today I Fixed

Short real fixes from production — posted daily

Browse fixes
Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments