Skip to main content

aws-why

aws-why turns failed AWS CLI commands into actionable access requests and can optionally simulate permissions for IAM actions and resources.

$ aws-why run -- aws s3 cp test.csv s3://prod-data/test.csv
upload failed: ... AccessDenied ...

DENIED: s3:PutObject on arn:aws:s3:::prod-data/test.csv

Role
  arn:aws:iam::123456789012:role/DataEngineer

Reason
  a permissions boundary blocks the action

NEXT STEP
  An identity-policy Allow alone will not fix this. Ask the boundary owner to
  permit the action, then verify the identity policy also allows it.

When AWS reports a missing identity-policy Allow, the handoff also includes a narrowly scoped candidate policy for administrator review. Before showing it, run checks the action and resource shape against AWS's public Service Authorization Reference. A confirmed mismatch suppresses the policy instead of suggesting an invalid grant. Policies are never applied automatically. Explicit denies, permissions boundaries, SCPs, resource policies, and other restrictive layers receive cause-specific guidance instead of an ineffective Allow recommendation.

The normal run workflow does not require IAM simulator access. Its default output contains only the denied request, attachable principal, reason, and next action. Add --verbose to include the session identity, evidence source, credential source, and diagnostic notes; use --json for the complete machine-readable report. The tool also does not treat every AWS failure as IAM: expired credentials, missing configuration, network errors, and missing resources get distinct results.

Install and use

The normal installation does not require Rust or Cargo. Install the native executable in an isolated environment with pipx:

pipx install aws-why
aws-why doctor
aws-why run -- aws sts get-caller-identity
aws-why explain aws-error.txt
aws-why run --json -- aws s3api get-object --bucket example --key report.csv report.csv

Or use uv:

uv tool install aws-why

Release wheels contain the compiled executable; Python is only the distribution mechanism. Wheels are built for macOS on Apple Silicon and Intel, Linux on ARM64 and x86-64, and Windows x86-64.

Explain an error without rerunning it

Analyze a saved AWS CLI error from a log, CI artifact, or another machine:

aws kms list-keys 2> aws-error.txt
aws-why explain aws-error.txt

Or pipe an error through stdin:

aws-why explain - < aws-error.txt

explain never reruns the failed command and never uses AWS credentials. By default it is fully offline. Add --validate-policy to check a generated policy's resource scope against AWS's public Service Authorization Reference; this makes unauthenticated HTTPS requests but sends no AWS credentials. --json, --verbose, and --redact work the same way as they do with run.

Validate candidate-policy resources

run validates generated policy resources by default. For example, AWS's reference says kms:ListKeys requires "Resource": "*", while s3:GetObject accepts object or access-point-object ARNs rather than a bare bucket ARN. The output labels a resource check as verified, mismatch, or not verified. A confirmed mismatch removes the candidate policy; a temporarily unavailable catalog leaves the candidate in place with an explicit warning for administrator review.

The catalog does not currently expose dependent-action metadata. aws-why therefore reports dependencies only when the live AWS error identifies them—for example, a kms:Decrypt denial encountered while reading a Secrets Manager secret—instead of guessing. Use run --no-policy-validation to disable the public catalog request.

Check your setup

Run doctor before you need to diagnose a failure:

$ aws-why doctor

AWS-WHY DOCTOR

+ AWS credentials
  arn:aws:iam::123456789012:role/DataEngineer

+ Denial explanations
  Available. AWS-reported causes do not require IAM simulation.

- Permission simulation (optional)
  Unavailable. `run` still works; `can` and `permissions` require iam:SimulatePrincipalPolicy.

READY
  Run: aws-why run -- aws <service> <operation>

doctor calls sts:GetCallerIdentity and makes a harmless iam:SimulatePrincipalPolicy request to determine which features are available. Missing simulation access is informational and does not make doctor fail because the normal run workflow does not require it. Use --profile, --region, --aws-cli, --json, or --redact when needed.

Share diagnostics safely

Generated reports contain real AWS identity and resource identifiers by default because administrators need them to fix access. Use --redact before posting output publicly:

aws-why run --redact -- aws s3api get-object \
  --bucket example \
  --key report.csv \
  report.csv

Redaction masks account, principal, session, resource, policy, and request identifiers in both human and JSON failure reports. It also suppresses output produced by a wrapped command that ultimately fails, so earlier partial output cannot undermine the redaction. It does not alter output from a successful wrapped command, because successful commands remain transparent pass-throughs.

Advanced: simulate permissions safely

can evaluates one IAM action against one or more resources without executing that action:

$ aws-why can s3:GetObject --resource arn:aws:s3:::example/report.csv

SIMULATED PERMISSIONS

Identity
  account: 123456789012
  principal: DataEngineer
  session: example-session
  policy source: arn:aws:iam::123456789012:role/DataEngineer

Resource
  arn:aws:s3:::example/report.csv
  + ALLOWED  s3:GetObject

Summary: 1 allowed, 0 denied, 0 unknown
Simulation only: this does not execute the actions or prove a live request will succeed.

can exits with status 0 when every simulated result is allowed, 3 when any result is denied or unknown, and 2 when identity discovery or simulation fails. Add --json for machine-readable output.

permissions builds a resource-specific matrix. By default it retrieves the current action inventory from AWS's public Service Authorization Reference and evaluates the actions in bounded batches:

aws-why permissions \
  --service s3 \
  --resource arn:aws:s3:::example/report.csv

Limit the matrix to selected actions when you want a shorter result or do not want to fetch the public catalog:

aws-why permissions \
  --service s3 \
  --action GetObject \
  --action PutObject \
  --resource arn:aws:s3:::example/report.csv

Repeat --resource to compare the same action set across up to 25 resources. Use --profile, --region, or --aws-cli with either simulation command. Advanced users with permission to inspect another identity can pass an IAM user or role ARN through --principal.

Both commands use iam:SimulatePrincipalPolicy. Simulation evaluates attached identity policies and supported restrictive controls but is not a live request. It does not fetch resource policies, cannot fully reproduce role session policies or request-time context, and does not include every enforcement layer. Missing condition keys are shown in the result instead of being treated as conclusive runtime evidence.

The command after -- is executed with exactly the supplied argument vector and inherited environment. stdin is inherited and human-mode stdout is streamed. stderr is held in a secure temporary file until the result is classified. On success, aws-why adds no output. It returns the wrapped command's exit code.

In --json mode, successful command output is replayed from secure temporary files. For failures, command output is replaced with one JSON diagnostic object on stdout so CI consumers can parse it reliably. Replay is capped at 64 MiB for stdout and 8 MiB for stderr; analysis retains only the final 1 MiB of stderr.

Each credentialed follow-up AWS call has a five-second timeout by default. Change it with --diagnostic-timeout <seconds>. Public candidate-policy validation is capped at two seconds so an unavailable catalog does not substantially delay the denial report.

Evidence hierarchy

aws-why stops at the first useful source:

  1. AWS GetRequestAuthorizationDetails, when the error contains an authorization ID and the API is supported and permitted.
  2. An encoded authorization message decoded through STS.
  3. The AWS service's denial message.
  4. IAM principal-policy simulation when identity, action, and resource are known.
  5. UNKNOWN.

Every action/resource result has its own evidence source and confidence:

  • verified: AWS returned the authorization evaluation for the live request.
  • reported: the live AWS error explicitly named the cause.
  • simulated: IAM simulation produced the result but did not reproduce the request.
  • incomplete: there was not enough evidence for an exact cause.

Simulation is never presented as proof that the live request will succeed. It can omit live request conditions, resource policies, endpoint policies, role chaining, and other enforcement layers.

AWS context and diagnostic permissions

Diagnostic calls use the same credential environment plus the wrapped command's explicit --profile and --region, but they ignore configured custom endpoints and always use the normal AWS endpoint resolver. Follow-up calls are skipped entirely when the original command uses --no-sign-request or an explicit --endpoint-url.

Generated diagnostics redact encoded authorization payloads, common AWS access-key/token formats, and terminal control characters. The wrapped command still runs with its inherited environment and can print any data it chooses—for example, aws sts get-session-token prints credentials by design. Do not use aws-why to run an untrusted executable, and protect captured CI output as you would ordinary AWS CLI output.

AWS may require these permissions for stronger explanations:

  • sts:GetCallerIdentity
  • iam:GetRequestAuthorizationDetails
  • sts:DecodeAuthorizationMessage
  • iam:SimulatePrincipalPolicy

The can and permissions commands require sts:GetCallerIdentity and iam:SimulatePrincipalPolicy. Policy validation and service-wide permissions make unauthenticated HTTPS requests to servicereference.us-east-1.amazonaws.com; no AWS credentials are sent to that catalog endpoint. Passing one or more explicit --action values skips the catalog request for permissions.

The original command still runs if none of those diagnostic permissions are available; the result simply becomes less specific.

Current scope

The action/resource parser is intentionally optimized for S3, Secrets Manager, KMS, IAM, and STS. AWS authorization IDs are not currently returned by every service or API. Cross-organization details can also be withheld by AWS. In those cases, aws-why reports the known identity/action/resource and says that the exact denial reason is unknown.

aws-why recognizes only a direct aws executable. Shell pipelines and commands such as sh -c 'aws ...' still execute, but follow-up AWS diagnostics are not attempted because their effective AWS context cannot be recovered safely.

Development

cargo fmt --check
cargo test
cargo clippy --all-targets --all-features -- -D warnings
maturin build --release --bindings bin

The end-to-end tests use a temporary fake AWS executable and never contact AWS.

Tagged releases build platform-specific wheels and publish them through PyPI Trusted Publishing. Configure this repository as a trusted publisher for the aws-why PyPI project with environment name pypi, require maintainer approval on that GitHub environment, protect release tags, and push a tag matching the Cargo version. The workflow rejects tags that do not match the Cargo package version, and every third-party action is pinned to an immutable commit.

Metadata

Release files for aws-why 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for aws-why 0.5.0
File
aws_why-0.5.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
aws_why-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details
aws_why-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details
aws_why-0.5.0-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details
aws_why-0.5.0-py3-none-macosx_10_12_x86_64.whl Python 3 none macOS 10.12+ x86-64 Details

Total release size: 9.7 MB

Release files / aws_why-0.5.0-py3-none-win_amd64.whl

Download URL aws_why-0.5.0-py3-none-win_amd64.whl
Size 2.0 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
6cdccfafdb9b29f6c77852eb372eb925b7901c9a7243b1b8e5a16abea84f5adb
BLAKE2b-256 checksum
How to use checksums
49f9308170939290ad2eafc302c4b6ecc205bc6cd4709ccc54c71a8914609a3d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

Release files / aws_why-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL aws_why-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 2.0 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
80e4d9292702d7274227d67df2d0789fa07a838c2a235af4ada2f6c94aac1b8c
BLAKE2b-256 checksum
How to use checksums
7d6060620b23d6a9f2ddf30b795f832fef5bdcbfd59f8d7c9730d3d2e2148c06
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

Release files / aws_why-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL aws_why-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 1.9 MB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
4fcf120bd2fee1391a005b8ae6e53213fd9f730d2ce721f70cd728c22dca3826
BLAKE2b-256 checksum
How to use checksums
12cf3d67a49e6bc874eda57eb8ae2f1254f52ed83988c4934306a338b2273063
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

Release files / aws_why-0.5.0-py3-none-macosx_11_0_arm64.whl

Download URL aws_why-0.5.0-py3-none-macosx_11_0_arm64.whl
Size 1.8 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
1e523ea7414cef2a7a4b4811c9d97ef56a623a02caf105d13c4c3ab78b4c1cbb
BLAKE2b-256 checksum
How to use checksums
13cebe7f4f97b43355f12005a62d15b110cee037e6a730a37631a3222dd4c38c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

Release files / aws_why-0.5.0-py3-none-macosx_10_12_x86_64.whl

Download URL aws_why-0.5.0-py3-none-macosx_10_12_x86_64.whl
Size 1.9 MB
Tags Python 3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
f3642ffe35e976c61920cca027429e33910a505b59fc70f23bd32f3235e95c5e
BLAKE2b-256 checksum
How to use checksums
41ab8e8dc6dd424bc2fe1dba0077f94c47b916c34a1f328a63acb726bce201fe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.5.0 This release

5 release files

0.4.0

5 release files

0.3.1

5 release files

0.3.0

5 release files

0.2.1

5 release files

0.1.0

5 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page