---
title: Claude Code for SRE work
slug: claude-code-for-sre
summary: Set up Claude Code so it is useful on infrastructure repos and safe around production, with a CLAUDE.md, tight permissions, a secret filter, headless mode and changes only through merge requests.
order: 28
updated: 2026-11-11
status: ready
---

Use this page to configure Claude Code (or a similar coding agent) for SRE repos: IaC, CI templates, runbooks and automation scripts. The [agent operating rules](./agent-operating-rules.md) are the policy; this page is how to enforce it in the tool.

Use it only on machines and with data your organization has approved for AI tools.

## Two ways to use it

| Mode | Command | Good for |
| --- | --- | --- |
| Interactive | `claude` in the repo | Writing a Terraform module, porting a pipeline, reviewing a diff, drafting a runbook |
| Headless | `claude -p "<prompt>"` | Scheduled, repeatable text jobs: classify tickets, summarise a plan, draft a report |

## Write a CLAUDE.md in every repo

Claude reads `CLAUDE.md` at the repo root on start. Keep it short and specific.

```markdown
# CLAUDE.md

## What this repo is
Terraform for <component> across dev, staging and prod. One state per env under envs/<env>.

## Ground rules
- Read-only by default. Never run terraform apply, aws write calls, git push to main, or force-push.
- Changes are proposed as a branch and a draft MR. A human merges.
- Use the `<account-alias>-ro` AWS profiles only. Always pass --profile and --region.
- Never print or paste secrets, tokens, account IDs, hostnames or customer data.
- When unsure, stop and ask; write the question in the MR description.

## How to check your work
- terraform fmt -recursive && terraform validate
- tflint --recursive
- ./scripts/plan.sh <env>   # read-only plan

## Workflows
### port-pipeline
1. Read the old CI file. 2. Generate the new CI file from templates/. 3. List every TODO. 4. Open a draft MR.
```

Add personal, machine-specific notes in `~/.claude/CLAUDE.md`, not in the repo.

## Lock down permissions

Project settings live in `.claude/settings.json` (shared) and `.claude/settings.local.json` (yours, git-ignored).

```json
{
  "permissions": {
    "defaultMode": "plan",
    "allow": [
      "Bash(terraform fmt:*)",
      "Bash(terraform validate:*)",
      "Bash(terraform plan:*)",
      "Bash(tflint:*)",
      "Bash(git status:*)",
      "Bash(git diff:*)",
      "Bash(git log:*)",
      "Bash(glab mr view:*)",
      "Bash(glab ci lint:*)"
    ],
    "deny": [
      "Bash(terraform apply:*)",
      "Bash(terraform destroy:*)",
      "Bash(git push --force:*)",
      "Bash(git push -f:*)",
      "Bash(aws secretsmanager get-secret-value:*)",
      "Read(./**/*.tfstate)",
      "Read(./**/.env)",
      "Read(~/.aws/credentials)"
    ]
  }
}
```

- [ ] `plan` mode means Claude proposes and you approve each edit or command.
- [ ] The strongest guard is outside the tool: give the session read-only cloud credentials, so even an allowed command cannot write.
- [ ] Never run with the skip-permissions flag on a machine that holds production credentials.

## Add a guard hook

A `PreToolUse` hook runs before every tool call and can block it (exit code 2).

```json
{
  "hooks": {
    "PreToolUse": [
      { "matcher": "Bash", "hooks": [ { "type": "command", "command": "~/.claude/hooks/guard.sh" } ] }
    ]
  }
}
```

```bash
#!/usr/bin/env bash
# ~/.claude/hooks/guard.sh: block mutating cloud and git commands
cmd="$(jq -r '.tool_input.command // ""')"
if grep -Eq '(^|[ ;&|])(terraform (apply|destroy)|kubectl (apply|delete)|git push (-f|--force))' <<<"$cmd" \
   || grep -Eq 'aws .* (delete|put|update|create|terminate|modify)-' <<<"$cmd"; then
  echo "blocked by guard.sh: mutating command needs a human" >&2
  exit 2
fi
exit 0
```

## Run headless jobs safely

```bash
redact() {
  sed -E \
    -e 's/AKIA[0-9A-Z]{16}/<aws-key-id>/g' \
    -e 's/\b[0-9]{12}\b/<account-id>/g' \
    -e 's/\b[0-9]{13,19}\b/<number>/g' \
    -e 's/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/<email>/g' \
    -e 's/\b([0-9]{1,3}\.){3}[0-9]{1,3}\b/<ip>/g' \
    -e 's/(token|secret|password|passwd|api[_-]?key)([" :=]+)[^" ,]+/\1\2<redacted>/Ig'
}

redact < tickets.jsonl | claude -p "$(cat prompts/classify.md)" \
  --output-format json \
  --max-turns 1 \
  --disallowedTools Bash Edit Write WebFetch WebSearch \
  | jq -r '.result' > state/classified.json
```

- [ ] Filter every byte before it reaches the model. Add org-specific patterns (internal domains, project names) from a local, git-ignored file.
- [ ] Disable tools for pure text transforms. The job reads input and writes an answer, nothing more.
- [ ] Validate output: parse the JSON, check labels are from the allowed set, and treat anything else as "needs a human".
- [ ] Cap cost with `--max-turns` and, where available, `--max-budget-usd`.

## Changes only through merge requests

- [ ] Claude works on a branch, never the default branch.
- [ ] It opens a draft MR (`glab mr create --draft`) or leaves the branch for you to push.
- [ ] CI runs plan and tests; a human reviews and merges.
- [ ] MR descriptions state that an agent drafted the change.

## Pitfalls

- A long CLAUDE.md nobody maintains. Keep it under a page; link to docs for the rest.
- Pasting log excerpts with secrets into an interactive session. Redact first, even when it feels urgent.
- Trusting a plan summary instead of the plan. Read the real `terraform plan` output.
