Prove an IAM Permissions Boundary

Simulate IAM policies to prove a proposed widening stays within a boundary.

Introduction

30 Second Summary

A security lead needs evidence before approving broader cloud access. The proof must show exactly where a proposed permission stops.

In this project, you will build a local evidence package that answers whether a proposed policy widening can escape an approved limit in AWS Identity and Access Management (IAM). A permissions boundary sets the approved ceiling for eight decisions tested through policy simulation.

What You'll Build

Your security lead can open readout.html to see a self-contained decision report backed by a tagged local repository.

By the end of this project, you'll have:

  • A six-check evidence trail that distinguishes explicit deny, implicit deny, boundary refusal, and an allowed control.
  • An eight-decision prediction score calculated from frozen model predictions and actual simulator results.
  • A tagged local Git repository whose design records, policy inputs, HTML readout, and NoSuchEntity teardown proof agree.
  • Secret Mission: Run separate IAM Access Analyzer policy checks when your AWS account supports them.

Are there any prerequisites?

You need an activated AWS account with a non-root administrative identity. Git 2.x, an editor, and an AI client must already be available.

Before We Start

Before hands-on work begins, this step locks in an account safety gate: AWS signup may require a valid payment method or temporary verification hold. Activation can take up to 24 hours, so the timed run must wait until the account is active.

Once activated, you will use a non-root administrative AWS identity to answer one question: can local widened.json grant iam:CreatePolicyVersion after the approved permissions boundary is applied?

Establish the Evidence Workspace

The security lead needs evidence tied to an accountable AWS Identity and Access Management (IAM) caller. This workspace starts the evidence trail before design or build work begins.

This setup prepares one local development environment before the timed work begins. You'll open or install Visual Studio Code, Cursor, or Google Antigravity, then verify Git and AWS CLI from that workspace.

In this step, get ready to:
  • Prepare the local toolchain with an IDE, Git, and AWS CLI.
  • Capture the non-root administrative caller response as check 1.
  • Initialize the local evidence repository with one commit and no remote.
Open your IDE and verify the command-line tools

Your evidence package needs an editor for its files plus a terminal for Git and authenticated AWS commands. Start by opening one supported IDE so every later check happens in the same workspace.

✔️ I have an IDE installed

Use the editor you already know so the setup stays focused on the evidence work.

  • Choose Visual Studio Code, Cursor, or Google Antigravity for this project.
  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar. On Linux, open the applications menu.
  • Type the name of your chosen IDE into the search field.
  • Press Enter to launch it.

You should see the editor's welcome or home screen. Your local workspace is ready for the command-line checks.

ⓧ I need to install an IDE

Starting from zero is fine. Choose one editor below and install it from its official download page.

Visual Studio Code

  • Open the official Visual Studio Code download page.
  • Select the installer for your operating system and processor.
  • Open the downloaded installer.
  • Complete the on-screen installation prompts.
  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar. On Linux, open the applications menu.
  • Type Visual Studio Code into the search field.
  • Press Enter to launch it.

You should see the Visual Studio Code welcome screen.

Cursor

  • Open the official Cursor download page.
  • Select the installer for your operating system and processor.
  • Open the downloaded installer.
  • Complete the on-screen installation prompts.
  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar. On Linux, open the applications menu.
  • Type Cursor into the search field.
  • Press Enter to launch it.
  • Complete the Cursor sign-in prompt.

You should see the Cursor welcome screen.

Google Antigravity

  • Open the official Google Antigravity download page.
  • Find the Antigravity IDE (Standalone) download option.
  • Select the installer for your operating system and processor.
  • Open the downloaded installer.
  • Complete the on-screen installation prompts.
  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar. On Linux, open the applications menu.
  • Type Antigravity IDE into the search field.
  • Press Enter to launch it.

You should see the Google Antigravity IDE welcome screen.

That's the first setup hurdle cleared. Your chosen IDE is installed and open for the rest of the project.

IDE not opening?

  • Close the installer before launching the IDE again.
  • Download a fresh installer from the official page if the first installation did not complete.

Ask for help with opening your chosen IDE.

  • Check the existing Git installation by running this command:
git --version

What does this command do?

This command asks Git to report its installed version. It does not change your files or configuration.

You should see a Git 2.x version. That confirms your existing version-control tool is ready for the evidence history.

Git command unavailable?

  • Stop the timed run because Git is an existing prerequisite for this project.
  • Restore access to the Git installation that was available before the project.

Ask for help with restoring the existing Git command.

  • Check the AWS CLI installation by running this command:
aws --version

What does this command do?

This command confirms that your terminal can find AWS CLI. The output also reports whether you have AWS CLI version 1 or version 2.

✔️ I see AWS CLI version 2

If the output begins with aws-cli/2, AWS CLI v2 is installed. Continue without installing anything.

ⓧ I see AWS CLI version 1

AWS CLI version 1 is installed, so replace it with AWS CLI v2 using the official installer for your operating system.

macOS

  • Install AWS CLI v2 by running this official command:
curl -fsSL https://awscli.amazonaws.com/v2/install.sh | bash
  • Start a fresh terminal session so it can detect AWS CLI v2.
  • Confirm the installed version by running this command:
aws --version

You should see output beginning with aws-cli/2.

Still seeing AWS CLI version 1?
  • Close every old terminal session that may still point to AWS CLI version 1.
  • Run the version check from a new terminal session.

Ask for help with finding the AWS CLI v2 executable.

Windows

  • Install AWS CLI v2 from PowerShell by running this official command:
irm https://awscli.amazonaws.com/v2/install.ps1 | iex
  • Start a fresh PowerShell session so it can detect AWS CLI v2.
  • Confirm the installed version by running this command:
aws --version

You should see output beginning with aws-cli/2.

Still seeing AWS CLI version 1?
  • Close every old PowerShell session that may still point to AWS CLI version 1.
  • Run the version check from a new PowerShell session.

Ask for help with finding the AWS CLI v2 executable.

Linux

  • Install AWS CLI v2 by running this official command:
curl -fsSL https://awscli.amazonaws.com/v2/install.sh | bash
  • Start a fresh terminal session so it can detect AWS CLI v2.
  • Confirm the installed version by running this command:
aws --version

You should see output beginning with aws-cli/2.

Still seeing AWS CLI version 1?
  • Close every old terminal session that may still point to AWS CLI version 1.
  • Run the version check from a new terminal session.

Ask for help with finding the AWS CLI v2 executable.

ⓧ Command not found

Your terminal cannot find AWS CLI. Install AWS CLI v2 using the official command for your operating system.

macOS

  • Install AWS CLI v2 by running this official command:
curl -fsSL https://awscli.amazonaws.com/v2/install.sh | bash
  • Start a fresh terminal session so it can detect the installation.
  • Verify the installation by running this command:
aws --version

You should see output beginning with aws-cli/2.

Installation not detected?
  • Close every terminal session that was open during installation.
  • Run the version check from a new terminal session.

Ask for help with finding the AWS CLI installation.

Windows

  • Install AWS CLI v2 from PowerShell by running this official command:
irm https://awscli.amazonaws.com/v2/install.ps1 | iex
  • Start a fresh PowerShell session so it can detect the installation.
  • Verify the installation by running this command:
aws --version

You should see output beginning with aws-cli/2.

Installation not detected?
  • Close every PowerShell session that was open during installation.
  • Run the version check from a new PowerShell session.

Ask for help with finding the AWS CLI installation.

Linux

  • Install AWS CLI v2 by running this official command:
curl -fsSL https://awscli.amazonaws.com/v2/install.sh | bash
  • Start a fresh terminal session so it can detect the installation.
  • Verify the installation by running this command:
aws --version

You should see output beginning with aws-cli/2.

Installation not detected?
  • Close every terminal session that was open during installation.
  • Run the version check from a new terminal session.

Ask for help with finding the AWS CLI installation.

Verify and capture the caller

The caller check uses the credentials already active in your terminal. It proves which AWS identity creates and tests the IAM resources in this project.

Credential setup varies between AWS accounts, so use the non-root administrative profile or session prepared before this project. Your account root user stays outside everyday project work.

Before you run the caller check, which administrative identity ARN do you expect the active session to report?

  • Retrieve the active caller identity by running this command:
aws sts get-caller-identity

What does this command do?

This command identifies the credentials active in your terminal. The complete JSON response becomes check 1 in the evidence package.

A successful caller check returns a three-field JSON response like this:

{
  "UserId": "AIDASAMPLEUSERID",
  "Account": "123456789012",
  "Arn": "arn:aws:iam::123456789012:user/DevAdmin"
}

Your values will differ from this example. A temporary role session can return an assumed-role ARN, but the ARN must identify your approved administrative identity instead of the AWS account root user.

  • Copy the complete caller response from the terminal.
  • Switch to your existing editor.
  • Create the iam-boundary-proof folder on your Desktop using your editor's folder controls.
  • Create the results folder inside iam-boundary-proof using the editor's folder control.

Your editor should now show the empty results folder inside iam-boundary-proof.

  • Create results/01-caller.json using the editor's new-file control.
  • Paste the complete caller response into results/01-caller.json.

The editor should show the complete caller response in results/01-caller.json.

  • Save results/01-caller.json.
  • Inspect the Arn field in the saved response.

The saved ARN should name the non-root administrative identity active in your terminal. This file now supplies the first required evidence output.

Caller identity looks wrong?

  • Stop the project if the ARN identifies the AWS account root user.
  • Restore the approved non-root administrative profile or session for your AWS account.
  • Rerun the caller check before replacing the saved response.

Ask for help with checking the active caller safely.

Create and commit the evidence workspace

The repository needs a task ledger plus a manifest for the six required checks. Each file has a clear folder so the first commit preserves an organized evidence workspace.

  • Create the docs folder inside iam-boundary-proof using the editor's folder control.

Your editor should now show the docs folder beside the existing results folder.

  • Create docs/TASKS.md inside the docs folder using the editor's new-file control.
  • Add the complete handoff task ledger by pasting this content:
# Security lead handoff tasks

- [ ] Setup 6: verify Git and AWS CLI
- [ ] Setup 6: save non-root caller output as check 1
- [ ] Setup 6: initialize local Git repository with no remote
- [ ] Design 8: write requirements, decisions, topology, plan, and source map
- [ ] Design 8: use exactly three AI prompts
- [ ] Design 8: freeze trust, boundary, widening, and eight predictions
- [ ] Build 20: create exactly one policy and one role
- [ ] Build 20: save the unbounded control as check 2
- [ ] Build 20: compare implementation with frozen design
- [ ] Validation 7: attach the boundary
- [ ] Validation 7: save checks 3 through 6
- [ ] Validation 7: calculate the exact prediction match count
- [ ] Validation 7: update the self-contained HTML readout
- [ ] Close-out 4: remove boundary and inspect dependencies
- [ ] Close-out 4: delete policy and role
- [ ] Close-out 4: paste two NoSuchEntity results
- [ ] Close-out 4: commit, tag, and confirm no remote

What does this file track?

The ledger pins each phase to the security lead's acceptance path. It keeps setup, design, build, validation, and teardown work visible without changing their scope.

  • Save docs/TASKS.md.
  • Confirm the editor preview shows all seventeen unchecked tasks.

Task list looks incomplete?

  • Check that every task begins with the Markdown checkbox characters shown above.
  • Restore any phase line that was omitted during the paste.

Ask for help with comparing the task ledger.

  • Create results/check-manifest.md using the editor's new-file control.
  • Add the six-check manifest by pasting this content:
# Required check manifest

1. results/01-caller.json: administrative caller, not account root
2. results/02-baseline.json: widening allowed without boundary
3. results/03-explicit-denies.json: five explicit denies
4. results/04-implicit-deny.json: missing identity allow
5. results/05-boundary-deny.json: AllowedByPermissionsBoundary is false
6. results/06-allowed-control.json: allowed read control

All six files must contain complete pasted command output before teardown. The final tag is created only after teardown evidence also exists.

What does this manifest protect?

The manifest gives every required command response a fixed destination. It also prevents the final evidence tag from being created before teardown proof exists.

  • Save results/check-manifest.md.
  • Confirm the manifest lists checks 1 through 6 in order.

Manifest order does not match?

  • Compare every numbered path with the supplied manifest.
  • Restore the final teardown requirement below the numbered list.

Ask for help with checking the evidence manifest.

✔️ Awesome, I've got everything!

Your task ledger and check manifest are saved in their folders. results/01-caller.json contains your complete runtime response.

ⓧ I'd like to double check the full code

Compare docs/TASKS.md with this complete reference.

# Security lead handoff tasks

- [ ] Setup 6: verify Git and AWS CLI
- [ ] Setup 6: save non-root caller output as check 1
- [ ] Setup 6: initialize local Git repository with no remote
- [ ] Design 8: write requirements, decisions, topology, plan, and source map
- [ ] Design 8: use exactly three AI prompts
- [ ] Design 8: freeze trust, boundary, widening, and eight predictions
- [ ] Build 20: create exactly one policy and one role
- [ ] Build 20: save the unbounded control as check 2
- [ ] Build 20: compare implementation with frozen design
- [ ] Validation 7: attach the boundary
- [ ] Validation 7: save checks 3 through 6
- [ ] Validation 7: calculate the exact prediction match count
- [ ] Validation 7: update the self-contained HTML readout
- [ ] Close-out 4: remove boundary and inspect dependencies
- [ ] Close-out 4: delete policy and role
- [ ] Close-out 4: paste two NoSuchEntity results
- [ ] Close-out 4: commit, tag, and confirm no remote

What should match?

The full task ledger should contain the same seventeen unchecked items in the same order. Its final item confirms the local-only history requirement.

Compare results/check-manifest.md with this complete reference.

# Required check manifest

1. results/01-caller.json: administrative caller, not account root
2. results/02-baseline.json: widening allowed without boundary
3. results/03-explicit-denies.json: five explicit denies
4. results/04-implicit-deny.json: missing identity allow
5. results/05-boundary-deny.json: AllowedByPermissionsBoundary is false
6. results/06-allowed-control.json: allowed read control

All six files must contain complete pasted command output before teardown. The final tag is created only after teardown evidence also exists.

What should match?

The manifest should name the six result files exactly. Its final paragraph should keep the final tag behind the teardown evidence.

Keep the complete runtime caller response in results/01-caller.json.

The files now define the first evidence checkpoint. Git can preserve their exact state without creating a remote copy.

  • Start a terminal from your editor's open iam-boundary-proof workspace.
  • Initialize the local repository by running this command:
git init

What does this command do?

This command creates the local repository metadata inside iam-boundary-proof. It does not create a commit or configure a remote.

You should see confirmation that Git initialized an empty repository inside iam-boundary-proof.

Repository initialized elsewhere?

  • Check that the terminal belongs to the open iam-boundary-proof workspace in your editor.
  • Run the initialization command again from the correct folder.

Ask for help with locating the initialized repository.

  • Verify whether this repository already has a local author name and email by running this command:
git config --local --list

✔️ I see my name and email

You should see user.name= followed by your author name and user.email= followed by your email address. Git is already configured for this repository, so leave these values unchanged.

ⓧ One or both values need fixing

A missing or incorrect entry can give the next commit the wrong author identity. Set both values explicitly so each input has one clear purpose.

  • Set the repository's author name and email with separate inputs by running these commands:
git config --local user.name "[[GIT_AUTHOR_NAME="your full name"]]"
git config --local user.email "[[GIT_AUTHOR_EMAIL="you@example.com"]]"
  • Confirm the saved values by rerunning the check:
git config --local --list

You should now see user.name= with your author name and user.email= with your email address.

Local configuration command failed?

  • Confirm your terminal is inside the iam-boundary-proof repository.
  • Repeat the local configuration check after the repository has been initialized.

Ask for help with configuring the local Git author identity.

Why configure the author locally?

These commands attach an author name plus email to commits in this repository. The settings remain local to this evidence package.

Your next commit now uses the verified local author name and email.

  • Stage the three evidence files by running this command:
git add docs/TASKS.md results/check-manifest.md results/01-caller.json

What does this command stage?

This command stages the task ledger, check manifest, and caller response. Only this named evidence enters the initialization commit.

🙋‍♀️ Pathspec did not match?

The core.ignoreCase=true setting helps Git work with case-insensitive Windows file systems. It cannot create or move a missing file.

  • Confirm that the terminal prompt ends with iam-boundary-proof.
  • Check that all three file paths exactly match the staging command.
  • Save any missing file before repeating the staging command.

Ask for help with fixing the unmatched path.

  • Verify that Git staged exactly the three evidence files by running this command:
git status --short

You should see three lines beginning with A. The paths should be docs/TASKS.md, results/check-manifest.md, and results/01-caller.json.

That is the staging check complete. All three evidence files are ready for the first commit.

  • Create the initialization commit by running this command:
git commit -m "chore: initialize security evidence workspace"

What does this command record?

This command records the staged workspace as the first local evidence snapshot. Its message identifies the commit as setup work.

You should see a commit summary naming the files recorded in the initialization snapshot.

Commit did not complete?

  • Return to the Git author check if the commit cannot find a name or email.
  • Confirm that each named file is saved at the path used by the staging command.
  • Repeat the staging command after correcting a missing file.

Ask for help with fixing the initialization commit.

  • Return to results/01-caller.json in your editor.
  • Confirm the file contains the complete caller response with a non-root administrative ARN.

Before you run the final check, how many commits do you expect to see? What should the remote list contain?

  • Verify the local history plus remote configuration by running these commands:
git log --oneline
git remote

What do these commands prove?

  • The history command shows the compact local commit list.
  • The remote command lists every configured remote repository.

You should see one commit with chore: initialize security evidence workspace. The remote command should print no lines.

History or remote output differs?

  • Confirm the terminal is inside the iam-boundary-proof repository.
  • Review the commit command if no initialization commit appears.
  • Stop before continuing if the remote command lists a configured remote.

Ask for help with checking the local-only Git state.

Strong start. Check 1 now identifies the administrative caller, while the first commit preserves the local workspace with no remote.

Next, you will freeze the security brief, design decisions, policy inputs, and predictions before creating any AWS resource.

Freeze the Security Brief and Predictions

Your local Git repository now records the administrative caller in its first evidence commit. The next goal is to freeze the brief before any AWS Identity and Access Management (IAM) resource exists.

The frozen files become the build's instructions. They prevent the evidence target from quietly shifting after results appear.

In this step, get ready to:
  • Write the requirements brief, decision records, topology, phase plan, and source map.
  • Use exactly three AI prompts for design review, a local widening candidate, and eight decision predictions.
  • Freeze the policy inputs and predictions in a local design commit before the build begins.
Write the design handoff

The design handoff turns the security lead's question into fixed acceptance criteria. Five Markdown documents define the scope, tradeoffs, evidence flow, time limits, and sources.

  • Create docs/requirements.md inside iam-boundary-proof/docs by pasting this content into a new editor file:
# Security lead requirements brief

1. Deliver one project in 45 minutes including setup.
2. Use phase pins of setup 6, design 8, build 20, validation 7, and close-out 4.
3. Protect the full 20-minute build floor.
4. Install only AWS CLI; use the learner's existing Git, editor, and AI client.
5. Use exactly three AI prompts in design and none in build or validation.
6. Create only BoundaryProofCeiling and BoundaryProofRole.
7. Keep widened.json local, pass it to simulate-principal-policy, and never attach it.
8. Paste complete output for all six named checks.
9. Compare eight frozen predictions with eight actual decisions and report matches/8.
10. Treat AllowedByPermissionsBoundary false as the boundary evidence.
11. Do not claim the simulator returns a statement Sid.
12. Keep every graded acceptance criterion within IAM and the policy simulator.
13. Declare the graded path cost as 0.00 USD.
14. Remove the boundary before deleting its policy.
15. Prove role and policy absence with NoSuchEntity.
16. Run teardown before the final evidence commit and local tag.
17. Ship a self-contained HTML readout and keep all design artifacts.

What Does This Brief Lock?

  • The numbered requirements define the evidence package that the security lead must receive.
  • The resource names and six-check manifest keep the later build within the approved scope.
  • The teardown rules make absence evidence a requirement before the final tag.
  • Save docs/requirements.md.

You should see requirements.md under the docs folder in your editor's file sidebar.

  • Create docs/decisions.md inside iam-boundary-proof/docs by pasting this content into a new editor file:
# Decision records

## Direct AWS CLI calls

Decision: Use direct, single-line AWS CLI invocations and editor-authored JSON.
Alternative: Shell scripts and pipelines.
Tradeoff: More manual evidence capture in exchange for the same graded commands across Windows, macOS, and Linux.
Reversal trigger: A later project can add tested automation without changing this evidence model.

## Local Git only

Decision: Keep the handoff in a local repository with no remote.
Alternative: A code host and ticket account.
Tradeoff: No collaboration or off-device backup in exchange for zero new external accounts.
Reversal trigger: The security lead requests team review or durable remote storage.

## Simulator adjudication

Decision: Use simulate-principal-policy and retain raw outputs.
Alternative: Execute live service operations.
Tradeoff: Safe, reproducible modelled decisions instead of proof of live runtime behavior.
Reversal trigger: A controlled integration account and live test plan become available.

## Existing learner tools

Decision: Use whichever editor and AI client the learner already has.
Alternative: Require named vendors.
Tradeoff: Consistent cohort access instead of vendor-specific screenshots or prompts.
Reversal trigger: The cohort standardizes its workstation image.

What Do These Decisions Record?

  • Each record names the chosen approach and the alternative that was left out.
  • Each tradeoff explains what the handoff gains from that choice.
  • Each reversal trigger states when the security lead could justify a different approach.
  • Save docs/decisions.md.

You should see four decision records covering direct CLI calls, local Git, simulator adjudication, and existing learner tools.

  • Create docs/topology.md inside iam-boundary-proof/docs by pasting this first section into a new editor file:
# Evidence topology

## Local evidence

- docs/TASKS.md
- docs/
- intent.md
- trust.json
- boundary.json
- widened.json
- predictions.csv
- results/
- readout.html
- .git/

## Temporary IAM resources

- BoundaryProofCeiling: customer managed policy used as the role's permissions boundary during validation
- BoundaryProofRole: role used as the policy-source ARN

What Does This Topology Separate?

The first section separates local evidence from the two temporary IAM resources. That boundary makes it clear which artifacts remain after AWS teardown.

  • Save docs/topology.md.

You should see the local evidence list followed by the two approved temporary resource names.

  • Append the ordered evidence flow below the BoundaryProofRole line by pasting:
## Ordered flow

1. Save the non-root caller response.
2. Freeze design documents, policies, and predictions.
3. Create the policy and role.
4. Pass widened.json to the simulator before the boundary and save the allowed baseline.
5. Attach BoundaryProofCeiling to BoundaryProofRole as the boundary.
6. Pass widened.json to four guarded simulations and score eight decisions.
7. Remove the boundary, clear dependencies, delete policy, delete role, and prove absence.
8. Commit and tag only after teardown.

widened.json is never attached to any identity.

Why Is the Flow Ordered?

The order preserves evidence provenance from caller verification through teardown. It also keeps widened.json as simulator input without attaching it to an identity.

  • Save docs/topology.md.

You should now see eight ordered stages followed by the rule that widened.json remains unattached.

  • Create docs/build-plan.md inside iam-boundary-proof/docs by pasting this content into a new editor file:
# Phase pins

- Setup: 6 minutes
- Design: 8 minutes
- Build: 20 minutes
- Validation: 7 minutes
- Close-out: 4 minutes
- Total: 45 minutes

No later instruction may borrow from the 20-minute build floor or silently revise these pins.

Why Pin the Phases?

The phase pins reserve the full build window before validation starts. The fixed total keeps setup and close-out inside the same delivery.

  • Save docs/build-plan.md.

You should see five phase allocations that total 45 minutes.

  • Create docs/source-map.md inside iam-boundary-proof/docs by pasting this content into a new editor file:
# Source map

- AWS IAM permissions boundaries and policy evaluation logic
- AWS CLI simulate-principal-policy command reference
- AWS IAM policy simulator limitations
- AWS IAM deletion dependencies and NoSuchEntity errors
- AWS root user guidance
- AWS account signup payment and activation guidance
- AWS IAM pricing
- Jha, Gaiha, Pandey, and Kaicker: Andhra Pradesh TPDS participation measured at 92 percent in the surveyed sample; no biometric-failure inference is made

What Does the Source Map Protect?

The source map records the authority behind the acceptance scope. Its final entry preserves the measured participation figure without turning it into a claim about biometric failure.

  • Save docs/source-map.md.

You should see eight source topics under the source map heading.

AI prompt 1 of 3: This review checks whether the three core design documents answer the security lead's question without expanding the build. The prompt and the complete contents of all three files must be sent together as one message.

  1. In the AI client, start a new message.
  2. Copy the complete design-review prompt below into the message.
  3. Leave the message unsent until you append the three labeled files shown after the prompt.
Act as a solutions-engineering design reviewer. Review the complete contents of docs/requirements.md, docs/decisions.md, and docs/topology.md that follow this prompt.

Check only for conflicts or omissions across these frozen design rules:
- The project lasts 45 minutes and preserves the 20-minute build floor.
- Exactly three AI prompts run during design. No AI prompts run during build or validation.
- Only BoundaryProofCeiling and BoundaryProofRole are created.
- widened.json remains local, is passed only to the IAM policy simulator, and is never attached.
- The simulator provides the adjudication evidence without live service operations.
- Eight predictions are frozen before resource creation and later compared with eight simulator decisions.
- Boundary refusal is proved by AllowedByPermissionsBoundary equal to false.
- Teardown and NoSuchEntity checks occur before the final evidence commit and tag.

Return one of these results:
- PASS if all three documents agree with every rule.
- NEEDS_CHANGES followed by at most five findings if a conflict or omission exists.

Format each finding as File, Conflict, Minimal correction.
Do not rewrite the files. Do not add services, accounts, resources, commands, or acceptance criteria.
  1. Type docs/requirements.md: on a new line beneath the prompt.
  2. Paste the complete contents of docs/requirements.md beneath its label.
  3. Type docs/decisions.md: on a new line beneath the requirements content.
  4. Paste the complete contents of docs/decisions.md beneath its label.
  5. Type docs/topology.md: on a new line beneath the decisions content.
  6. Paste the complete contents of docs/topology.md beneath its label.
  7. Check that the message contains the review prompt followed by all three labels and complete file contents.
  8. Send the completed message once.

If the response is PASS, leave the files unchanged. If it returns NEEDS_CHANGES, compare each finding with the fixed acceptance scope and make only corrections that keep the five design files aligned with the references below.

✔️ Awesome, I've got everything!

Your five design documents are saved. The first AI prompt has challenged the core design without changing its scope.

ⓧ I'd like to double check the full code

Compare your five design documents with these complete references:

# Security lead requirements brief

1. Deliver one project in 45 minutes including setup.
2. Use phase pins of setup 6, design 8, build 20, validation 7, and close-out 4.
3. Protect the full 20-minute build floor.
4. Install only AWS CLI; use the learner's existing Git, editor, and AI client.
5. Use exactly three AI prompts in design and none in build or validation.
6. Create only BoundaryProofCeiling and BoundaryProofRole.
7. Keep widened.json local, pass it to simulate-principal-policy, and never attach it.
8. Paste complete output for all six named checks.
9. Compare eight frozen predictions with eight actual decisions and report matches/8.
10. Treat AllowedByPermissionsBoundary false as the boundary evidence.
11. Do not claim the simulator returns a statement Sid.
12. Keep every graded acceptance criterion within IAM and the policy simulator.
13. Declare the graded path cost as 0.00 USD.
14. Remove the boundary before deleting its policy.
15. Prove role and policy absence with NoSuchEntity.
16. Run teardown before the final evidence commit and local tag.
17. Ship a self-contained HTML readout and keep all design artifacts.
# Decision records

## Direct AWS CLI calls

Decision: Use direct, single-line AWS CLI invocations and editor-authored JSON.
Alternative: Shell scripts and pipelines.
Tradeoff: More manual evidence capture in exchange for the same graded commands across Windows, macOS, and Linux.
Reversal trigger: A later project can add tested automation without changing this evidence model.

## Local Git only

Decision: Keep the handoff in a local repository with no remote.
Alternative: A code host and ticket account.
Tradeoff: No collaboration or off-device backup in exchange for zero new external accounts.
Reversal trigger: The security lead requests team review or durable remote storage.

## Simulator adjudication

Decision: Use simulate-principal-policy and retain raw outputs.
Alternative: Execute live service operations.
Tradeoff: Safe, reproducible modelled decisions instead of proof of live runtime behavior.
Reversal trigger: A controlled integration account and live test plan become available.

## Existing learner tools

Decision: Use whichever editor and AI client the learner already has.
Alternative: Require named vendors.
Tradeoff: Consistent cohort access instead of vendor-specific screenshots or prompts.
Reversal trigger: The cohort standardizes its workstation image.
# Evidence topology

## Local evidence

- docs/TASKS.md
- docs/
- intent.md
- trust.json
- boundary.json
- widened.json
- predictions.csv
- results/
- readout.html
- .git/

## Temporary IAM resources

- BoundaryProofCeiling: customer managed policy used as the role's permissions boundary during validation
- BoundaryProofRole: role used as the policy-source ARN

## Ordered flow

1. Save the non-root caller response.
2. Freeze design documents, policies, and predictions.
3. Create the policy and role.
4. Pass widened.json to the simulator before the boundary and save the allowed baseline.
5. Attach BoundaryProofCeiling to BoundaryProofRole as the boundary.
6. Pass widened.json to four guarded simulations and score eight decisions.
7. Remove the boundary, clear dependencies, delete policy, delete role, and prove absence.
8. Commit and tag only after teardown.

widened.json is never attached to any identity.
# Phase pins

- Setup: 6 minutes
- Design: 8 minutes
- Build: 20 minutes
- Validation: 7 minutes
- Close-out: 4 minutes
- Total: 45 minutes

No later instruction may borrow from the 20-minute build floor or silently revise these pins.
# Source map

- AWS IAM permissions boundaries and policy evaluation logic
- AWS CLI simulate-principal-policy command reference
- AWS IAM policy simulator limitations
- AWS IAM deletion dependencies and NoSuchEntity errors
- AWS root user guidance
- AWS account signup payment and activation guidance
- AWS IAM pricing
- Jha, Gaiha, Pandey, and Kaicker: Andhra Pradesh TPDS participation measured at 92 percent in the surveyed sample; no biometric-failure inference is made
Freeze the policy inputs and predictions

A permissions boundary defines the maximum permissions that an identity policy can grant. It does not grant permissions by itself.

The frozen JSON inputs create cases for an explicit deny, an implicit deny, a boundary refusal, and an allowed control. The CSV file records the predicted decision for each case before the simulator runs.

  • Create trust.json inside iam-boundary-proof by pasting this content into a new editor file:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "TrustEc2WithoutInstanceProfile",
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

What Does This Trust Policy Define?

The policy defines the role's trust relationship under TrustEc2WithoutInstanceProfile. It gives the later role a valid trust policy without creating an instance profile.

  • Save trust.json.

You should see TrustEc2WithoutInstanceProfile as the only statement identifier in the file.

  • Create boundary.json inside iam-boundary-proof by pasting this content into a new editor file:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowReviewedOperationsOnly",
      "Effect": "Allow",
      "Action": [
        "iam:GetRole",
        "iam:ListUsers",
        "iam:DeleteRole",
        "iam:DeleteRolePermissionsBoundary",
        "iam:UpdateAssumeRolePolicy",
        "iam:PutRolePolicy",
        "iam:TagRole"
      ],
      "Resource": "*"
    }
  ]
}

How Does the Boundary Set the Ceiling?

The AllowReviewedOperationsOnly statement allows the reviewed operations through the boundary. The action that creates a new managed-policy version is absent from this ceiling.

  • Save boundary.json.

You should see seven reviewed actions under the single boundary statement.

  • Create intent.md inside iam-boundary-proof by pasting this content into a new editor file:
# Decision intent for the security lead

The proposed policy allows iam:GetRole as the control.

It explicitly denies five privileged role mutations:

- iam:DeleteRole
- iam:DeleteRolePermissionsBoundary
- iam:UpdateAssumeRolePolicy
- iam:PutRolePolicy
- iam:TagRole

It does not allow iam:ListUsers.

It proposes iam:CreatePolicyVersion against the boundary policy. The unbounded control should allow that proposal. After the boundary is applied, the required evidence is AllowedByPermissionsBoundary false.

What Does the Intent Predict?

The intent names the allowed control and five privileged mutations that must be explicitly denied. It also separates a missing identity allow from the boundary refusal of the proposed widening.

  • Save intent.md.

You should see eight intended decisions represented across the control, privileged mutations, missing allow, and widening case.

AI prompt 2 of 3: This review proposes only the local widening policy. The prompt is self-contained and forbids deployment instructions, so send it without appending any files.

  1. In the AI client, start a new message.
  2. Copy the complete widening-policy prompt below into the message.
  3. Send the prompt once without adding file contents or other instructions.
Act as an AWS IAM policy design reviewer. Return one candidate identity policy for a local file named widened.json.

The candidate must:
- Use policy version 2012-10-17.
- Allow iam:GetRole as the read control with Sid AllowExpectedRead.
- Allow iam:CreatePolicyVersion as the proposed widening with Sid AllowBoundaryWidening.
- Explicitly deny iam:DeleteRole, iam:DeleteRolePermissionsBoundary, iam:UpdateAssumeRolePolicy, iam:PutRolePolicy, and iam:TagRole with Sid DenyPrivilegedRoleMutation.
- Leave iam:ListUsers unallowed so it remains an implicit deny.
- Use Resource "*" for every statement.
- Remain local. It will only be passed to aws iam simulate-principal-policy and must never be attached.

Return only valid JSON. Do not include Markdown fences, explanations, AWS CLI commands, deployment commands, or resource-creation instructions.
  1. Confirm the response contains only valid JSON with no Markdown fences, explanations, commands, or deployment instructions.
  2. Compare the returned candidate with docs/requirements.md and intent.md.
  3. Confirm the Allow statements contain iam:GetRole and iam:CreatePolicyVersion.
  4. Confirm the Deny statement contains all five privileged role mutations from intent.md.
  5. Confirm iam:ListUsers does not appear in an Allow statement.
  6. Confirm the three Sid values are AllowExpectedRead, AllowBoundaryWidening, and DenyPrivilegedRoleMutation.
  7. Reject the candidate if any Effect, Action, Resource, or Condition differs from the canonical policy below.
  8. Use the canonical reviewed policy below after any behavioral difference or added instruction.
  1. Create widened.json inside iam-boundary-proof by pasting the canonical reviewed policy below into a new editor file.
  2. Keep the returned AI response open only for comparison with the canonical policy.
  3. Keep unreviewed AI output out of widened.json.
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowExpectedRead",
      "Effect": "Allow",
      "Action": "iam:GetRole",
      "Resource": "*"
    },
    {
      "Sid": "AllowBoundaryWidening",
      "Effect": "Allow",
      "Action": "iam:CreatePolicyVersion",
      "Resource": "*"
    },
    {
      "Sid": "DenyPrivilegedRoleMutation",
      "Effect": "Deny",
      "Action": [
        "iam:DeleteRole",
        "iam:DeleteRolePermissionsBoundary",
        "iam:UpdateAssumeRolePolicy",
        "iam:PutRolePolicy",
        "iam:TagRole"
      ],
      "Resource": "*"
    }
  ]
}

How Does the Widening Create the Test?

  • The read statement creates the allowed control.
  • The widening statement proposes permission to create another managed-policy version.
  • The deny statement explicitly refuses five privileged role mutations.
  • Save widened.json.

You should see AllowExpectedRead, AllowBoundaryWidening, and DenyPrivilegedRoleMutation as the three statement identifiers.

AI prompt 3 of 3: This review predicts the simulator's eight decisions from the frozen boundary and widening. The embedded policies make the prompt self-contained, and freezing the reviewed CSV now turns the later score into evidence instead of hindsight.

  1. In the AI client, start a new message.
  2. Copy the complete decision-prediction prompt below into the message.
  3. Send the prompt once without adding file contents or other instructions.
Act as an AWS IAM policy decision reviewer. Predict the post-boundary simulator decisions from these frozen policies.

Treat widened.json as the identity-policy input and boundary.json as the permissions boundary.

boundary.json:
{"Version":"2012-10-17","Statement":[{"Sid":"AllowReviewedOperationsOnly","Effect":"Allow","Action":["iam:GetRole","iam:ListUsers","iam:DeleteRole","iam:DeleteRolePermissionsBoundary","iam:UpdateAssumeRolePolicy","iam:PutRolePolicy","iam:TagRole"],"Resource":"*"}]}

widened.json:
{"Version":"2012-10-17","Statement":[{"Sid":"AllowExpectedRead","Effect":"Allow","Action":"iam:GetRole","Resource":"*"},{"Sid":"AllowBoundaryWidening","Effect":"Allow","Action":"iam:CreatePolicyVersion","Resource":"*"},{"Sid":"DenyPrivilegedRoleMutation","Effect":"Deny","Action":["iam:DeleteRole","iam:DeleteRolePermissionsBoundary","iam:UpdateAssumeRolePolicy","iam:PutRolePolicy","iam:TagRole"],"Resource":"*"}]}

Return exactly one CSV header and eight rows in this order:
- iam:DeleteRole
- iam:DeleteRolePermissionsBoundary
- iam:UpdateAssumeRolePolicy
- iam:PutRolePolicy
- iam:TagRole
- iam:ListUsers
- iam:CreatePolicyVersion
- iam:GetRole

Use the exact header Action,PredictedDecision.
Use only these exact decision values: allowed, explicitDeny, implicitDeny.
Apply these IAM evaluation rules:
- An applicable explicit deny overrides an allow.
- A missing identity-policy allow produces an implicit deny.
- An identity-policy allow outside the permissions boundary produces an implicit deny.
- An action allowed by both policies is allowed.

Return only valid CSV. Do not include Markdown fences, explanations, AWS CLI commands, deployment commands, or resource-creation instructions.
  1. Confirm the response contains only valid CSV with no Markdown fences, explanations, commands, or deployment instructions.
  2. Confirm the response contains the exact header plus eight data rows.
  3. Confirm the five privileged role mutations use explicitDeny.
  4. Confirm iam:ListUsers uses implicitDeny.
  5. Confirm iam:CreatePolicyVersion uses implicitDeny.
  6. Confirm iam:GetRole uses allowed.
  7. Normalize only differences in CSV quotation marks or row order when the eight actions and decisions remain unchanged.
  8. Use the canonical reviewed rows below if an action is missing, an extra action appears, or a predicted decision differs from intent.md.
  1. Create predictions.csv inside iam-boundary-proof by pasting the canonical reviewed rows below into a new editor file.
  2. Keep the returned AI response open only for comparison with the canonical rows.
  3. Keep unreviewed AI output out of predictions.csv.
Action,PredictedDecision
"iam:DeleteRole","explicitDeny"
"iam:DeleteRolePermissionsBoundary","explicitDeny"
"iam:UpdateAssumeRolePolicy","explicitDeny"
"iam:PutRolePolicy","explicitDeny"
"iam:TagRole","explicitDeny"
"iam:ListUsers","implicitDeny"
"iam:CreatePolicyVersion","implicitDeny"
"iam:GetRole","allowed"

How Will These Predictions Be Scored?

Each row pairs one action with its frozen predicted decision. The later validation phase compares these eight rows with eight simulator decisions and counts exact matches.

  • Save predictions.csv.
  • Compare the policy inputs with docs/requirements.md.
  • Compare the policy inputs with docs/decisions.md.
  • Compare the policy inputs with docs/topology.md.

You should see eight prediction rows beneath the CSV header. Exactly three AI prompts have now been used in the design phase.

✔️ Awesome, I've got everything!

Your policy inputs and eight predictions are frozen. No policy has been attached or deployed.

ⓧ I'd like to double check the full code

Compare your five policy and prediction files with these complete references:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "TrustEc2WithoutInstanceProfile",
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowReviewedOperationsOnly",
      "Effect": "Allow",
      "Action": [
        "iam:GetRole",
        "iam:ListUsers",
        "iam:DeleteRole",
        "iam:DeleteRolePermissionsBoundary",
        "iam:UpdateAssumeRolePolicy",
        "iam:PutRolePolicy",
        "iam:TagRole"
      ],
      "Resource": "*"
    }
  ]
}
# Decision intent for the security lead

The proposed policy allows iam:GetRole as the control.

It explicitly denies five privileged role mutations:

- iam:DeleteRole
- iam:DeleteRolePermissionsBoundary
- iam:UpdateAssumeRolePolicy
- iam:PutRolePolicy
- iam:TagRole

It does not allow iam:ListUsers.

It proposes iam:CreatePolicyVersion against the boundary policy. The unbounded control should allow that proposal. After the boundary is applied, the required evidence is AllowedByPermissionsBoundary false.
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowExpectedRead",
      "Effect": "Allow",
      "Action": "iam:GetRole",
      "Resource": "*"
    },
    {
      "Sid": "AllowBoundaryWidening",
      "Effect": "Allow",
      "Action": "iam:CreatePolicyVersion",
      "Resource": "*"
    },
    {
      "Sid": "DenyPrivilegedRoleMutation",
      "Effect": "Deny",
      "Action": [
        "iam:DeleteRole",
        "iam:DeleteRolePermissionsBoundary",
        "iam:UpdateAssumeRolePolicy",
        "iam:PutRolePolicy",
        "iam:TagRole"
      ],
      "Resource": "*"
    }
  ]
}
Action,PredictedDecision
"iam:DeleteRole","explicitDeny"
"iam:DeleteRolePermissionsBoundary","explicitDeny"
"iam:UpdateAssumeRolePolicy","explicitDeny"
"iam:PutRolePolicy","explicitDeny"
"iam:TagRole","explicitDeny"
"iam:ListUsers","implicitDeny"
"iam:CreatePolicyVersion","implicitDeny"
"iam:GetRole","allowed"
Commit and verify the frozen design

A local design commit creates the provenance line between planning and implementation. The build can now be judged against files that existed before either temporary IAM resource.

Before you run the check, which commit and design files do you expect the history to confirm?

  • Seal the frozen package in Git by running these commands:
git add docs/TASKS.md docs intent.md trust.json boundary.json widened.json predictions.csv
git commit -m "docs: freeze security brief design and predictions"
git log --oneline --decorate

What Do These Commands Prove?

  • The staging command selects the existing task list plus every new design artifact.
  • The commit records the frozen brief, policy inputs, and predictions before the build starts.
  • The history command shows the design commit above the initialization commit.

You should see docs: freeze security brief design and predictions as the newest commit. The design files now exist in history before any resource-creation command has run.

  • Confirm the repository still has no remote by running:
git remote

You should see no output. That empty result confirms the evidence repository remains local.

The design gate is complete. Your next step uses the frozen inputs to create the two temporary IAM resources and capture the unbounded control.

Build the Unbounded Control

Your design phase froze the security lead's brief in local Git. It also sealed the policy inputs before any AWS resource existed.

The next question is whether the local widening can grant iam:CreatePolicyVersion while no approved permissions boundary participates in AWS Identity and Access Management (IAM). The unbounded control gives the security lead a before-state for the later boundary decision.

In this step, get ready to:
  • Create the frozen BoundaryProofCeiling policy.
  • Create the frozen BoundaryProofRole role.
  • Capture the allowed unbounded control as check 2.
Create the frozen IAM resources

The frozen boundary.json file defines the customer managed policy. The frozen trust.json file defines which service can assume the role.

The AWS CLI returns a runtime ARN for each resource. Those identifiers connect the local evidence to the exact resources used by the simulator.

  • Create the BoundaryProofCeiling policy from boundary.json by running this command:
aws iam create-policy --policy-name BoundaryProofCeiling --policy-document file://boundary.json --output json --no-cli-pager

What does this command do?

  • The command creates one customer managed policy named BoundaryProofCeiling.
  • The file://boundary.json input keeps the created policy aligned with the frozen local document.
  • The returned policy ARN identifies the exact resource that the later simulation targets.
  • Locate the Arn field inside the Policy object in the policy-creation response.
  • Record that managed-policy ARN here: your BoundaryProofCeiling ARN.
  • Confirm that the recorded value ends with :policy/BoundaryProofCeiling.
  • Use the ARN ending with :role/BoundaryProofRole only for ROLE_ARN.

You should see a JSON response for a customer managed policy named BoundaryProofCeiling.

Policy creation failed?

Check that your administrative AWS session remains active. Confirm that boundary.json is inside the iam-boundary-proof folder used by your terminal.

help me troubleshoot the BoundaryProofCeiling creation command.

  • Create the BoundaryProofRole role from trust.json by running this command:
aws iam create-role --role-name BoundaryProofRole --assume-role-policy-document file://trust.json --output json --no-cli-pager

What does this command do?

  • The command creates one role named BoundaryProofRole.
  • The file://trust.json input applies the frozen trust policy.
  • Role creation establishes trust only. No permissions boundary or permissions policy is added by this command.
  • Record the returned role ARN here: your BoundaryProofRole ARN.

You should see a JSON response for a role named BoundaryProofRole.

Role creation failed?

Confirm that trust.json is inside the iam-boundary-proof folder. Check that your session can create IAM roles.

help me troubleshoot the BoundaryProofRole creation command.

The build record preserves the runtime identifiers beside the fixed resource names. Its final configuration marker stays open until the boundary is attached in the next step.

  • Create docs/build-record.md inside the iam-boundary-proof folder using your editor from earlier.
  • Copy this frozen template into docs/build-record.md:
# Build record

Policy name: BoundaryProofCeiling
Policy ARN: OWED_UNTIL_RUN
Role name: BoundaryProofRole
Role ARN: OWED_UNTIL_RUN

Configuration evidence after boundary attachment:

OWED_UNTIL_RUN

The role has no attached managed permissions policy and no inline permissions policy. widened.json remains local simulator input only.

How does the build record work?

The first two runtime markers reserve space for the policy ARN and role ARN you just recorded. The final marker belongs to the boundary configuration evidence captured in the next step.

The closing statement records the controlled build state. The local widened.json file remains simulator input only.

  • Replace the policy ARN marker with your BoundaryProofCeiling ARN.
  • Replace the role ARN marker with your BoundaryProofRole ARN.
  • Keep the configuration evidence marker as OWED_UNTIL_RUN.
  • Save docs/build-record.md.

✔️ Awesome, I've got everything!

Your build record now contains the actual policy ARN and role ARN. The configuration evidence marker remains ready for the next step.

ⓧ I'd like to double check the full code

Compare the fixed lines in docs/build-record.md with this template. Your first two markers should already contain the runtime ARNs you recorded.

# Build record

Policy name: BoundaryProofCeiling
Policy ARN: OWED_UNTIL_RUN
Role name: BoundaryProofRole
Role ARN: OWED_UNTIL_RUN

Configuration evidence after boundary attachment:

OWED_UNTIL_RUN

The role has no attached managed permissions policy and no inline permissions policy. widened.json remains local simulator input only.

How to compare this file

Keep every fixed line unchanged. Replace only the first two runtime markers with the returned ARNs.

Run the unbounded control

An unbounded control isolates the local widening before an approved ceiling participates. The simulator receives widened.json as policy input without attaching that file to the role.

At this point, BoundaryProofRole has its frozen trust policy only. No boundary or permissions policy has been added.

  • Confirm that the command below automatically displays your BoundaryProofRole ARN for --policy-source-arn.
  • Confirm that the command below automatically displays your BoundaryProofCeiling ARN for --resource-arns.

Before you run the control, do you think iam:CreatePolicyVersion succeeds or fails while no boundary participates?

  • Choose your platform below, then run the unbounded control in your existing terminal.

macOS

aws iam simulate-principal-policy --policy-source-arn [[ROLE_ARN="your BoundaryProofRole ARN"]] --policy-input-list "$(cat widened.json)" --action-names iam:CreatePolicyVersion --resource-arns [[POLICY_ARN="your BoundaryProofCeiling ARN"]] --output json --no-cli-pager

Windows

aws iam simulate-principal-policy --policy-source-arn [[ROLE_ARN="your BoundaryProofRole ARN"]] --policy-input-list (Get-Content -Raw widened.json) --action-names iam:CreatePolicyVersion --resource-arns [[POLICY_ARN="your BoundaryProofCeiling ARN"]] --output json --no-cli-pager

Linux

aws iam simulate-principal-policy --policy-source-arn [[ROLE_ARN="your BoundaryProofRole ARN"]] --policy-input-list "$(cat widened.json)" --action-names iam:CreatePolicyVersion --resource-arns [[POLICY_ARN="your BoundaryProofCeiling ARN"]] --output json --no-cli-pager

What does this command test?

  • The --policy-source-arn value identifies BoundaryProofRole as the simulated principal.
  • widened.json remains a policy object. Your shell reads its contents as one string, which becomes the only member of --policy-input-list.
  • The --resource-arns value targets the created BoundaryProofCeiling policy.
  • The simulator models the authorization decision without making the requested service operation.

You will see an EvalDecision value of allowed. That allowed result is the intended shortfall in the unbounded control.

The result proves that the local widening can grant iam:CreatePolicyVersion while no approved ceiling participates. It does not perform a live policy-version operation.

  • Select the complete JSON response in the terminal.
  • Copy the selected response.
  • Create results/02-baseline.json inside the iam-boundary-proof folder using your editor from earlier.
  • Paste the complete response into results/02-baseline.json.

Your editor should now show the complete simulator response in results/02-baseline.json.

  • Save results/02-baseline.json.

The saved response should identify iam:CreatePolicyVersion with an allowed decision against your policy ARN.

Baseline simulation failed?

If the CLI rejects PolicyInputList, confirm that you used the command for your platform. Do not pass file://widened.json to this list parameter because widened.json is a policy object, while file input for this parameter must be an array of policy strings.

help me troubleshoot the unbounded IAM simulation.

Verify and commit the build

The frozen design now acts as the acceptance test for this build. The implementation must preserve the two resource names and the unattached local widening.

  • Compare the resource names in docs/build-record.md with docs/topology.md.
  • Confirm that the policy ARN in docs/build-record.md matches the policy creation response.
  • Confirm that the role ARN in docs/build-record.md matches the role creation response.
  • Review the role creation response to confirm that no permissions boundary was added.
  • Confirm that no command attached widened.json or another permissions policy to the role.
  • Mark the three Build 20 items as complete in TASKS.md.
  • Save TASKS.md.
  • Commit the build record and baseline evidence by running these commands:
git add docs/TASKS.md docs/build-record.md results/02-baseline.json
git commit -m "build: create frozen IAM control case"

What do these commands preserve?

The first command stages the updated task list plus both build artifacts. The second command records the build-control checkpoint in local Git.

No remote is involved. The evidence remains inside the local repository.

🙋‍♀️ Commit did not complete?

Confirm that all three files are saved inside iam-boundary-proof. Check that your terminal remains inside that repository.

help me troubleshoot the build-control Git commit.

Before you check the repository, which commit and simulator decision do you expect the evidence to show?

  • Verify the newest local commit by running this command:
git log --oneline

What should the history show?

The newest line should contain build: create frozen IAM control case. Earlier setup and design commits remain below it.

  • Inspect results/02-baseline.json to confirm that the complete response contains the allowed decision.
  • Inspect docs/build-record.md to confirm that both runtime ARNs match the created resources.

That is the build baseline secured. The repository now proves that the widening is allowed before the approved boundary participates.

Next, you will apply the approved ceiling and adjudicate all eight frozen predictions against simulator evidence.

Apply the Boundary and Adjudicate

Your unbounded control is preserved in Git. It proves the proposed widening can return allowed before the approved ceiling applies.

That baseline does not answer whether the permissions boundary blocks the widening. This validation phase applies the ceiling before testing every frozen prediction.

In this step, get ready to:
  • Attach BoundaryProofCeiling to BoundaryProofRole as its permissions boundary.
  • Capture four guarded simulator responses.
  • Publish the exact eight-decision score in the evidence readout.
Apply the permissions boundary

A permissions boundary sets the maximum permissions an identity policy can grant. It grants no permissions by itself.

  • Use BoundaryProofRole for --role-name.
  • Confirm that --permissions-boundary displays the managed-policy ARN ending with :policy/BoundaryProofCeiling.
  • Replace the boundary value if it ends with :role/BoundaryProofRole because that value identifies the role.
  • Apply the approved ceiling from the existing terminal by running this command:
aws iam put-role-permissions-boundary --role-name BoundaryProofRole --permissions-boundary [[POLICY_ARN="your BoundaryProofCeiling ARN"]] --no-cli-pager

What does this command do?

--role-name identifies BoundaryProofRole as the role receiving the boundary. --permissions-boundary accepts the managed-policy ARN for BoundaryProofCeiling, which ends with :policy/BoundaryProofCeiling.

The role now has its approved boundary. A fresh role read provides configuration evidence for the handoff.

  • Retrieve the role configuration by running this command:
aws iam get-role --role-name BoundaryProofRole --output json --no-cli-pager

What does this command prove?

The returned role metadata records the permissions boundary attached to BoundaryProofRole. This response proves the ceiling is part of the role configuration before adjudication begins.

  • Open docs/build-record.md in your existing editor.
  • Find the Configuration evidence after boundary attachment: heading.
  • Select only the OWED_UNTIL_RUN marker directly below that heading.
  • Replace the marker with the entire JSON response from the preceding aws iam get-role command, from the opening { to the closing }.
  • Save docs/build-record.md.

In the pasted JSON, you should see Role.PermissionsBoundary.PermissionsBoundaryArn ending with :policy/BoundaryProofCeiling. This confirms that the role references the managed policy as its permissions boundary.

Boundary configuration missing?

An ARN ending with :role/BoundaryProofRole identifies the role, so AWS rejects it as the --permissions-boundary value. Use the Policy.Arn value recorded in Step 3, which ends with :policy/BoundaryProofCeiling.

Use help me diagnose the missing role boundary if the configuration still does not appear.

Capture the four guarded checks

The IAM policy simulator now tests the frozen widening against the boundary. Each platform command reads widened.json as one string for --policy-input-list, while the file stays an ordinary policy object.

  • Confirm that the saved role ARN from Step 3 appears here: your BoundaryProofRole ARN.
  • Confirm that the saved boundary policy ARN from Step 3 appears here: your BoundaryProofCeiling ARN.

Check 3: Five explicit denies.

Before you run this check, which decision in predictions.csv should appear for each privileged role mutation?

  • Run check 3 with the autofilled role ARN by running this command:

macOS

aws iam simulate-principal-policy --policy-source-arn [[ROLE_ARN="your BoundaryProofRole ARN"]] --policy-input-list "$(cat widened.json)" --action-names iam:DeleteRole iam:DeleteRolePermissionsBoundary iam:UpdateAssumeRolePolicy iam:PutRolePolicy iam:TagRole --output json --no-cli-pager

Windows

aws iam simulate-principal-policy --policy-source-arn [[ROLE_ARN="your BoundaryProofRole ARN"]] --policy-input-list (Get-Content -Raw widened.json) --action-names iam:DeleteRole iam:DeleteRolePermissionsBoundary iam:UpdateAssumeRolePolicy iam:PutRolePolicy iam:TagRole --output json --no-cli-pager

Linux

aws iam simulate-principal-policy --policy-source-arn [[ROLE_ARN="your BoundaryProofRole ARN"]] --policy-input-list "$(cat widened.json)" --action-names iam:DeleteRole iam:DeleteRolePermissionsBoundary iam:UpdateAssumeRolePolicy iam:PutRolePolicy iam:TagRole --output json --no-cli-pager

What does check 3 prove?

  • The role ARN supplies the principal being modelled.
  • The local widened.json file supplies the unattached identity-policy proposal.
  • The simulator loads the BoundaryProofCeiling permissions boundary already attached to the role.
  • The five requested actions test the frozen privileged-mutation refusal.

You should see all five named actions return explicitDeny. The matching explicit refusal overrides any applicable allow.

  • Create results/03-explicit-denies.json in the existing editor with the complete terminal response as its contents.
  • Save results/03-explicit-denies.json.
  • Locate each returned StartPosition value in the saved response.
  • Locate each corresponding EndPosition value in the saved response.
  • Compare those positions with the unchanged statements in widened.json.

Why map source positions?

The simulator can return source positions for matching policy statements. Those positions let you trace the decision back to the unchanged local JSON.

The simulator does not return the local statement Sid as a matched-statement field. Treat DenyPrivilegedRoleMutation as the author's local label only.

🙋‍♀️ Explicit-deny check failed?

Confirm that widened.json still contains the five frozen privileged actions. Check that the file remains valid JSON.

Use help me diagnose the explicit-deny simulation if any decision differs.

Check 4: Missing identity allow.

Before you run this check, what should happen when widened.json contains no matching allow for iam:ListUsers?

  • Run check 4 with the autofilled role ARN by running this command:

macOS

aws iam simulate-principal-policy --policy-source-arn [[ROLE_ARN="your BoundaryProofRole ARN"]] --policy-input-list "$(cat widened.json)" --action-names iam:ListUsers --output json --no-cli-pager

Windows

aws iam simulate-principal-policy --policy-source-arn [[ROLE_ARN="your BoundaryProofRole ARN"]] --policy-input-list (Get-Content -Raw widened.json) --action-names iam:ListUsers --output json --no-cli-pager

Linux

aws iam simulate-principal-policy --policy-source-arn [[ROLE_ARN="your BoundaryProofRole ARN"]] --policy-input-list "$(cat widened.json)" --action-names iam:ListUsers --output json --no-cli-pager

What does check 4 prove?

The boundary includes iam:ListUsers within its ceiling. The local widening provides no identity allow for that action.

This isolates the default refusal caused by a missing matching allow.

You should see iam:ListUsers return implicitDeny. The boundary cannot grant the missing identity permission.

  • Create results/04-implicit-deny.json in the existing editor with the complete terminal response as its contents.
  • Save results/04-implicit-deny.json.

Check 4 now preserves the ordinary implicit-deny case separately from the explicit refusals.

Check 5: Boundary refusal.

Before you run this check, do you expect the proposed widening to escape the approved ceiling?

  • Run check 5 with the autofilled role and boundary policy ARNs by running this command:

macOS

aws iam simulate-principal-policy --policy-source-arn [[ROLE_ARN="your BoundaryProofRole ARN"]] --policy-input-list "$(cat widened.json)" --action-names iam:CreatePolicyVersion --resource-arns [[POLICY_ARN="your BoundaryProofCeiling ARN"]] --output json --no-cli-pager

Windows

aws iam simulate-principal-policy --policy-source-arn [[ROLE_ARN="your BoundaryProofRole ARN"]] --policy-input-list (Get-Content -Raw widened.json) --action-names iam:CreatePolicyVersion --resource-arns [[POLICY_ARN="your BoundaryProofCeiling ARN"]] --output json --no-cli-pager

Linux

aws iam simulate-principal-policy --policy-source-arn [[ROLE_ARN="your BoundaryProofRole ARN"]] --policy-input-list "$(cat widened.json)" --action-names iam:CreatePolicyVersion --resource-arns [[POLICY_ARN="your BoundaryProofCeiling ARN"]] --output json --no-cli-pager

What does check 5 prove?

The proposed widening allows iam:CreatePolicyVersion against the managed-policy ARN. The approved boundary omits that action from its maximum permission set.

The decisive field is PermissionsBoundaryDecisionDetail.AllowedByPermissionsBoundary.

You should see implicitDeny for the action. You should also see AllowedByPermissionsBoundary equal to false.

  • Create results/05-boundary-deny.json in the existing editor with the complete terminal response as its contents.
  • Save results/05-boundary-deny.json.
  • Confirm that the saved response retains AllowedByPermissionsBoundary as false.

Boundary field missing or true?

--policy-source-arn loads the permissions boundary attached to BoundaryProofRole. Confirm that the earlier role response shows a boundary ARN ending with :policy/BoundaryProofCeiling.

Leave --permissions-boundary-policy-input-list out of this command. Passing the normal policy object in boundary.json to that list parameter causes the AWS CLI to parse the file with the wrong shape.

Use help me diagnose the boundary-refusal evidence if the field still differs.

Check 6: Allowed control.

Before you run the final guarded check, what should happen when both the local widening and the boundary allow iam:GetRole?

  • Run check 6 with the autofilled role ARN in both simulator options by running this command:

macOS

aws iam simulate-principal-policy --policy-source-arn [[ROLE_ARN="your BoundaryProofRole ARN"]] --policy-input-list "$(cat widened.json)" --action-names iam:GetRole --resource-arns [[ROLE_ARN="your BoundaryProofRole ARN"]] --output json --no-cli-pager

Windows

aws iam simulate-principal-policy --policy-source-arn [[ROLE_ARN="your BoundaryProofRole ARN"]] --policy-input-list (Get-Content -Raw widened.json) --action-names iam:GetRole --resource-arns [[ROLE_ARN="your BoundaryProofRole ARN"]] --output json --no-cli-pager

Linux

aws iam simulate-principal-policy --policy-source-arn [[ROLE_ARN="your BoundaryProofRole ARN"]] --policy-input-list "$(cat widened.json)" --action-names iam:GetRole --resource-arns [[ROLE_ARN="your BoundaryProofRole ARN"]] --output json --no-cli-pager

What does check 6 prove?

The identity-policy input allows iam:GetRole. The boundary also allows that action.

This control proves the test setup can return a positive decision when both policy layers agree.

You should see iam:GetRole return allowed. The allowed control completes the six-check manifest.

  • Create results/06-allowed-control.json in the existing editor with the complete terminal response as its contents.
  • Save results/06-allowed-control.json.
  • Inspect all six paths listed in results/check-manifest.md.

All six required files should now contain complete command responses. Checks 3 through 6 distinguish four separate decision rules.

Allowed control returned a denial?

Confirm that both frozen JSON files include iam:GetRole in an Allow statement. Confirm that the resource ARN belongs to BoundaryProofRole.

Use help me diagnose the allowed-control simulation if the control still returns a denial.

Publish the adjudication score

The raw responses now replace prediction with evidence. Scoring compares each frozen row with its corresponding simulator decision.

  • Create results/actual.csv in the existing editor.
  • Add this frozen scoring header:
Action,PredictedDecision,ActualDecision,Match,DecisionRule,BoundaryAllowed,ReturnedSourcePosition

What does this scoring table capture?

Each row links one frozen prediction to one simulator result. The final columns preserve the deciding rule plus any relevant boundary value or returned source position.

  • Add one row for each of the eight actions in predictions.csv.
  • Copy each actual decision from the corresponding saved simulator response.
  • Enter yes in the match column when the actual decision equals the frozen prediction.
  • Record the exact runtime source positions for the five explicit-deny rows.
  • Record the returned source position for the allowed control.
  • Enter none for the ordinary implicit-deny source position.
  • Enter none expected for boundary refusal for the boundary-refusal source position.
  • Save results/actual.csv.

Count the rows marked yes. The eight frozen predictions should produce an exact score of 8/8.

  • Create results/scored.md with the calculated score by adding this content:
# Prediction score

Exact match count: 8/8

The count is calculated from the eight rows in actual.csv after all guarded simulator output is pasted. It is not estimated from the model's confidence.

Why calculate the score from the rows?

The score comes from eight observable comparisons. It does not depend on the AI client's confidence or wording.

  • Save results/scored.md.
  • Confirm that its count matches the eight rows in results/actual.csv.

The security lead also needs a readout that opens locally with no network dependency. The first chunk creates the complete evidence table plus its limitations.

  • Create readout.html in the iam-boundary-proof folder with this content:
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>IAM Boundary Decision Proof</title>
</head>
<body>
<main>
  <h1>IAM Boundary Decision Proof</h1>
  <p>Audience: Security lead</p>
  <p class="score">Simulator-confirmed predictions: 8/8</p>
  <table>
    <thead><tr><th>Case</th><th>Decision rule</th><th>Actual result</th></tr></thead>
    <tbody>
      <tr><td>Five privileged role mutations</td><td>Matching explicit Deny</td><td>explicitDeny</td></tr>
      <tr><td>iam:ListUsers</td><td>No matching identity Allow</td><td>implicitDeny</td></tr>
      <tr><td>iam:CreatePolicyVersion</td><td>AllowedByPermissionsBoundary false</td><td>implicitDeny</td></tr>
      <tr><td>iam:GetRole</td><td>Identity Allow and boundary Allow</td><td>allowed</td></tr>
    </tbody>
  </table>
  <section class="limit">
    <h2>Limits and tradeoffs</h2>
    <p>The policy simulator does not make a live service request, and AWS states that results can differ from the live environment.</p>
    <p>No resource-based policy was supplied. No service control policy, cross-account trust, or separate human console session was tested.</p>
    <p>The proof trades those wider runtime questions for a reproducible, no-side-effect decision over the policy inputs captured in this repository.</p>
  </section>
</main>
</body>
</html>

What does the readout show?

The table reports all four adjudication categories. Its score matches results/scored.md.

The limits distinguish modelled authorization evidence from live service behavior. They also record the policy contexts that remain outside this proof.

  • Save readout.html.
  • Locate readout.html in Finder on macOS or File Explorer on Windows.
  • Double-click readout.html to open it in your browser.

You should see the title, the 8/8 score, four evidence rows, plus a limits section. The page works from the local file alone.

Readout missing content?

Confirm that the browser opened the readout.html file inside iam-boundary-proof. Check that the file ends with the closing </html> tag.

Use help me fix the local evidence readout if the table remains incomplete.

Embedded styling keeps the handoff self-contained. The browser reads every visual rule directly from readout.html.

  • In readout.html, locate the <title>IAM Boundary Decision Proof</title> line.
  • Insert this style block immediately below that line:
  <style>
    body { font-family: system-ui, sans-serif; max-width: 1080px; margin: 2rem auto; padding: 0 1rem; color: #172033; background: #f3f6fa; }
    main { background: #ffffff; padding: 2rem; border-radius: 12px; }
    table { width: 100%; border-collapse: collapse; }
    th, td { border: 1px solid #cbd5e1; padding: 0.6rem; text-align: left; }
    th { background: #e2e8f0; }
    .score { font-size: 2rem; font-weight: 700; color: #075985; }
    .limit { border-left: 4px solid #b45309; background: #fff7ed; padding: 1rem; }
  </style>

What does the embedded CSS do?

The styles separate the evidence table from the limitations panel. Every rule remains inside the same HTML file.

This keeps the readout portable for an offline security handoff.

  • Save readout.html.
  • Refresh the local readout in your browser.

You should see a white evidence panel on a pale background. The score should appear larger than the table text.

Readout still unstyled?

Confirm that the complete <style> block sits inside <head> before the closing </head> tag. Save the file before refreshing the browser.

Use help me fix the embedded readout styles if the page remains unstyled.

✔️ Awesome, I've got everything!

Your saved readout should show the same 8/8 count as results/scored.md.

ⓧ I'd like to double check the full code

Compare your complete readout.html with this reference:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>IAM Boundary Decision Proof</title>
  <style>
    body { font-family: system-ui, sans-serif; max-width: 1080px; margin: 2rem auto; padding: 0 1rem; color: #172033; background: #f3f6fa; }
    main { background: #ffffff; padding: 2rem; border-radius: 12px; }
    table { width: 100%; border-collapse: collapse; }
    th, td { border: 1px solid #cbd5e1; padding: 0.6rem; text-align: left; }
    th { background: #e2e8f0; }
    .score { font-size: 2rem; font-weight: 700; color: #075985; }
    .limit { border-left: 4px solid #b45309; background: #fff7ed; padding: 1rem; }
  </style>
</head>
<body>
<main>
  <h1>IAM Boundary Decision Proof</h1>
  <p>Audience: Security lead</p>
  <p class="score">Simulator-confirmed predictions: 8/8</p>
  <table>
    <thead><tr><th>Case</th><th>Decision rule</th><th>Actual result</th></tr></thead>
    <tbody>
      <tr><td>Five privileged role mutations</td><td>Matching explicit Deny</td><td>explicitDeny</td></tr>
      <tr><td>iam:ListUsers</td><td>No matching identity Allow</td><td>implicitDeny</td></tr>
      <tr><td>iam:CreatePolicyVersion</td><td>AllowedByPermissionsBoundary false</td><td>implicitDeny</td></tr>
      <tr><td>iam:GetRole</td><td>Identity Allow and boundary Allow</td><td>allowed</td></tr>
    </tbody>
  </table>
  <section class="limit">
    <h2>Limits and tradeoffs</h2>
    <p>The policy simulator does not make a live service request, and AWS states that results can differ from the live environment.</p>
    <p>No resource-based policy was supplied. No service control policy, cross-account trust, or separate human console session was tested.</p>
    <p>The proof trades those wider runtime questions for a reproducible, no-side-effect decision over the policy inputs captured in this repository.</p>
  </section>
</main>
</body>
</html>

What should match?

The score, decision rows, embedded styles, plus all five stated limitation areas should match this reference. No external asset belongs in the file.

The adjudication artifacts now agree on the runtime score. The commit seals this validation phase without creating the final evidence tag.

Before you commit, what message should appear at the top of the local history?

  • Commit the completed adjudication by running these commands:
git add docs/TASKS.md docs/build-record.md results readout.html
git commit -m "test: adjudicate eight frozen predictions"

What do these commands record?

The first command stages the updated checklist, configuration evidence, raw results, score, plus the HTML readout. The second records them as the adjudication commit.

Before you inspect the history, should the final v1.0.0 tag exist yet?

  • Inspect the decorated local history by running this command:
git log --oneline --decorate

What does the history prove?

The newest line should show test: adjudicate eight frozen predictions at HEAD. The final tag should still be absent because teardown has not happened.

🙋‍♀️ Commit or score does not match?

Inspect the eight rows in results/actual.csv before changing the score. Every actual decision must come from a saved simulator response.

Use help me reconcile the adjudication evidence if the artifacts disagree.

Your security lead now has a boundary-backed adjudication with six raw checks plus an 8/8 score. Next, you will remove both temporary AWS resources before sealing the final evidence.

Tear Down and Seal the Evidence

Your eight scored decisions now answer the security lead's authorization question. The temporary resources in AWS Identity and Access Management (IAM) still exist.

The close-out must remove the permissions boundary before deleting its policy. Fresh absence reads must succeed before Git seals the evidence.

In this step, get ready to:
  • Remove the role's permissions boundary.
  • Delete both temporary IAM resources after dependency checks prove they are clear.
  • Seal the post-teardown evidence with a local Git tag.
Remove the boundary and inspect dependencies

BoundaryProofCeiling cannot be deleted while an identity still uses it as a boundary. The dependency checks also catch any attachment or policy version that drifted from the frozen design.

  • Confirm that the saved boundary policy ARN from the previous step appears here: your BoundaryProofCeiling ARN.
  • Remove the role boundary and inspect every deletion dependency by running these commands in the terminal from earlier:
aws iam delete-role-permissions-boundary --role-name BoundaryProofRole --no-cli-pager
aws iam list-attached-role-policies --role-name BoundaryProofRole --output json --no-cli-pager
aws iam list-entities-for-policy --policy-arn [[POLICY_ARN="your BoundaryProofCeiling ARN"]] --policy-usage-filter PermissionsBoundary --output json --no-cli-pager
aws iam list-policy-versions --policy-arn [[POLICY_ARN="your BoundaryProofCeiling ARN"]] --output json --no-cli-pager

Why does this order matter?

  • The first command removes BoundaryProofCeiling from BoundaryProofRole before either resource is deleted.
  • The second command checks for managed policies attached to the role.
  • The third command checks whether another identity still uses BoundaryProofCeiling as a permissions boundary.
  • The fourth command checks whether BoundaryProofCeiling has any non-default policy versions.

You should see no managed policy attached to BoundaryProofRole.

You should also see no entity using BoundaryProofCeiling as a permissions boundary. The policy should have only its default version.

Seeing dependency drift?

A returned attachment or non-default version means the implementation diverged from the frozen design. Clear every returned dependency before attempting deletion.

A remaining permissions-boundary entity also blocks policy deletion. Use the returned identity to locate the unexpected boundary assignment.

If deletion later reports DeleteConflict, return to these checks because a dependency remains.

help me diagnose the IAM dependency returned by these checks.

✔️ I see no dependency drift

Good. The role has no attached managed policy. The policy has no remaining boundary use or non-default version.

ⓧ An attached policy appears

An attached managed policy is implementation drift. Detach every returned policy before continuing.

  • Replace <ATTACHED_POLICY_ARN> with one returned policy ARN.
  • Detach that policy by running this command:
aws iam detach-role-policy --role-name BoundaryProofRole --policy-arn <ATTACHED_POLICY_ARN>

What does this command do?

This removes the unexpected managed permissions policy from BoundaryProofRole. Repeat it once for each returned attachment.

  • Rerun the dependency inspection commands after every returned attachment is detached.

ⓧ A non-default version appears

A customer managed policy can retain older non-default versions. Delete each non-default version before deleting BoundaryProofCeiling.

  • Replace <POLICY_ARN> with the runtime BoundaryProofCeiling ARN.
  • Replace <NON_DEFAULT_VERSION_ID> with one returned non-default version ID.
  • Delete that non-default version by running this command:
aws iam delete-policy-version --policy-arn <POLICY_ARN> --version-id <NON_DEFAULT_VERSION_ID>

What does this command do?

This removes one historical policy version while preserving the default version. Repeat it for every returned non-default version.

  • Rerun the dependency inspection commands after every non-default version is deleted.
Delete the resources and prove absence

The dependency checks establish that deletion can proceed safely. Successful delete commands confirm request acceptance.

  • Confirm that the saved boundary policy ARN appears here: your BoundaryProofCeiling ARN.
  • Delete the policy before the role by running these commands:
aws iam delete-policy --policy-arn [[POLICY_ARN="your BoundaryProofCeiling ARN"]] --no-cli-pager
aws iam delete-role --role-name BoundaryProofRole --no-cli-pager

What do these commands remove?

  • The first command deletes BoundaryProofCeiling after its boundary use and policy dependencies are clear.
  • The second command deletes BoundaryProofRole after its permissions boundary is gone.

Fresh failed reads establish absence. These next commands are intentionally expected to fail because the resources should no longer exist.

Before you run these fresh reads, do you expect AWS to return resource metadata or an absence error?

  • Confirm that the saved boundary policy ARN appears here: your BoundaryProofCeiling ARN.
  • Request both deleted resources by running these commands:
aws iam get-role --role-name BoundaryProofRole --output json --no-cli-pager
aws iam get-policy --policy-arn [[POLICY_ARN="your BoundaryProofCeiling ARN"]] --output json --no-cli-pager

What do these reads prove?

Each command asks AWS for the resource after deletion. A fresh NoSuchEntity response proves the named resource is absent.

The failed reads are evidence. Clean delete exits alone do not provide the same proof.

You should see NoSuchEntity for BoundaryProofRole. You should also see NoSuchEntity for BoundaryProofCeiling.

  • Paste the complete failed role read into results/07-role-absent.txt.
  • Paste the complete failed policy read into results/08-policy-absent.txt.
  • Save both absence files in your existing editor.

Does a resource still exist?

If the role read returns metadata, confirm the role deletion command completed successfully. Check that you used the exact role name BoundaryProofRole.

If the policy read returns metadata, rerun the dependency checks. An attachment, boundary use, or non-default version may still require cleanup.

help me interpret my IAM teardown output.

Complete the review and seal the repository

The final files connect the runtime proof to its limits. This preserves evidence provenance inside the same local repository.

Why seal after teardown?

The final tag must point to a snapshot that already contains both absence responses. This prevents the sealed handoff from implying that its temporary AWS resources remain active.

  • Create docs/after-action-review.md inside the iam-boundary-proof/docs folder in your existing editor.
  • Record the completed proof and its tradeoffs by adding this content:
# After-action review for the security lead

## Proven

- 8/8: exact prediction score
- Five explicit-deny decisions and returned source positions
- Ordinary implicit deny for iam:ListUsers
- AllowedByPermissionsBoundary false for iam:CreatePolicyVersion
- Allowed iam:GetRole control
- Role and policy NoSuchEntity results

## Left undone and tradeoff

- Live service authorization remains untested; traded for a no-side-effect simulator proof.
- Resource-based policies remain untested; traded for a focused role and identity-policy boundary case.
- Service control policies remain untested; traded for a single-account proof that every learner can run.
- A separate human console session remains untested; traded for one reproducible administrative CLI session.
- Cross-account trust remains untested; traded for a two-resource build that fits 45 minutes.
- Remote backup and collaboration remain unconfigured; traded for no code-host or ticket-account dependency.

What does this file capture?

The first section records the evidence produced by this run. The second section defines what the proof cannot establish.

Those limitations keep the security lead's conclusion tied to the tested policy inputs.

  • Save docs/after-action-review.md.
  • Confirm the file shows the 8/8 score.
  • Confirm the file records both NoSuchEntity results.
  • Replace the contents of TASKS.md with the completed checklist below:
# Security lead handoff tasks

- [x] Setup 6: verify Git and AWS CLI
- [x] Setup 6: save non-root caller output as check 1
- [x] Setup 6: initialize local Git repository with no remote
- [x] Design 8: write requirements, decisions, topology, plan, and source map
- [x] Design 8: use exactly three AI prompts
- [x] Design 8: freeze trust, boundary, widening, and eight predictions
- [x] Build 20: create exactly one policy and one role
- [x] Build 20: save the unbounded control as check 2
- [x] Build 20: compare implementation with frozen design
- [x] Validation 7: attach the boundary
- [x] Validation 7: save checks 3 through 6
- [x] Validation 7: calculate the exact prediction match count
- [x] Validation 7: update the self-contained HTML readout
- [x] Close-out 4: remove boundary and inspect dependencies
- [x] Close-out 4: delete policy and role
- [x] Close-out 4: paste two NoSuchEntity results
- [x] Close-out 4: commit, tag, and confirm no remote

What do the checkmarks show?

The completed checklist connects every project phase to its saved evidence. All 17 tasks now belong in the post-teardown commit.

  • Save TASKS.md.
  • Confirm every task begins with a completed checkbox.
  • Replace the contents of results/check-manifest.md with the final manifest below:
# Required check manifest

1. results/01-caller.json: administrative caller, not account root
2. results/02-baseline.json: widening allowed without boundary
3. results/03-explicit-denies.json: five explicit denies
4. results/04-implicit-deny.json: missing identity allow
5. results/05-boundary-deny.json: AllowedByPermissionsBoundary is false
6. results/06-allowed-control.json: allowed read control

All six files contain complete pasted command output before teardown. The final tag was created only after teardown evidence existed.

Why update the manifest?

The manifest freezes the six graded checks as complete. Its closing sentence places the final tag after the separate teardown evidence.

  • Save results/check-manifest.md.
  • Confirm the manifest lists exactly six graded check files.

Do the evidence files differ?

Check the 8/8 score against results/scored.md before saving the review.

Check that both absence files contain complete NoSuchEntity responses before completing the teardown tasks.

help me reconcile my final evidence files.

✔️ Awesome, I've got everything!

Great. Make sure all three closing evidence files are saved before sealing the repository.

ⓧ I'd like to double check the full code

Compare your complete docs/after-action-review.md file with this reference:

# After-action review for the security lead

## Proven

- 8/8: exact prediction score
- Five explicit-deny decisions and returned source positions
- Ordinary implicit deny for iam:ListUsers
- AllowedByPermissionsBoundary false for iam:CreatePolicyVersion
- Allowed iam:GetRole control
- Role and policy NoSuchEntity results

## Left undone and tradeoff

- Live service authorization remains untested; traded for a no-side-effect simulator proof.
- Resource-based policies remain untested; traded for a focused role and identity-policy boundary case.
- Service control policies remain untested; traded for a single-account proof that every learner can run.
- A separate human console session remains untested; traded for one reproducible administrative CLI session.
- Cross-account trust remains untested; traded for a two-resource build that fits 45 minutes.
- Remote backup and collaboration remain unconfigured; traded for no code-host or ticket-account dependency.

What should match?

Your review should contain six proven outcomes. It should also contain six limitations with their tradeoffs.

Compare your complete TASKS.md file with this reference:

# Security lead handoff tasks

- [x] Setup 6: verify Git and AWS CLI
- [x] Setup 6: save non-root caller output as check 1
- [x] Setup 6: initialize local Git repository with no remote
- [x] Design 8: write requirements, decisions, topology, plan, and source map
- [x] Design 8: use exactly three AI prompts
- [x] Design 8: freeze trust, boundary, widening, and eight predictions
- [x] Build 20: create exactly one policy and one role
- [x] Build 20: save the unbounded control as check 2
- [x] Build 20: compare implementation with frozen design
- [x] Validation 7: attach the boundary
- [x] Validation 7: save checks 3 through 6
- [x] Validation 7: calculate the exact prediction match count
- [x] Validation 7: update the self-contained HTML readout
- [x] Close-out 4: remove boundary and inspect dependencies
- [x] Close-out 4: delete policy and role
- [x] Close-out 4: paste two NoSuchEntity results
- [x] Close-out 4: commit, tag, and confirm no remote

What should match?

Your checklist should contain 17 completed tasks. No unchecked item should remain.

Compare your complete results/check-manifest.md file with this reference:

# Required check manifest

1. results/01-caller.json: administrative caller, not account root
2. results/02-baseline.json: widening allowed without boundary
3. results/03-explicit-denies.json: five explicit denies
4. results/04-implicit-deny.json: missing identity allow
5. results/05-boundary-deny.json: AllowedByPermissionsBoundary is false
6. results/06-allowed-control.json: allowed read control

All six files contain complete pasted command output before teardown. The final tag was created only after teardown evidence existed.

What should match?

Your manifest should name the same six graded checks. Its closing sentence should confirm that teardown evidence existed before tagging.

  • Switch back to readout.html in your existing editor.
  • Confirm the readout reports simulator-confirmed predictions as 8/8.
  • Confirm the readout covers the simulator limitation.
  • Confirm the readout covers resource-based policies.
  • Confirm the readout covers service control policies.
  • Confirm the readout covers cross-account trust.
  • Confirm the readout covers the separate console-session limitation.

Before you run the final seal, what do you expect the history and remote checks to reveal?

  • Commit the post-teardown evidence and create the local tag by running these commands:
git add TASKS.md docs results readout.html
git commit -m "evidence: prove teardown and seal security handoff"
git tag -a v1.0.0 -m "IAM permissions boundary proof complete"
git log --oneline --decorate
git remote

What does this seal prove?

  • The staging command includes the completed checklist, review, result files, and final readout.
  • The commit records that evidence after both AWS resources were deleted.
  • The annotated v1.0.0 tag marks the completed local handoff.
  • The history and remote checks confirm the tag location and repository locality.

You should see v1.0.0 decorating the newest post-teardown commit. The git remote command should print nothing.

That closes the evidence chain. Your security lead now has a tagged local proof whose AWS resources are confirmed absent.

Tag or commit missing?

Confirm every edited evidence file was saved before rerunning the staging command.

If the tag is absent from the decorated history, confirm the evidence commit succeeded before running the tag command from the seal block.

help me diagnose my final Git seal.

Secret mission

Optional Lane: Run IAM Access Analyzer Policy Checks

Add separate static policy checks to the security handoff without reopening the sealed proof. You will validate the frozen widening policy and can optionally compare it with the retained boundary document.

Clean Up Your Resources

Clean Up Your Resources

The temporary AWS Identity and Access Management (IAM) resources BoundaryProofRole and BoundaryProofCeiling are already deleted. Only the local Git evidence repository remains with no ongoing cost.

Resources you used:

  • The local iam-boundary-proof repository. It contains the sealed evidence package, the v1.0.0 tag, the self-contained readout, the absence proof, and any optional outputs retained with the handoff.

Keep everything running

No action is needed. Choose this option while the security lead reviews the handoff or whenever you want to retain the proof for future reference.

  • Retain the iam-boundary-proof repository as the complete security handoff.
  • Open readout.html locally whenever you need to present the 8/8 result.
  • Preserve the v1.0.0 tag as the marker for the post-teardown evidence state.

Pause - I'll come back to this later

There is no cloud workload to pause because both IAM resources are already absent. This option leaves the local evidence unchanged for a later review.

  • Close the repository files in your existing editor.
  • Leave the iam-boundary-proof repository on your computer.
  • Return to the v1.0.0 evidence state when you are ready to review the handoff again.

Delete - I don't want to use this again

Remove the local evidence repository if you no longer need the handoff. The deleted IAM role and policy remain absent from your AWS account.

  • Confirm that the security lead has accepted the evidence before removing iam-boundary-proof.

macOS

  • Switch back to the terminal session in the iam-boundary-proof repository.
  • Remove the local evidence repository by running these commands:
cd ..
rm -rf iam-boundary-proof
ls
What do these commands do?

The first command moves the terminal to the folder containing the repository. The second command permanently removes the repository.

The final command lists the folders that remain. This gives you an immediate deletion check.

  • Inspect the final folder listing.

You should no longer see iam-boundary-proof in the output.

Repository folder still listed?
  • Check that the terminal was inside iam-boundary-proof before you ran the first command.
  • Use help removing the local evidence repository if the folder remains.

Windows

  • Switch back to the PowerShell session in the iam-boundary-proof repository.
  • Remove the local evidence repository by running these commands:
cd ..
Remove-Item -Recurse -Force .\iam-boundary-proof
ls
What do these commands do?

The first command moves PowerShell to the folder containing the repository. The second command permanently removes the repository.

The final command lists the folders that remain. This gives you an immediate deletion check.

  • Inspect the final folder listing.

You should no longer see iam-boundary-proof in the output.

Repository folder still listed?
  • Check that PowerShell was inside iam-boundary-proof before you ran the first command.
  • Use help removing the local evidence repository if the folder remains.

Linux

  • Switch back to the terminal session in the iam-boundary-proof repository.
  • Remove the local evidence repository by running these commands:
cd ..
rm -rf iam-boundary-proof
ls
What do these commands do?

The first command moves the terminal to the folder containing the repository. The second command permanently removes the repository.

The final command lists the folders that remain. This gives you an immediate deletion check.

  • Inspect the final folder listing.

You should no longer see iam-boundary-proof in the output.

Repository folder still listed?
  • Check that the terminal was inside iam-boundary-proof before you ran the first command.
  • Use help removing the local evidence repository if the folder remains.

Nice Work!

Nice Work!

You did it! Your locally tagged AWS Identity and Access Management (IAM) evidence package proves that the proposed policy widening cannot escape the approved permissions boundary.

You've learned how to:

  • Capture six complete checks with the IAM Policy Simulator. The outputs distinguish explicit policy refusal from missing permission. They prove boundary refusal with AllowedByPermissionsBoundary=false. They also preserve an allowed control.
  • Freeze eight predictions before building. You compared them with eight actual simulator decisions. You calculated the exact 8/8 score in results/scored.md. You published the same result in the self-contained readout.html.
  • Remove dependency-bound IAM resources in the correct order. You proved BoundaryProofRole was absent with NoSuchEntity. You proved BoundaryProofCeiling was absent with fresh evidence. You sealed the local Git history with v1.0.0 only after teardown.
  • Secret Mission: Assess IAM Access Analyzer availability before running optional policy checks. When available, you stored those outputs outside the six-check manifest. You preserved the graded path's 0.00 USD cost and sealed acceptance evidence.

Ready to quiz yourself?