Understanding AWS IAM Policies: A Practical Guide
A developer's guide to the core components of an AWS IAM policy. Learn how to read and write policies by understanding Effect, Principal, Action, Resource, and Condition.
In AWS, everything revolves around permissions. IAM (Identity and Access Management) is the service that controls who can do what in your AWS account. The way you define these permissions is with IAM Policies.
An IAM policy is a JSON document that explicitly lists permissions. Understanding the structure of this document is a fundamental skill for any AWS developer, as it's the key to building secure and robust applications.
The Structure of an IAM Policy
At its core, every IAM policy is a JSON object containing one or more statements. Each statement is a formal declaration of a single permission.
Let's break down the main components of a statement:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-app-bucket/*",
"Condition": {
"StringEquals": {
"aws:SourceIp": "203.0.113.0/24"
}
}
}
]
}
1. Effect
: Allow or Deny
This is the simplest part. The Effect
is either Allow
or Deny
.
Allow
: Grants the permission.Deny
: Explicitly forbids the permission.
An explicit Deny
always overrides an Allow
. This is a crucial security principle. If a user has a policy that allows access to S3 but another policy that denies it, the Deny
will win.
2. Principal
(Only in Resource-Based Policies)
The Principal
is the user, account, service, or other entity that is allowed or denied access.
Important: The Principal
element is only used in resource-based policies (like an S3 bucket policy or an IAM role's trust policy). It is not used in identity-based policies (policies you attach directly to a user or a group).
"Principal": { "AWS": "arn:aws:iam::123456789012:root" } // A specific AWS account
"Principal": { "Service": "ec2.amazonaws.com" } // An AWS service
3. Action
: What You Can Do
The Action
describes the specific API call(s) that are allowed or denied. Actions are always prefixed with the service name.
s3:GetObject
: The action to read an object from S3.dynamodb:PutItem
: The action to write an item to DynamoDB.ec2:RunInstances
: The action to launch a new EC2 instance.
You can use wildcards (*
) to grant multiple permissions.
s3:*
: All actions for S3.ec2:Describe*
: All actions that start withDescribe
in EC2.
4. Resource
: What You Can Do It To
The Resource
specifies the object or objects that the statement covers. Resources are always specified by their ARN (Amazon Resource Name).
An ARN is a globally unique identifier for an AWS resource.
arn:partition:service:region:account-id:resource-id
arn:aws:s3:::my-app-bucket/*
arn:aws:dynamodb:us-east-1:123456789012:table/MyTable
Like with Action
, you can use wildcards in the Resource
element.
5. Condition
(Optional): When You Can Do It
The Condition
element is an optional but powerful way to add fine-grained control. It lets you specify circumstances under which the policy is in effect.
Example: Only allow access if the request is coming from a specific IP address range.
"Condition": {
"IpAddress": {
"aws:SourceIp": "203.0.113.0/24"
}
}
Example: Only allow writing an S3 object if it has a specific tag.
"Condition": {
"StringEquals": {
"s3:x-amz-object-tagging": "department=finance"
}
}
Putting It All Together: A Real-World Example
Let's write a policy that we would attach to an IAM user. This policy will grant the user read-only access to a specific S3 bucket.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowListBucket",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-data-bucket"
},
{
"Sid": "AllowReadObjects",
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-data-bucket/*"
}
]
}
This policy has two statements:
- The first statement allows the user to list the contents of the
my-data-bucket
. - The second statement allows the user to get (read) any object inside that bucket.
This separation is necessary because s3:ListBucket
is an action on the bucket itself, while s3:GetObject
is an action on the objects within the bucket.
Conclusion
IAM policies are the foundation of security in AWS. By mastering the five key components—Effect
, Principal
, Action
, Resource
, and Condition
—you can move beyond using overly permissive, AWS-managed policies and start writing fine-grained, least-privilege policies that grant exactly the permissions needed, and no more. This is a critical skill for building secure and professional applications in the cloud.