Protect AWS production profiles in Claude Code with hooks
Gabriele Mariotti shows how to use Claude Code's `PreToolUse` hook to allow all AWS CLI commands by default while forcing explicit confirmation only when a command targets a protected production profile.
Score breakdown
Teams using Claude Code for AWS work can adopt this pattern to let AI agents move freely across dev and staging environments while ensuring a human is always in the loop before any production account is touched — without modifying daily workflows.
- 01A blanket `Bash(aws *)` allow rule in `settings.json` grants Claude Code permission to run any AWS CLI command without confirmation.
- 02A `PreToolUse` hook intercepts only AWS commands using a two-layer filter: `matcher: "Bash"` and `"if": "Bash(aws *)"`.
- 03The guard script `aws-prod-guard.sh` reads the pending command from stdin as JSON using `jq`.
Gabriele Mariotti outlines a problem familiar to teams using Claude Code with AWS: the two obvious permission settings are both unsatisfying. Adding `Bash(aws *)` to the allow list removes all friction but lets production commands run unconfirmed, while manually approving every AWS call is tedious even for harmless operations like `aws s3 ls` on dev buckets. The solution layers a `PreToolUse` hook on top of the broad allow rule to create a selective confirmation gate.
When a match is found, it returns `permissionDecision: "ask"` along with the profile name, description, and account ID, which Claude surfaces to the user as a confirmation prompt.
The hook configuration in `~/.claude/settings.json` uses two filters: an outer `matcher: "Bash"` to select the tool type, and an inner `"if": "Bash(aws *)"` pre-filter so the guard script is only invoked for AWS commands — not every `git`, `npm`, or `make` call. The script `aws-prod-guard.sh` reads the command from stdin via `jq`, then iterates over entries in `aws-prod-profiles.json`, checking whether the command references a protected profile via `--profile`, `AWS_PROFILE`, or `AWS_DEFAULT_PROFILE`. When a match is found, it returns `permissionDecision: "ask"` along with the profile name, description, and account ID, which Claude surfaces to the user as a confirmation prompt. If no protected profile is detected, the script exits silently and Claude proceeds normally. Protected profiles are managed in a separate JSON file, making it easy to add or remove entries without modifying the script itself.
Key facts
- 01A blanket `Bash(aws *)` allow rule in `settings.json` grants Claude Code permission to run any AWS CLI command without confirmation.
- 02A `PreToolUse` hook intercepts only AWS commands using a two-layer filter: `matcher: "Bash"` and `"if": "Bash(aws *)"`.
- 03The guard script `aws-prod-guard.sh` reads the pending command from stdin as JSON using `jq`.
- 04When a protected production profile is detected, the hook returns `permissionDecision: "ask"`, pausing Claude and prompting the user.
- 05Protected profiles are stored in a separate `aws-prod-profiles.json` file with `profile`, `account`, and `description` fields.
- 06The confirmation message shown to the user includes the profile name, description, and AWS account ID.
- 07Non-production commands exit the hook silently and proceed without any interruption.