IAM Policy Enforcement
Evaluate IAM policies on every API request before service dispatch. Test your permission boundaries, resource policies, and least-privilege setups locally.
Enabling IAM Enforcement
Strict mode: denied requests return 403 AccessDenied
$ IAM_ENFORCEMENT=1 localemu start
IAM policy enforcement: enabled
Ready. Soft mode: denied requests are logged but still allowed
$ IAM_ENFORCEMENT=soft localemu start
IAM policy enforcement: soft mode (log only)
Ready.
# Denied requests are logged but still allowed:
WARN IAM: would deny s3:GetObject for arn:aws:iam::000000000000:user/dev on arn:aws:s3:::my-bucket/* Soft mode is useful for auditing existing workflows before switching to strict enforcement.
How It Works
Every API request is intercepted before it reaches the service handler. The enforcement engine extracts the caller identity, the action, and the target resource, then evaluates all applicable policies using the AWS policy evaluation algorithm:
Supported Policy Features
| Feature | Details |
|---|---|
| Action matching | Exact match, wildcards (s3:*, s3:Get*) |
| Resource matching | Full ARN matching with wildcards |
| NotAction / NotResource | Inverse matching for actions and resources |
| Condition operators | 26 operators: StringEquals, StringLike, IpAddress, ArnLike, DateLessThan, NumericGreaterThan, Bool, Null, and more |
| IfExists variants | StringEqualsIfExists, IpAddressIfExists, etc. - condition passes if the key is absent |
| Set operators | ForAllValues and ForAnyValue qualifiers for multi-valued condition keys |
| Identity-based policies | Inline and managed policies on users, groups, and roles |
| Resource-based policies | S3 bucket policies, SQS queue policies, SNS topic policies, KMS key policies |
| Permission boundaries | Maximum permissions boundary attached to users or roles |
Special Rules
Root principal bypass
Requests signed with a root access key bypass all IAM checks. Default root keys are
AKIAIOSFODNN7EXAMPLE
and 000000000000.
The awsemu CLI uses
AKIAIOSFODNN7EXAMPLE
by default. To add your own root key IDs, set
ROOT_ACCESS_KEYS
to a comma-separated list of access key IDs you want to exempt.
One unconditionally exempt action
Only sts:GetCallerIdentity
is exempt from enforcement so callers can identify themselves even with no permissions. Every other
action, including the rest of STS (AssumeRole,
GetSessionToken) and all of IAM, is evaluated
against policies.
Internal-call sentinel
LocalEmu's own cross-service calls (e.g., an S3 PutObject that triggers a Lambda, or an Event-Source-Mapping reading from SQS) bypass enforcement via an internal sentinel key. Users never produce this key, it exists so the emulator's plumbing is not blocked by user policies.
Error format
Denied requests return HTTP 403 with an AccessDenied error.
The message includes the caller ARN, the attempted action, and the target resource.
ARN Building
The enforcement engine builds resource ARNs automatically for each service. This determines what the
Resource field in your policy should match.
| Service | ARN Pattern |
|---|---|
| S3 | arn:aws:s3:::bucket-name/* |
| DynamoDB | arn:aws:dynamodb:us-east-1:000000000000:table/name |
| Lambda | arn:aws:lambda:us-east-1:000000000000:function:name |
| SQS | arn:aws:sqs:us-east-1:000000000000:queue-name |
| SNS | arn:aws:sns:us-east-1:000000000000:topic-name |
| KMS | arn:aws:kms:us-east-1:000000000000:key/key-id |
| IAM | arn:aws:iam::000000000000:user/name |
| EC2 | arn:aws:ec2:us-east-1:000000000000:instance/i-id |
| CloudFormation | arn:aws:cloudformation:us-east-1:000000000000:stack/name/uuid |
| CloudWatch Logs | arn:aws:logs:us-east-1:000000000000:log-group:name |
| Secrets Manager | arn:aws:secretsmanager:us-east-1:000000000000:secret:name |
| SSM | arn:aws:ssm:us-east-1:000000000000:parameter/name |
| Step Functions | arn:aws:states:us-east-1:000000000000:stateMachine:name |
| EventBridge | arn:aws:events:us-east-1:000000000000:rule/name |
| Kinesis | arn:aws:kinesis:us-east-1:000000000000:stream/name |
| API Gateway | arn:aws:apigateway:us-east-1::/restapis/id |
Example: Create User, Attach Policy, Verify
Step 1: Create an IAM user with access keys
$ awsemu iam create-user --user-name dev
$ awsemu iam create-access-key --user-name dev
AccessKeyId: AKIA_DEV_KEY
SecretAccessKey: dev_secret_key Step 2: Attach an inline policy allowing S3 read access
$ awsemu iam put-user-policy \
--user-name dev \
--policy-name s3-read \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
]
}]
}' Step 3: List objects succeeds (s3:ListBucket is allowed)
$ AWS_ACCESS_KEY_ID=AKIA_DEV_KEY \
AWS_SECRET_ACCESS_KEY=dev_secret_key \
awsemu s3 ls s3://my-bucket/
2026-04-10 12:00:00 1024 file.txt Step 4: Delete object fails (s3:DeleteObject is not in the policy)
$ AWS_ACCESS_KEY_ID=AKIA_DEV_KEY \
AWS_SECRET_ACCESS_KEY=dev_secret_key \
awsemu s3 rm s3://my-bucket/file.txt
An error occurred (AccessDenied): User arn:aws:iam::000000000000:user/dev
is not authorized to perform s3:DeleteObject on resource
arn:aws:s3:::my-bucket/file.txt Resource-Based Policies
Services like S3, SQS, SNS, and KMS support resource-based policies. These are evaluated alongside identity-based policies during the enforcement check.
# S3 bucket policy allowing cross-account access
$ awsemu s3api put-bucket-policy \
--bucket shared-data \
--policy '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::000000000000:user/reader"},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::shared-data/*"
}]
}'