Intermediate

IAM Policies for AI Agents

Create dedicated IAM roles with least-privilege permissions for AI coding agents. Learn to write deny policies that block destructive actions, set permission boundaries, and use condition keys to restrict access by IP, MFA, and time.

The Golden Rule: Dedicated IAM Roles for AI Agents

Never give an AI agent your personal AWS credentials or an admin role. Always create a dedicated IAM user or role specifically for the agent. This gives you a single point of control: you can audit, restrict, and revoke the agent's access without affecting your own.

Never use root credentials: If an AI agent has root account access, no IAM policy, SCP, or permission boundary can restrict it. Root access bypasses all IAM controls. Always use IAM users or roles.

Creating a Dedicated IAM Role for AI Agents

Start by creating an IAM role that your AI agent will assume. This role should have a trust policy that only allows the specific entity (your local machine, a CI/CD pipeline, etc.) to assume it:

JSON — Trust policy for AI agent role
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:user/developer-workstation"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "ai-agent-session-2026"
        }
      }
    }
  ]
}
Bash — Create the IAM role via AWS CLI
# Create the role
aws iam create-role \
  --role-name AIAgentRole \
  --assume-role-policy-document file://trust-policy.json \
  --description "Restricted role for AI coding agents" \
  --max-session-duration 3600 \
  --tags Key=Purpose,Value=ai-agent Key=Environment,Value=development

# Verify the role was created
aws iam get-role --role-name AIAgentRole

Deny Policies: Explicitly Blocking Destructive Actions

The most critical IAM pattern for AI agent safety is the explicit deny. In AWS IAM, an explicit deny always overrides any allow. This means even if the agent somehow gets additional permissions (through another policy, group membership, or session policy), the deny will still block destructive actions.

Comprehensive Deny Policy for Destructive Actions

The following policy denies all common destructive actions across major AWS services:

JSON — DenyDestructiveActions policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyEC2Destruction",
      "Effect": "Deny",
      "Action": [
        "ec2:TerminateInstances",
        "ec2:DeleteVpc",
        "ec2:DeleteSubnet",
        "ec2:DeleteSecurityGroup",
        "ec2:DeleteNatGateway",
        "ec2:DeleteInternetGateway",
        "ec2:DeleteRouteTable",
        "ec2:DeleteVolume",
        "ec2:DeleteKeyPair",
        "ec2:DeleteSnapshot"
      ],
      "Resource": "*"
    },
    {
      "Sid": "DenyRDSDestruction",
      "Effect": "Deny",
      "Action": [
        "rds:DeleteDBInstance",
        "rds:DeleteDBCluster",
        "rds:DeleteDBSnapshot",
        "rds:DeleteDBClusterSnapshot",
        "rds:DeleteGlobalCluster"
      ],
      "Resource": "*"
    },
    {
      "Sid": "DenyS3Destruction",
      "Effect": "Deny",
      "Action": [
        "s3:DeleteBucket",
        "s3:DeleteObject",
        "s3:DeleteObjectVersion",
        "s3:DeleteBucketPolicy"
      ],
      "Resource": "*"
    },
    {
      "Sid": "DenyLambdaDestruction",
      "Effect": "Deny",
      "Action": [
        "lambda:DeleteFunction",
        "lambda:DeleteLayerVersion",
        "lambda:DeleteEventSourceMapping"
      ],
      "Resource": "*"
    },
    {
      "Sid": "DenyEKSDestruction",
      "Effect": "Deny",
      "Action": [
        "eks:DeleteCluster",
        "eks:DeleteNodegroup",
        "eks:DeleteFargateProfile"
      ],
      "Resource": "*"
    },
    {
      "Sid": "DenyDynamoDBDestruction",
      "Effect": "Deny",
      "Action": [
        "dynamodb:DeleteTable",
        "dynamodb:DeleteBackup"
      ],
      "Resource": "*"
    },
    {
      "Sid": "DenyCloudFormationDestruction",
      "Effect": "Deny",
      "Action": [
        "cloudformation:DeleteStack",
        "cloudformation:DeleteStackSet"
      ],
      "Resource": "*"
    },
    {
      "Sid": "DenyIAMDestruction",
      "Effect": "Deny",
      "Action": [
        "iam:DeleteRole",
        "iam:DeletePolicy",
        "iam:DeleteUser",
        "iam:DeleteGroup",
        "iam:DeleteRolePolicy",
        "iam:DeleteUserPolicy"
      ],
      "Resource": "*"
    }
  ]
}
Bash — Attach the deny policy to the AI agent role
# Create the policy
aws iam create-policy \
  --policy-name DenyDestructiveActions \
  --policy-document file://deny-destructive.json \
  --description "Blocks all destructive actions for AI agents"

# Attach it to the AI agent role
aws iam attach-role-policy \
  --role-name AIAgentRole \
  --policy-arn arn:aws:iam::123456789012:policy/DenyDestructiveActions

Allow Policies: What AI Agents Should Be Able to Do

After blocking destructive actions, grant the agent permissions for the tasks it needs to perform. Here are practical examples:

EC2: Allow Describe and Create, Deny Terminate and Delete

JSON — EC2 read/create policy for AI agents
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowEC2ReadAndCreate",
      "Effect": "Allow",
      "Action": [
        "ec2:Describe*",
        "ec2:RunInstances",
        "ec2:CreateTags",
        "ec2:CreateSecurityGroup",
        "ec2:AuthorizeSecurityGroupIngress",
        "ec2:AuthorizeSecurityGroupEgress",
        "ec2:CreateSubnet",
        "ec2:CreateVpc",
        "ec2:CreateVolume",
        "ec2:AttachVolume",
        "ec2:StartInstances",
        "ec2:StopInstances",
        "ec2:RebootInstances"
      ],
      "Resource": "*"
    }
  ]
}

S3: Allow Put and Get, Deny Delete

JSON — S3 read/write policy for AI agents
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowS3ReadWrite",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:GetObjectVersion",
        "s3:PutObject",
        "s3:ListBucket",
        "s3:ListAllMyBuckets",
        "s3:GetBucketLocation",
        "s3:CreateBucket",
        "s3:PutBucketTagging"
      ],
      "Resource": [
        "arn:aws:s3:::*",
        "arn:aws:s3:::*/*"
      ]
    }
  ]
}

Permission Boundaries for AI Agent Roles

A permission boundary is an advanced IAM feature that sets the maximum permissions a role can have. Even if someone accidentally attaches an AdministratorAccess policy to the AI agent role, the permission boundary will restrict it to only the actions you defined.

JSON — Permission boundary for AI agents
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowReadAndCreateOnly",
      "Effect": "Allow",
      "Action": [
        "ec2:Describe*",
        "ec2:RunInstances",
        "ec2:CreateTags",
        "ec2:StartInstances",
        "ec2:StopInstances",
        "s3:GetObject",
        "s3:PutObject",
        "s3:ListBucket",
        "s3:ListAllMyBuckets",
        "rds:Describe*",
        "rds:ListTagsForResource",
        "lambda:GetFunction",
        "lambda:ListFunctions",
        "lambda:CreateFunction",
        "lambda:UpdateFunctionCode",
        "lambda:InvokeFunction",
        "logs:GetLogEvents",
        "logs:DescribeLogGroups",
        "logs:DescribeLogStreams",
        "cloudwatch:GetMetricData",
        "cloudwatch:DescribeAlarms",
        "sts:GetCallerIdentity"
      ],
      "Resource": "*"
    },
    {
      "Sid": "ExplicitDenyAllDestructive",
      "Effect": "Deny",
      "Action": [
        "ec2:TerminateInstances",
        "ec2:Delete*",
        "rds:Delete*",
        "s3:Delete*",
        "lambda:DeleteFunction",
        "dynamodb:DeleteTable",
        "cloudformation:DeleteStack",
        "eks:DeleteCluster",
        "iam:Delete*",
        "iam:Create*",
        "iam:Attach*",
        "iam:Detach*",
        "iam:Put*"
      ],
      "Resource": "*"
    }
  ]
}
Bash — Apply permission boundary to AI agent role
# Create the permission boundary policy
aws iam create-policy \
  --policy-name AIAgentPermissionBoundary \
  --policy-document file://permission-boundary.json

# Apply it to the role
aws iam put-role-permissions-boundary \
  --role-name AIAgentRole \
  --permissions-boundary arn:aws:iam::123456789012:policy/AIAgentPermissionBoundary

Condition Keys: Advanced Restrictions

IAM condition keys let you add additional constraints to policies. These are especially useful for AI agent scenarios:

Restrict by Source IP

Only allow the AI agent to make API calls from your office or VPN IP range:

JSON — IP-based condition
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyFromUnknownIPs",
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "NotIpAddress": {
          "aws:SourceIp": [
            "203.0.113.0/24",
            "198.51.100.0/24"
          ]
        }
      }
    }
  ]
}

Restrict by Time (Business Hours Only)

Prevent AI agents from making changes outside business hours when no one is monitoring:

JSON — Time-based condition
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyOutsideBusinessHours",
      "Effect": "Deny",
      "Action": [
        "ec2:RunInstances",
        "ec2:StopInstances",
        "s3:PutObject",
        "lambda:UpdateFunctionCode"
      ],
      "Resource": "*",
      "Condition": {
        "DateGreaterThan": {
          "aws:CurrentTime": "2026-01-01T18:00:00Z"
        },
        "DateLessThan": {
          "aws:CurrentTime": "2026-01-02T09:00:00Z"
        }
      }
    }
  ]
}

Require MFA for Sensitive Actions

Require that the session used by the AI agent was established with MFA:

JSON — MFA condition
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyWithoutMFA",
      "Effect": "Deny",
      "Action": [
        "ec2:RunInstances",
        "rds:CreateDBInstance",
        "s3:CreateBucket"
      ],
      "Resource": "*",
      "Condition": {
        "BoolIfExists": {
          "aws:MultiFactorAuthPresent": "false"
        }
      }
    }
  ]
}

AWS Organizations SCPs to Prevent Deletion Across Accounts

If you use AWS Organizations, you can apply Service Control Policies at the organization or OU level. SCPs restrict what IAM policies in member accounts can do — even if the account has an admin user.

💡
SCP vs IAM policy: SCPs set the maximum permissions for an entire account. IAM policies grant permissions within that boundary. We cover SCPs in depth in the next lesson. Here, we show a quick example of how they complement IAM policies for AI agent safety.
JSON — SCP preventing deletion in production OU
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyProductionDeletion",
      "Effect": "Deny",
      "Action": [
        "ec2:TerminateInstances",
        "rds:DeleteDBInstance",
        "s3:DeleteBucket",
        "lambda:DeleteFunction",
        "eks:DeleteCluster",
        "dynamodb:DeleteTable",
        "cloudformation:DeleteStack"
      ],
      "Resource": "*"
    }
  ]
}

Testing Your IAM Policies

Always test your policies before deploying them to production. AWS provides several tools:

Bash — Simulate IAM policy evaluation
# Test if the AI agent role can terminate an EC2 instance
aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::123456789012:role/AIAgentRole \
  --action-names ec2:TerminateInstances \
  --output table

# Expected output: implicitDeny or explicitDeny

# Test if the AI agent role can describe EC2 instances
aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::123456789012:role/AIAgentRole \
  --action-names ec2:DescribeInstances \
  --output table

# Expected output: allowed

# Test multiple actions at once
aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::123456789012:role/AIAgentRole \
  --action-names \
    ec2:DescribeInstances \
    ec2:RunInstances \
    ec2:TerminateInstances \
    s3:GetObject \
    s3:DeleteBucket \
    rds:DescribeDBInstances \
    rds:DeleteDBInstance \
  --output table
💡
Best practice: Run policy simulations as part of your CI/CD pipeline. Any time an IAM policy for the AI agent role is modified, automatically verify that destructive actions remain denied.

Summary

StrategyPurposeWhen to Use
Dedicated IAM RoleIsolate agent permissionsAlways
Explicit Deny PolicyBlock destructive actionsAlways
Permission BoundaryCap maximum permissionsWhen multiple policies might be attached
IP ConditionRestrict by networkWhen agent runs from known IPs
Time ConditionRestrict by scheduleWhen agent should only run during business hours
MFA ConditionRequire human verificationFor sensitive create/modify actions
SCPsOrganization-wide guardrailsWhen using AWS Organizations