An AWS IAM policy is a JSON document that defines Allow or Deny permissions on AWS resources. AWS-managed policies cover common cases (S3 read-only, Lambda basic execution). Customer-managed policies are reusable across roles/users. Inline policies live on a single identity. The team that follows least-privilege (specific actions, specific resources, no * unless required) has the right posture per the AWS IAM docs.
Table of contents
- Policy structure
- AWS managed vs customer managed vs inline
- The action format
- The resource ARN format
- The condition block
- Policy evaluation logic
- The policy generator
- FAQ
Policy structure
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "AllowS3ReadOnly",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
]
}]
]
}
The team that reads JSON policies fluently debugs permissions issues faster than the team that uses the policy generator without understanding.
AWS managed vs customer managed vs inline
- AWS managed:
arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess. AWS-curated. The team that uses these as building blocks gets best-practice permissions; the team that relies solely on them gets coarse-grained access (often too broad). - Customer managed: defined by your team, versioned, reusable. The team that uses customer managed policies has DRY, auditable permission sets.
- Inline: embedded in a single user/group/role. The team that uses inline policies has permissions scattered across identities, hard to audit.
The action format
Actions follow service:Action:
s3:GetObject- read an objects3:PutObject- write an objects3:*- all S3 actions (avoid this)ec2:DescribeInstances- list instancesiam:CreateUser- create IAM user
The team that uses specific actions (s3:GetObject) has least-privilege. The team that uses wildcards (s3:*) has admin-equivalent S3 access.
The resource ARN format
ARNs follow arn:partition:service:region:account:resource:
arn:aws:s3:::my-bucket(no region/account for S3)arn:aws:s3:::my-bucket/*(objects in bucket)arn:aws:ec2:us-east-1:123456789012:instance/i-12345(specific EC2 instance)*(any resource - use sparingly)
The team that uses specific ARNs (arn:aws:s3:::my-bucket/*) limits blast radius. The team that uses * allows the action on all resources of that type.
The condition block
Conditions add fine-grained access control:
"Condition": {
"StringEquals": {
"aws:RequestedRegion": ["us-east-1", "us-west-2"]
},
"IpAddress": {
"aws:SourceIp": "203.0.113.0/24"
},
"DateGreaterThan": {
"aws:CurrentTime": "2026-01-01T00:00:00Z"
}
}
The team that uses conditions has time-bound, IP-bound, MFA-bound permissions - the kind of controls SOC2 auditors look for.
Policy evaluation logic
- By default, all requests are denied.
- Explicit Allow in any applicable policy permits the request.
- Explicit Deny in any applicable policy blocks the request (Deny wins).
- SCPs (org-level), permissions boundaries, and session policies further restrict.
The team that thinks ‘Allow overrides Deny’ is wrong. The team that uses explicit Deny for sensitive actions (iam:DeleteUser, s3:DeleteBucket) has belt-and-suspenders.
The policy generator
AWS Policy Generator builds policies from selections. The team that uses it as a starting point and then refines to specific ARNs has faster authoring. The team that uses the generated policy verbatim has the right starting permissions but possibly over-broad.
FAQ
What’s the difference between Allow and Deny in IAM?
Allow grants permission; Deny explicitly blocks. Deny wins over Allow. The team that uses Deny for sensitive operations (s3:DeleteBucket, iam:CreateAccessKey) has hardening beyond what Allow-only policies provide.
Should I use * in Action or Resource?
Avoid * unless explicitly required. The team that uses s3:GetObject on arn:aws:s3:::specific-bucket/* has specific permissions; the team that uses s3:* on * has admin-level S3 access.
What’s a permissions boundary?
A permissions boundary is a managed policy that defines the maximum permissions an identity can have. The team that uses boundaries for high-privilege roles (admin, devops) has a safety net even if the role’s own policy is too broad.
How do I know which actions a service supports?
The IAM JSON policy reference lists every action, resource, and condition key for every service. The team that checks this for new services has correct permissions from day one.
What’s the difference between a policy and a permission set?
Policy = the JSON document. Permission set (AWS SSO) = a bundle of policies assigned to SSO users. The team that uses permission sets has centralized control across many SSO users.
If you are sizing the infrastructure for the kind of project this post covers, the RunxBuild hosting calculator is the right place to model the line items. The compute, the memory, the storage, the bandwidth, the database - each one is a separate number, and the team’s mental model for the platform is the sum of those numbers. The RunxBuild dashboard is where the team sees the actual usage in one place.
Useful related references: