Cloud 15 min read

AWS CLI First #3: Crafting Granular JSON Policies, Boundaries & Policy Simulator

Master AWS Least Privilege from the terminal. Learn how to write fine-grained JSON IAM policies, evaluate permissions using the IAM Policy Simulator CLI, enforce permission boundaries against privilege escalation, and implement account guardrails.

Mohammad Rizky Prawira portrait
Mohammad Rizky Prawira
terminal Series Guide Part 3 of 17

AWS with CLI: The Terminal-First Mastery Path

Part of the CLI-first comprehensive learning path from foundational setup to production cloud architectures.

In Part 1 and Part 2, we set up our CLI environment, configured MFA-enforced role assumption, and simulated our zero-privilege engineer (engineer-bob).

However, in both previous parts, we relied on coarse AWS-managed policies like AdministratorAccess and AmazonEC2FullAccess.

In production cloud security, relying on broad managed policies violates the Principle of Least Privilege:

  1. Excessive Blast Radius: AmazonEC2FullAccess allows an engineer to delete production VPC gateways, wipe snapshot backups, or launch expensive GPU instances.
  2. No Tag-Based Scoping: AWS-managed policies cannot restrict access to specific environments (e.g. allowing deployments in Staging while strictly blocking modifications in Production).
  3. No Dynamic Variable Interpolation: Managed policies cannot dynamically isolate users to their own private storage folders or credentials.

In this guide, we master Custom JSON Permission Engineering entirely from the terminal.

By the end of this guide, you will:

  • Master the 5 structural primitives of an IAM policy document.
  • Understand the 3-way policy evaluation matrix (Implicit Deny vs Explicit Allow vs Explicit Deny).
  • Use Policy Variables (${aws:username}) to build automated self-service credentials management.
  • Craft fine-grained infrastructure policies scoped by resource tags (aws:ResourceTag/Environment).
  • Test and debug permissions before deployment using the AWS IAM Policy Simulator CLI.
  • Enforce Permissions Boundaries to prevent junior developers from escalating their own privileges.
  • Manage policy lifecycle, revisions, and instant rollbacks using IAM Policy Versioning.

💰 Estimated Lab Cost

ComponentUsage in LabAWS Cost
Custom Customer Managed PoliciesJSON Policy Creation & Versioning$0.00 (100% Free)
IAM Policy Simulator APIPermission Dry-Run Evaluation$0.00 (100% Free)
Permissions BoundariesGuardrail Enforcement$0.00 (100% Free)
Total Estimated CostComplete Hands-On Session$0.00 (100% Free)

Core Concept: Anatomy of an IAM Policy JSON

Every IAM policy document in AWS is a JSON structure containing one or more Statements. Each Statement is constructed from 5 foundational building blocks:

+-------------------------------------------------------------------------------+
|                           IAM Policy Document JSON                            |
+-------------------------------------------------------------------------------+
|                                                                               |
|  {                                                                            |
|    "Version": "2012-10-17",                                                   |
|    "Statement": [                                                             |
|      {                                                                        |
|        "Sid": "AllowStagingEC2Ops",          <-- Statement Identifier         |
|        "Effect": "Allow",                    <-- Allow or Deny                |
|        "Action": [                           <-- API verbs to permit/block    |
|          "ec2:StartInstances",                                                |
|          "ec2:StopInstances"                                                  |
|        ],                                                                     |
|        "Resource": "arn:aws:ec2:*:*:instance/*", <-- Target ARN               |
|        "Condition": {                        <-- Contextual constraints       |
|          "StringEquals": {                                                    |
|            "aws:ResourceTag/Environment": "Staging"                           |
|          }                                                                    |
|        }                                                                      |
|      }                                                                        |
|    ]                                                                          |
|  }                                                                            |
+-------------------------------------------------------------------------------+

The 5 Policy Primitives Explained:

  1. Sid (Statement ID): An optional human-readable label (e.g. AllowStagingEC2Ops) to document the purpose of the statement.
  2. Effect: Must be either "Allow" or "Deny".
  3. Action: Specifies the exact API operations. You can use wildcards (e.g. ec2:Describe*) or exact API verbs (e.g. s3:GetObject).
  4. Resource: The Amazon Resource Name (ARN) of the target assets. For actions that do not support resource-level permissions (like ec2:DescribeInstances), this must be "*".
  5. Condition: Fine-grained contextual rules (e.g. IP CIDR whitelists, MFA presence, request timestamps, or resource tags).

Core Concept: The 3-Way Policy Evaluation Matrix

How does AWS decide whether to permit or reject an API request when multiple policies are attached?

AWS evaluates authorization through a strict, deterministic evaluation chain:

                  [ Incoming AWS API Request ]
                               |
                               v
               +-------------------------------+
               | Is there an EXPLICIT DENY?    | -- YES --> [ 403 ACCESS DENIED ]
               +-------------------------------+            (Overrides everything!)
                               |
                              NO
                               |
                               v
               +-------------------------------+
               | Is there an EXPLICIT ALLOW?   | -- YES --> [ 200 ALLOWED ]
               +-------------------------------+
                               |
                              NO
                               |
                               v
                     [ IMPLICIT DENY ]
                   (Default safe state:
                   403 Access Denied)

The Golden Rules of IAM Evaluation:

  1. Default is Always Deny (Implicit Deny): By default, all requests are blocked until an explicit Allow statement grants access.
  2. Explicit Allow Grants Access: If a policy statement matches the action and resource with "Effect": "Allow", the request is authorized.
  3. Explicit Deny TRUMPS EVERYTHING: If any matching statement contains "Effect": "Deny", the request is immediately blocked, regardless of how many Allow policies are attached.

Core Concept: Dynamic Policy Variables (${aws:username})

Writing separate policies for every individual developer (alice-policy.json, bob-policy.json) does not scale.

AWS IAM solves this with Policy Variables. When an API request is made, AWS automatically substitutes ${aws:username} with the caller’s actual identity:

{
  "Effect": "Allow",
  "Action": ["s3:GetObject", "s3:PutObject"],
  "Resource": "arn:aws:s3:::company-engineering-vault/${aws:username}/*"
}
  • When Alice calls s3:GetObject, the ARN resolves to: company-engineering-vault/alice/*
  • When Bob calls s3:GetObject, the ARN resolves to: company-engineering-vault/engineer-bob/*

This single policy dynamically creates private home directories for thousands of engineers without authoring separate documents.


Core Concept: The Defense-in-Depth Hierarchy

In enterprise cloud security, multiple guardrails operate together:

Policy LayerAttached ToPurposeCan it grant permissions?
Identity-Based PolicyIAM User / Role / GroupGrants specific permissions to callersYes
Resource-Based PolicyS3 Bucket, SQS Queue, KMS KeyDictates who can access this specific resourceYes
Permissions BoundaryIAM User / RoleSets the maximum ceiling of allowed permissionsNo (Limits only)
Service Control Policy (SCP)AWS Account / Organizational UnitAccount-wide guardrails across AWS OrganizationsNo (Limits only)

Phase 1: Crafting the Developer Self-Service MFA Policy

Let’s begin by authoring a reusable, enterprise-grade policy named DeveloperSelfManageMFA using policy variables.

This policy allows developers to manage their own passwords, access keys, and MFA devices without granting them permission to modify any other team member’s credentials.

Step 1: Create self-manage-mfa-policy.json

Run the following command in your terminal to generate the policy definition:

cat <<'EOF' > self-manage-mfa-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowViewAccountInfo",
      "Effect": "Allow",
      "Action": [
        "iam:GetAccountSummary",
        "iam:ListAccountAliases",
        "iam:ListUsers"
      ],
      "Resource": "*"
    },
    {
      "Sid": "AllowSelfManageCredentials",
      "Effect": "Allow",
      "Action": [
        "iam:ChangePassword",
        "iam:GetUser",
        "iam:CreateAccessKey",
        "iam:DeleteAccessKey",
        "iam:ListAccessKeys",
        "iam:UpdateAccessKey"
      ],
      "Resource": "arn:aws:iam::*:user/${aws:username}"
    },
    {
      "Sid": "AllowSelfManageMFA",
      "Effect": "Allow",
      "Action": [
        "iam:CreateVirtualMFADevice",
        "iam:DeleteVirtualMFADevice",
        "iam:EnableMFADevice",
        "iam:ResyncMFADevice",
        "iam:ListMFADevices"
      ],
      "Resource": [
        "arn:aws:iam::*:mfa/${aws:username}-mfa",
        "arn:aws:iam::*:user/${aws:username}"
      ]
    }
  ]
}
EOF

Step 2: Create the Customer Managed Policy via CLI

Using your administrator profile (cloud-dev-admin), publish this policy to your AWS account:

aws iam create-policy \
  --policy-name DeveloperSelfManageMFA \
  --policy-document file://self-manage-mfa-policy.json \
  --description "Allows developers to manage their own keys and MFA devices" \
  --profile cloud-dev-admin

JSON Output:

{
    "Policy": {
        "PolicyName": "DeveloperSelfManageMFA",
        "PolicyId": "ANPAEXAMPLEPOLICYID",
        "Arn": "arn:aws:iam::123456789012:policy/DeveloperSelfManageMFA",
        "Path": "/",
        "DefaultVersionId": "v1",
        "AttachmentCount": 0,
        "IsAttachable": true,
        "CreateDate": "2026-08-29T01:00:00+00:00"
    }
}

Step 3: Attach the Policy to the Developers Group

Instead of attaching the policy directly to Bob’s individual user account, we attach it to the Developers IAM Group we created in Part 2:

ACCOUNT_ID=$(aws sts get-caller-identity --profile cloud-dev-admin --query "Account" --output text)

aws iam attach-group-policy \
  --group-name Developers \
  --policy-arn "arn:aws:iam::${ACCOUNT_ID}:policy/DeveloperSelfManageMFA" \
  --profile cloud-dev-admin

[!TIP] Why Group + Policy Variables is Enterprise Magic: Because the policy uses arn:aws:iam::*:user/${aws:username}, attaching it to Developers grants self-service permissions to all current and future engineers in the group.

  • When engineer-bob runs iam:CreateAccessKey, ${aws:username} evaluates to engineer-bob.
  • When engineer-alice joins next month, adding her to Developers gives her an isolated credential sandbox with zero additional policy authoring!

Clean up the local file:

rm -f self-manage-mfa-policy.json

Phase 2: Crafting Scoped Infrastructure Policies with Resource Tags

Now let’s author a scoped operational policy for staging environments: StagingEC2OperatorPolicy.

The Security Requirements:

  1. The engineer can query EC2 instances globally (ec2:Describe*).
  2. The engineer can Start, Stop, and Reboot instances only if the instance has the tag Environment: Staging.
  3. The engineer is explicitly denied from terminating (ec2:TerminateInstances) any instance under all circumstances.

Step 1: Create staging-ec2-operator-policy.json

cat <<'EOF' > staging-ec2-operator-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowGlobalReadAndDiscovery",
      "Effect": "Allow",
      "Action": [
        "ec2:Describe*",
        "ec2:Get*",
        "ec2:List*"
      ],
      "Resource": "*"
    },
    {
      "Sid": "AllowControlStagingInstancesOnly",
      "Effect": "Allow",
      "Action": [
        "ec2:StartInstances",
        "ec2:StopInstances",
        "ec2:RebootInstances"
      ],
      "Resource": "arn:aws:ec2:*:*:instance/*",
      "Condition": {
        "StringEquals": {
          "aws:ResourceTag/Environment": "Staging"
        }
      }
    },
    {
      "Sid": "ExplicitlyDenyTerminationEverywhere",
      "Effect": "Deny",
      "Action": [
        "ec2:TerminateInstances"
      ],
      "Resource": "*"
    }
  ]
}
EOF

Step 2: Publish the Policy via CLI

aws iam create-policy \
  --policy-name StagingEC2OperatorPolicy \
  --policy-document file://staging-ec2-operator-policy.json \
  --description "Scoped control for staging EC2 instances with termination block" \
  --profile cloud-dev-admin

Step 3: Attach the Policy to the Developers Group

Attach this policy directly to the Developers group so Bob (and all developers) inherit it:

ACCOUNT_ID=$(aws sts get-caller-identity --profile cloud-dev-admin --query "Account" --output text)

aws iam attach-group-policy \
  --group-name Developers \
  --policy-arn "arn:aws:iam::${ACCOUNT_ID}:policy/StagingEC2OperatorPolicy" \
  --profile cloud-dev-admin

Phase 3: Testing Policies with the AWS IAM Policy Simulator CLI

Before letting engineers run commands on live infrastructure, how do you verify that your JSON tag conditions work as expected?

The AWS CLI provides two powerful simulation commands:

  1. simulate-principal-policy (Recommended): Tests the live, effective permissions of a real User or Role (including all policies inherited from their groups).
  2. simulate-custom-policy: Tests a raw offline JSON file before you attach or publish it.

Step 1: Test 1 - Simulating Allowed Action on Staging Instance

Let’s simulate Bob attempting to start an EC2 instance tagged with Environment: Staging:

aws iam simulate-principal-policy \
  --policy-source-arn "arn:aws:iam::${ACCOUNT_ID}:user/engineer-bob" \
  --action-names "ec2:StartInstances" \
  --resource-arns "arn:aws:ec2:ap-southeast-1:${ACCOUNT_ID}:instance/i-0123456789abcdef0" \
  --context-entries "ContextKeyName='aws:ResourceTag/Environment',ContextKeyValues='Staging',ContextKeyType='string'" \
  --profile cloud-dev-admin \
  --query "EvaluationResults[0].[EvalActionName, EvalDecision]" \
  --output table

[!TIP] Simulating Raw JSON Files with simulate-custom-policy: If you want to test the JSON file directly before attaching it, run:

aws iam simulate-custom-policy \
  --policy-input-list file://staging-ec2-operator-policy.json \
  --action-names "ec2:StartInstances" \
  --resource-arns "arn:aws:ec2:ap-southeast-1:${ACCOUNT_ID}:instance/i-0123456789abcdef0" \
  --context-entries "ContextKeyName='aws:ResourceTag/Environment',ContextKeyValues='Staging',ContextKeyType='string'" \
  --profile cloud-dev-admin \
  --query "EvaluationResults[0].[EvalActionName, EvalDecision]" \
  --output table

Simulator Output:

---------------------------------
|     SimulatePrincipalPolicy   |
+---------------------+---------+
|  ec2:StartInstances |  allowed|
+---------------------+---------+

Step 2: Test 2 - Simulating Blocked Action on Production Instance

What happens if Bob attempts to stop a Production instance (Environment: Production)?

aws iam simulate-principal-policy \
  --policy-source-arn "arn:aws:iam::${ACCOUNT_ID}:user/engineer-bob" \
  --action-names "ec2:StopInstances" \
  --resource-arns "arn:aws:ec2:ap-southeast-1:${ACCOUNT_ID}:instance/i-0123456789abcdef0" \
  --context-entries "ContextKeyName='aws:ResourceTag/Environment',ContextKeyValues='Production',ContextKeyType='string'" \
  --profile cloud-dev-admin \
  --query "EvaluationResults[0].[EvalActionName, EvalDecision]" \
  --output table

Simulator Output:

---------------------------------
|     SimulatePrincipalPolicy   |
+-------------------+-----------+
|  ec2:StopInstances|  implicitDeny|
+-------------------+-----------+

Result: implicitDeny! The condition failed because the tag was Production, so the action was safely blocked.


Step 3: Test 3 - Simulating Explicit Deny (ec2:TerminateInstances)

What if Bob tries to terminate an instance, even one tagged as Staging?

aws iam simulate-principal-policy \
  --policy-source-arn "arn:aws:iam::${ACCOUNT_ID}:user/engineer-bob" \
  --action-names "ec2:TerminateInstances" \
  --resource-arns "arn:aws:ec2:ap-southeast-1:${ACCOUNT_ID}:instance/i-0123456789abcdef0" \
  --context-entries "ContextKeyName='aws:ResourceTag/Environment',ContextKeyValues='Staging',ContextKeyType='string'" \
  --profile cloud-dev-admin \
  --query "EvaluationResults[0].[EvalActionName, EvalDecision]" \
  --output table

Simulator Output:

-------------------------------------
|       SimulatePrincipalPolicy     |
+------------------------+----------+
|  ec2:TerminateInstances|  explicitDeny|
+------------------------+----------+

Result: explicitDeny! The explicit Deny statement overrode all conditions.

Clean up the local file:

rm -f staging-ec2-operator-policy.json

Phase 4: Enforcing Permissions Boundaries (Preventing Privilege Escalation)

In many engineering organizations, developers need permissions to create IAM Roles for their serverless workloads (e.g. AWS Lambda functions, ECS task roles).

The Privilege Escalation Threat:

If developer Bob has iam:CreateRole and iam:AttachRolePolicy, Bob could create a new IAM role, attach AdministratorAccess to it, and assume the role—instantly escalating himself to full cloud root!

The Solution: Permissions Boundary

A Permissions Boundary is an advanced IAM feature that acts as a maximum ceiling. Even if Bob attaches AdministratorAccess to a role, the role can never perform actions outside the boundary.

+-------------------------------------------------------------------------------+
|                       Effective Permission Calculation                        |
+-------------------------------------------------------------------------------+
|                                                                               |
|   [ Identity Permission Policy ]       [ Permissions Boundary ]               |
|   (e.g. AdministratorAccess)     ∩     (e.g. S3 & CloudWatch Logs Only)       |
|                                                                               |
|                                  =                                            |
|                                                                               |
|                   [ EFFECTIVE PERMISSION ]                                    |
|                   (Only S3 & CloudWatch Logs!)                                |
|                                                                               |
+-------------------------------------------------------------------------------+

Step 1: Create developer-boundary-policy.json

Let’s create a boundary policy that caps all operations strictly to EC2, S3, and CloudWatch:

cat <<'EOF' > developer-boundary-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowedServicesCeiling",
      "Effect": "Allow",
      "Action": [
        "ec2:*",
        "s3:*",
        "logs:*",
        "cloudwatch:*"
      ],
      "Resource": "*"
    },
    {
      "Sid": "BlockIAMModifications",
      "Effect": "Deny",
      "Action": [
        "iam:*",
        "organizations:*",
        "account:*"
      ],
      "Resource": "*"
    }
  ]
}
EOF

Step 2: Publish the Boundary Policy via CLI

aws iam create-policy \
  --policy-name DeveloperBoundaryCeiling \
  --policy-document file://developer-boundary-policy.json \
  --description "Maximum permission boundary ceiling for engineering users" \
  --profile cloud-dev-admin

Step 3: Attach the Boundary to engineer-bob

Apply the boundary to Bob using aws iam put-user-permissions-boundary:

aws iam put-user-permissions-boundary \
  --user-name engineer-bob \
  --permissions-boundary "arn:aws:iam::${ACCOUNT_ID}:policy/DeveloperBoundaryCeiling" \
  --profile cloud-dev-admin

Verify that Bob now has an active boundary:

aws iam get-user --user-name engineer-bob --profile cloud-dev-admin --query "User.PermissionsBoundary" --output json

Output:

{
    "PermissionsBoundaryType": "Policy",
    "PermissionsBoundaryArn": "arn:aws:iam::123456789012:policy/DeveloperBoundaryCeiling"
}

Now, even if an administrator accidentally attaches AdministratorAccess directly to Bob, Bob is mathematically barred from modifying IAM, billing, or organization settings because the boundary caps his maximum permissions!

Clean up the local file:

rm -f developer-boundary-policy.json

Phase 5: Policy Versioning & Instant Rollbacks via CLI

Unlike code repositories where Git handles history, AWS IAM has native policy versioning built into the API.

Each customer-managed policy retains up to 5 versions (v1, v2, v3, v4, v5). You can modify, inspect, and instantly roll back policies from the command line.

Step 1: Create a New Policy Version (v2)

Let’s say we want to update DeveloperSelfManageMFA to also allow developers to inspect STS caller identities:

cat <<'EOF' > updated-self-mfa.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowSelfManageCredentialsAndSTS",
      "Effect": "Allow",
      "Action": [
        "iam:ChangePassword",
        "iam:GetUser",
        "iam:CreateAccessKey",
        "iam:DeleteAccessKey",
        "iam:ListAccessKeys",
        "sts:GetCallerIdentity"
      ],
      "Resource": "*"
    }
  ]
}
EOF

# Create version 2 and set it as the active default
aws iam create-policy-version \
  --policy-arn "arn:aws:iam::${ACCOUNT_ID}:policy/DeveloperSelfManageMFA" \
  --policy-document file://updated-self-mfa.json \
  --set-as-default \
  --profile cloud-dev-admin

Step 2: List All Policy Versions

aws iam list-policy-versions \
  --policy-arn "arn:aws:iam::${ACCOUNT_ID}:policy/DeveloperSelfManageMFA" \
  --profile cloud-dev-admin \
  --output table

Output:

-------------------------------------------------------------------------
|                          ListPolicyVersions                           |
+------------------+------------+---------------------------------------+
|  CreateDate      | IsDefault  |  VersionId                            |
+------------------+------------+---------------------------------------+
|  2026-08-29T...  |  True      |  v2                                   |
|  2026-08-29T...  |  False     |  v1                                   |
+------------------+------------+---------------------------------------+

Step 3: Instant Rollback to v1

If a new policy version causes unexpected permission errors in production, you can instantly revert to v1 with a single command:

aws iam set-default-policy-version \
  --policy-arn "arn:aws:iam::${ACCOUNT_ID}:policy/DeveloperSelfManageMFA" \
  --version-id v1 \
  --profile cloud-dev-admin

No re-uploading JSON or modifying user attachments required—the policy rollback takes effect across all attached users within milliseconds.

Clean up the local file:

rm -f updated-self-mfa.json

Phase 6: Multi-Account Guardrails with Service Control Policies (SCPs)

When operating multiple AWS accounts within an AWS Organization, IAM policies alone are not enough.

A rogue administrator in a child account could create an IAM user with AdministratorAccess and bypass local rules. To prevent this, organizations enforce Service Control Policies (SCPs) at the AWS Organization root.

+-------------------------------------------------------------------------------+
|                       AWS Organizations SCP Architecture                      |
+-------------------------------------------------------------------------------+
|                                                                               |
|  [ AWS Organization Root ]                                                    |
|     |                                                                         |
|     +-- [ Service Control Policy: Region Restriction SCP ]                   |
|            |                                                                  |
|            +--> [ Staging Account (111122223333) ]                            |
|            |       -> Enforces: All APIs outside ap-southeast-1 are DENIED    |
|            |                                                                  |
|            +--> [ Production Account (444455556666) ]                         |
|                    -> Enforces: Even ROOT cannot delete CloudTrail logs       |
|                                                                               |
+-------------------------------------------------------------------------------+

Real-World Example: Region-Locking SCP JSON

The following SCP denies all AWS infrastructure actions in any region other than Singapore (ap-southeast-1), protecting the company from expensive accidental deployments or cryptocurrency mining in unauthorized regions:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyAllOutsideSingapore",
      "Effect": "Deny",
      "NotAction": [
        "iam:*",
        "organizations:*",
        "route53:*",
        "cloudfront:*",
        "support:*"
      ],
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "aws:RequestedRegion": [
            "ap-southeast-1"
          ]
        }
      }
    }
  ]
}

[!TIP] Notice the NotAction block: global services like IAM, Route53, and CloudFront must be exempted because their APIs do not run in specific geographical regions.


Phase 7: Security Auditing & Compliance with IAM Credential Reports

How do security teams and compliance auditors (SOC2, ISO 27001, PCI-DSS) verify that all users have MFA enabled and that no stale access keys exist?

AWS provides the IAM Credential Report API, which generates an account-wide security audit table.

Step 1: Request Credential Report Generation

aws iam generate-credential-report --profile cloud-dev-admin

Output:

{
    "State": "COMPLETE",
    "Description": "No report exists. Generating a new report."
}

Step 2: Download and Inspect the Audit CSV

The report content is returned as a Base64-encoded CSV string. We can decode and format it directly in the terminal:

aws iam get-credential-report \
  --profile cloud-dev-admin \
  --query "Content" \
  --output text | base64 -d | cut -d',' -f1,4,8,9,11,16 | column -s, -t

Formatted Audit Output:

user               password_enabled  mfa_active  access_key_1_active  access_key_1_last_rotated  access_key_2_active
<root_account>     not_supported     true        false                N/A                        false
cloud-dev-admin    false             false       true                 2026-08-27T11:20:00+00:00  false
engineer-bob       false             true        true                 2026-08-28T04:00:00+00:00  false

What Auditors Check in This Report:

  1. mfa_active == true: Confirms that all users and the root account have multi-factor authentication enforced.
  2. access_key_last_rotated < 90 days: Verifies that static access keys are routinely rotated.
  3. access_key_last_used_date: Detects unused or abandoned credentials that should be deactivated.

Quick Reference: Essential Policy & Boundary CLI Commands

ActionCLI Command
Create Custom Policyaws iam create-policy --policy-name <name> --policy-document file://policy.json
Attach Policy to Groupaws iam attach-group-policy --group-name <group> --policy-arn <arn>
Attach Policy to Useraws iam attach-user-policy --user-name <user> --policy-arn <arn>
Attach Policy to Roleaws iam attach-role-policy --role-name <role> --policy-arn <arn>
Simulate Custom Policyaws iam simulate-custom-policy --policy-input-list file://policy.json --action-names <action>
Simulate Principal Policyaws iam simulate-principal-policy --policy-source-arn <user-arn> --action-names <action>
Set Permissions Boundaryaws iam put-user-permissions-boundary --user-name <user> --permissions-boundary <arn>
Create Policy Versionaws iam create-policy-version --policy-arn <arn> --policy-document file://new.json --set-as-default
List Policy Versionsaws iam list-policy-versions --policy-arn <arn>
Rollback Policy Versionaws iam set-default-policy-version --policy-arn <arn> --version-id <version>
Generate Credential Reportaws iam generate-credential-report
Get Credential Reportaws iam get-credential-report --query "Content" --output text | base64 -d

Summary & What’s Next

In this third installment of AWS with CLI, we moved to mastery of cloud authorization:

  1. We broke down the 5 structural primitives of IAM policies and the 3-way evaluation matrix.
  2. We used ${aws:username} policy variables to provision an automated self-service MFA policy attached to the Developers group.
  3. We authored tag-scoped infrastructure policies using aws:ResourceTag/Environment conditions.
  4. We verified permissions without touching live servers using the AWS IAM Policy Simulator CLI.
  5. We enforced Permissions Boundaries to eliminate privilege escalation vectors.
  6. We leveraged native IAM Policy Versioning for instant, zero-downtime rollbacks.
  7. We audited account credential hygiene and compliance using IAM Credential Reports.

Now that our identity, security, and authorization foundation is impenetrable, we are ready to move into the physical cloud.

In Part 4, we begin Module 2: Global Infrastructure & VPC Networking, mastering AWS Regions, Availability Zones, physical ZoneId mapping, and opt-in region management from the CLI!

Related & Recommended Guides

Continue exploring related systems architectures and engineering field notes.