Build an AI Kubernetes YAML Explainer and Validator with Claude API
Step-by-step tutorial to build a tool that takes any Kubernetes YAML manifest, explains what it does in plain English, catches misconfigurations and security issues, and suggests improvements — using Claude API.
Kubernetes YAML is notorious for being hard to read and easy to misconfigure. A Deployment that looks correct can silently have no resource limits, a dangerously permissive security context, or a readiness probe that will never pass.
Having Claude API read and critique Kubernetes manifests turns a tedious manual review process into something you can automate — and the quality of analysis is genuinely useful.
What We're Building
A Python CLI and FastAPI service that takes a Kubernetes YAML file and returns:
- Plain English explanation — what this resource does and how it fits into a cluster
- Security analysis — dangerous configurations, privilege escalation risks, missing security contexts
- Best practice violations — missing resource limits, no health probes, deprecated fields
- Improved version — a corrected YAML with issues fixed
Setup
pip install anthropic fastapi uvicorn python-multipart pyyaml python-dotenvStep 1: YAML Parsing and Validation
import yaml
import json
import os
from typing import Union
import anthropic
def load_kubernetes_yaml(content: str) -> list[dict]:
"""Parse YAML content — handles multi-document YAML (---)."""
documents = []
for doc in yaml.safe_load_all(content):
if doc is not None:
documents.append(doc)
return documents
def extract_resource_context(doc: dict) -> dict:
"""Extract key fields for context-aware analysis."""
kind = doc.get("kind", "Unknown")
name = doc.get("metadata", {}).get("name", "unnamed")
namespace = doc.get("metadata", {}).get("namespace", "default")
spec = doc.get("spec", {})
context = {
"kind": kind,
"name": name,
"namespace": namespace,
}
# Extract container specs for Deployments, Pods, StatefulSets
containers = []
if kind in ["Deployment", "StatefulSet", "DaemonSet", "Job", "CronJob"]:
template_spec = spec.get("template", {}).get("spec", {})
containers = template_spec.get("containers", [])
init_containers = template_spec.get("initContainers", [])
elif kind == "Pod":
containers = spec.get("containers", [])
init_containers = spec.get("initContainers", [])
else:
init_containers = []
context["containers"] = [
{
"name": c.get("name"),
"image": c.get("image"),
"resources": c.get("resources", {}),
"securityContext": c.get("securityContext", {}),
"readinessProbe": c.get("readinessProbe"),
"livenessProbe": c.get("livenessProbe"),
"env": [e.get("name") for e in c.get("env", [])]
}
for c in containers
]
context["initContainers"] = [c.get("name") for c in init_containers]
# Service-specific context
if kind == "Service":
context["type"] = spec.get("type", "ClusterIP")
context["ports"] = spec.get("ports", [])
# RBAC context
if kind in ["Role", "ClusterRole"]:
context["rules"] = doc.get("rules", [])
return contextStep 2: Claude API Analysis
client = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
def analyze_kubernetes_manifest(yaml_content: str) -> dict:
"""
Send Kubernetes YAML to Claude for comprehensive analysis.
Returns explanation, issues, and improved YAML.
"""
try:
documents = load_kubernetes_yaml(yaml_content)
except yaml.YAMLError as e:
return {
"error": f"Invalid YAML: {str(e)}",
"explanation": None,
"issues": [],
"improved_yaml": None
}
if not documents:
return {"error": "Empty YAML document", "explanation": None, "issues": [], "improved_yaml": None}
# Extract context for each resource
contexts = [extract_resource_context(doc) for doc in documents]
yaml_block = "```yaml\n" + yaml_content + "\n```"
context_json = json.dumps(contexts, indent=2)
prompt = (
"You are a Kubernetes expert reviewing a manifest. Analyze the following Kubernetes YAML "
"and provide a comprehensive review.\n\n"
"## YAML to Review\n\n" + yaml_block + "\n\n"
"## Extracted Context (for your reference)\n" + context_json + "\n\n---\n\n"
"Provide your analysis in the following exact JSON structure:\n\n"
'{"explanation": "Plain English explanation...", '
'"security_issues": [{"severity": "critical|high|medium|low", "field": "path.to.field", "issue": "...", "fix": "..."}], '
'"best_practice_violations": [{"severity": "high|medium|low", "field": "...", "issue": "...", "fix": "..."}], '
'"positive_observations": ["..."], '
'"summary": {"overall_score": 0-100, "verdict": "Production-ready|Needs fixes|Not production-safe", "top_priority": "..."}, '
'"improved_yaml": "Complete corrected YAML."}\n\n'
"Security: check runAsRoot, privileged, hostNetwork, missing seccompProfile, wildcard RBAC, hardcoded secrets, NodePort.\n"
"Best practices: missing resource limits, no health probes, :latest tags, missing PDB, single replica.\n"
"Return ONLY valid JSON, no markdown code blocks around the JSON."
)
message = client.messages.create(
model="claude-sonnet-5",
max_tokens=4000,
messages=[{"role": "user", "content": prompt}]
)
response_text = message.content[0].text.strip()
# Parse JSON response
try:
# Sometimes Claude wraps in code block despite instructions
if response_text.startswith("```"):
response_text = response_text.split("```")[1]
if response_text.startswith("json"):
response_text = response_text[4:]
result = json.loads(response_text)
return result
except json.JSONDecodeError:
return {
"explanation": response_text,
"security_issues": [],
"best_practice_violations": [],
"positive_observations": [],
"summary": {"overall_score": 0, "verdict": "Parse error", "top_priority": "N/A"},
"improved_yaml": None
}Step 3: CLI Interface
import sys
def format_analysis_report(analysis: dict, yaml_file: str) -> str:
"""Format the analysis as a readable terminal report."""
lines = []
lines.append(f"\n{'='*60}")
lines.append(f" KUBERNETES YAML ANALYSIS: {yaml_file}")
lines.append(f"{'='*60}\n")
if "error" in analysis:
lines.append(f"ERROR: {analysis['error']}")
return "\n".join(lines)
# Overall score
summary = analysis.get("summary", {})
score = summary.get("overall_score", 0)
verdict = summary.get("verdict", "Unknown")
score_emoji = "✅" if score >= 80 else "⚠️" if score >= 60 else "❌"
lines.append(f"{score_emoji} Overall Score: {score}/100 — {verdict}")
lines.append("")
# Explanation
if analysis.get("explanation"):
lines.append("📖 WHAT THIS DOES")
lines.append(f" {analysis['explanation']}")
lines.append("")
# Security issues
security = analysis.get("security_issues", [])
if security:
lines.append("🔒 SECURITY ISSUES")
for issue in security:
sev = issue.get("severity", "").upper()
icon = "🔴" if sev == "CRITICAL" else "🟠" if sev == "HIGH" else "🟡"
lines.append(f" {icon} [{sev}] {issue.get('field', '')}")
lines.append(f" Issue: {issue.get('issue', '')}")
lines.append(f" Fix: {issue.get('fix', '')}")
lines.append("")
# Best practice violations
violations = analysis.get("best_practice_violations", [])
if violations:
lines.append("📋 BEST PRACTICE VIOLATIONS")
for v in violations:
sev = v.get("severity", "").upper()
icon = "🟠" if sev == "HIGH" else "🟡" if sev == "MEDIUM" else "🔵"
lines.append(f" {icon} [{sev}] {v.get('field', '')}")
lines.append(f" Issue: {v.get('issue', '')}")
lines.append(f" Fix: {v.get('fix', '')}")
lines.append("")
# Positive observations
positive = analysis.get("positive_observations", [])
if positive:
lines.append("✅ WHAT'S DONE WELL")
for obs in positive:
lines.append(f" • {obs}")
lines.append("")
# Top priority
top_priority = summary.get("top_priority")
if top_priority:
lines.append(f"🎯 TOP PRIORITY: {top_priority}")
lines.append("")
return "\n".join(lines)
def cli():
if len(sys.argv) < 2:
print("Usage: python k8s_analyzer.py <path-to-yaml-file> [--save-fixed]")
sys.exit(1)
yaml_file = sys.argv[1]
save_fixed = "--save-fixed" in sys.argv
with open(yaml_file, "r") as f:
yaml_content = f.read()
print(f"Analyzing {yaml_file}...")
analysis = analyze_kubernetes_manifest(yaml_content)
print(format_analysis_report(analysis, yaml_file))
if save_fixed and analysis.get("improved_yaml"):
fixed_path = yaml_file.replace(".yaml", "-fixed.yaml").replace(".yml", "-fixed.yml")
with open(fixed_path, "w") as f:
f.write(analysis["improved_yaml"])
print(f"\n💾 Improved YAML saved to: {fixed_path}")Step 4: FastAPI Web Service
from fastapi import FastAPI, UploadFile, File, Form
from fastapi.responses import JSONResponse
app = FastAPI(title="K8s YAML Analyzer", version="1.0.0")
@app.post("/analyze")
async def analyze_yaml(
file: UploadFile = File(None),
yaml_content: str = Form(None)
):
"""Accept either a file upload or raw YAML string."""
if file:
content = (await file.read()).decode("utf-8")
elif yaml_content:
content = yaml_content
else:
return JSONResponse({"error": "Provide either a file or yaml_content"}, status_code=400)
result = analyze_kubernetes_manifest(content)
return result
@app.get("/health")
def health():
return {"status": "ok"}
if __name__ == "__main__":
import uvicorn
cli() # Run CLI mode if yaml file passed, else run APIExample Output
For a Deployment with a hardcoded password, no resource limits, and privileged containers:
============================================================
KUBERNETES YAML ANALYSIS: payment-api.yaml
============================================================
❌ Overall Score: 28/100 — Not production-safe
📖 WHAT THIS DOES
This creates a Deployment running 1 replica of a payment API
container, exposing port 8080, connected to a PostgreSQL database
via a hardcoded password in an environment variable.
🔒 SECURITY ISSUES
🔴 [CRITICAL] spec.template.spec.containers[0].securityContext.privileged
Issue: Container runs as privileged — full access to host kernel
Fix: Remove privileged: true, add securityContext.runAsNonRoot: true
🔴 [CRITICAL] spec.template.spec.containers[0].env[DB_PASSWORD]
Issue: Database password hardcoded in env var, visible in kubectl describe
Fix: Use secretKeyRef — create a Secret and reference with valueFrom.secretKeyRef
🎯 TOP PRIORITY: Remove privileged container — this gives the container
root access to the host kernel and is a critical security risk.
Integration with CI/CD
# .github/workflows/k8s-lint.yml
- name: Analyze Kubernetes manifests
run: |
pip install anthropic pyyaml
for f in k8s/**/*.yaml; do
python k8s_analyzer.py "$f"
done
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}More AI tools for DevOps? Read our AI GitHub Actions failure analyzer and AI Terraform drift detector.
Today I Fixed
Short real fixes from production — posted daily
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
Build an AI Deployment Health Checker with Claude API and Kubernetes
Step-by-step tutorial to build an AI-powered deployment health checker using Claude API and the Kubernetes Python client. Automatically diagnose failing pods, check resource limits, and get plain-English explanations of what's wrong.
Build an AI Kubernetes Deployment Readiness Checker with Claude API
Build a Python CLI tool using Claude API that analyzes Kubernetes YAML manifests before deployment — catches missing resource limits, root containers, and security issues with a go/no-go score.
Build an AI Kubernetes Runbook Generator with Claude API
Step-by-step tutorial to build a tool that automatically generates operational runbooks for any Kubernetes resource using Claude API — with real examples for Deployments, StatefulSets, and CronJobs.