Post

How to Read AWS IAM Policies Like a Pro

How to Read AWS IAM Policies Like a Pro

AWS Identity and Access Management (IAM) policies are the backbone of access control in AWS. But let’s be honest: IAM policies can look intimidating at first. They are written in JSON, filled with permissions jargon, and one wrong setting can lock you out of your own resources.

In this post, we’ll break down how to read and understand AWS IAM policies, piece by piece. We’ll also share a few tips to spot common mistakes and understand what a policy really allows (or denies). By the end, you’ll be able to approach an IAM policy with confidence.


1. Understanding the Key Concepts

Before diving into IAM policies, it’s important to understand a few core AWS concepts.

1.1 Account

An AWS account is the fundamental container for all your AWS resources. Every account has its own:

  • Billing and payment settings
  • Resources (like EC2 instances, S3 buckets)
  • Security boundaries

Each AWS account has a well-defined security boundary. By default, resources, users, and permissions are confined within the account. Accounts are isolated from each other unless explicitly connected. Explicit connections include:

  • Cross-account IAM roles: Allow an identity (user or service) in one AWS account to assume a role in another account, gaining temporary permissions.
  • VPC Peering or PrivateLink: Establish private network connectivity between resources in different AWS accounts without traversing the public internet.
  • Resource policies with cross-account access: Directly attach policies to resources (like S3 buckets or KMS keys) that permit access from another AWS account.

These explicit connections must be deliberately configured, ensuring isolation is preserved unless intentional collaboration is set up.

1.2 IAM (Identity and Access Management)

IAM is the service AWS provides to manage access to AWS resources securely. IAM helps you:

  • Create and manage AWS users and groups
  • Assign permissions to control which resources users and groups can access

IAM operates within the boundary of an AWS account but can also interact across accounts with features like roles and cross-account access.

1.3 IAM User, Group, and Role

  • User: Represents a person or application. Each user has long-term credentials like a password or access keys.
  • Group: A collection of IAM users. You can attach policies to a group to apply the same permissions to all members.
  • Role: A set of permissions that can be assumed by a user, application, or service. Roles are temporary and don’t have long-term credentials. They are powerful for cross-account access or service-to-service communication.

Common examples and use cases for roles include:

  • Developer Role: Grants developers access to necessary AWS services like S3, EC2, and DynamoDB for development and testing. Typically full access in non-production environments and restricted or read-only access in production.
  • Read-Only Role: Allows users to view AWS resources without modifying or deleting them. Commonly used for auditors, compliance officers, or business analysts.
  • EC2 Instance Role: Assign a role to an EC2 instance to allow it to access other AWS services like S3 or DynamoDB without embedding long-term credentials in the code.
  • Lambda Execution Role: Provide AWS Lambda functions the necessary permissions to interact with AWS services, such as reading from an S3 bucket or writing to CloudWatch Logs.
  • Cross-Account Role: Enable users or services in one AWS account to access resources in another account securely.
  • Federated User Role: Allow users authenticated via an external identity provider (like Google Workspace or Active Directory) to assume roles and access AWS resources temporarily.

1.4 Principal

A Principal is the entity that is allowed or denied access to a resource. It can be:

  • An IAM user
  • An IAM role
  • A federated user (someone accessing via an external identity provider)

In a policy, Principal defines who the policy applies to.

1.5 Action

The Action element describes what the principal is allowed or denied to do. Each AWS service has its own set of actions. For example:

  • s3:PutObject — Upload an object to an S3 bucket
  • ec2:StartInstances — Start an EC2 instance

Actions are specific to services and control the operations allowed.

1.6 Resource

The Resource element specifies which AWS resources the action applies to. Resources are defined by their Amazon Resource Name (ARN).

Examples:

  • An S3 bucket: arn:aws:s3:::my-bucket
  • An EC2 instance: arn:aws:ec2:us-west-2:123456789012:instance/i-1234567890abcdef0

In short:

  • Account: Container for all your AWS assets
  • IAM: Service to control access
  • Principal: Who is trying to access
  • Action: What they want to do
  • Resource: What they want to act on

2. Anatomy of an IAM Policy

Here is a basic IAM policy:

1
2
3
4
5
6
7
8
9
10
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::example-bucket"
    }
  ]
}

Let’s break this down:

  • Version: Policy language version. Almost always 2012-10-17. AWS uses this to interpret the policy.
  • Statement: The main block that describes permissions. You can have a single statement or multiple statements inside a policy.

Each statement has:

  • Effect: Either Allow or Deny.
  • Action: The specific operation(s) permitted or denied.
  • Resource: The AWS resource(s) the action applies to.
  • Condition (optional): Additional requirements for the statement to apply.
  • Principal (resource-based policies only): Specifies who is affected.

3. Key Elements Explained

Understanding each element in depth helps in mastering IAM policies.

3.1 Effect

The Effect element controls whether the statement allows or denies access.

  • Allow: Grants permission for the specified actions.
  • Deny: Explicitly denies permission, even if another policy allows it.

Important:

  • AWS applies an implicit deny by default.
  • An explicit deny always overrides an allow.

Tip: Favor Allow policies and let the implicit deny work for everything else unless you have a specific reason to deny explicitly.

3.2 Action

The Action element specifies the operations you allow or deny.

Examples:

  • s3:GetObject — Download an object from an S3 bucket.
  • ec2:StartInstances — Start a specific EC2 instance.

You can specify multiple actions or use wildcards:

  • s3:* — Grants all S3 actions.
  • s3:Get* — Grants all Get actions on S3.

Tip: Avoid over-broad actions (*). Be as specific as possible to apply the principle of least privilege.

3.3 Resource

The Resource element specifies what the action can apply to.

AWS uses Amazon Resource Names (ARNs) to uniquely identify resources.

Example:

  • S3 bucket: arn:aws:s3:::example-bucket
  • Objects inside a bucket: arn:aws:s3:::example-bucket/*

Tip: When granting access to S3 objects, don’t forget to include /* to cover the contents of the bucket.

3.4 Principal

The Principal element identifies who the policy applies to.

  • In identity-based policies, AWS assumes the principal is the identity (user/role/group) the policy is attached to.
  • In resource-based policies, you explicitly specify the principal.

Example:

1
2
3
"Principal": {
  "AWS": "arn:aws:iam::123456789012:user/Alice"
}

Note: Resource-based policies allow cross-account access by specifying a principal from another account.

3.5 Condition

Conditions refine when a policy is effective.

Example Conditions:

  • IP restriction:
1
2
3
"Condition": {
  "IpAddress": {"aws:SourceIp": "203.0.113.0/24"}
}
  • Require MFA:
1
2
3
"Condition": {
  "Bool": {"aws:MultiFactorAuthPresent": "true"}
}

Common condition operators:

  • StringEquals
  • StringLike
  • NumericLessThan
  • IpAddress
  • Bool

Tip: Conditions make your policies much safer but can also complicate them. Review them carefully.


4. Types of IAM Policies

There are several types of IAM policies, each serving different purposes.

4.1 Identity-Based Policies

  • Attached to users, groups, or roles.
  • Define what actions those identities can perform on resources.

Example: A policy that allows a user to read from an S3 bucket.

4.2 Resource-Based Policies

  • Attached directly to a resource like an S3 bucket, Lambda function, or SQS queue.
  • Specify who (Principal) can access the resource and what they can do.

Example: A bucket policy that allows cross-account access.

4.3 Permissions Boundaries

  • Advanced feature.
  • Define the maximum permissions a user or role can have, even if identity policies grant more.

Useful for:

  • Restricting delegated administrators
  • Enforcing security policies

4.4 Service Control Policies (SCPs)

  • Used in AWS Organizations.
  • Apply to AWS accounts.
  • Set permission guardrails; they don’t grant permissions, only restrict.

Common use cases:

  • Prevent deletion of CloudTrail logs
  • Restrict creation of internet-facing resources

4.5 Session Policies

  • Passed when a role or federated identity is assumed.
  • Temporary policies that limit what a session can do.

Useful for:

  • Fine-grained access control during short-lived sessions

5. Example: Reading a Real Policy

Let’s look at a real-world example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject"
      ],
      "Resource": "arn:aws:s3:::example-bucket/*"
    },
    {
      "Effect": "Deny",
      "Action": "s3:DeleteObject",
      "Resource": "arn:aws:s3:::example-bucket/*"
    }
  ]
}

Interpretation:

  • Allow putting and getting objects in example-bucket.
  • Explicitly deny deleting objects.

Even though permissions can be granted broadly, using an explicit deny for deletion provides extra safety.


6. Common Mistakes When Reading Policies

  • Missing Resource Detail: Forgetting /* in S3 policies means you are only granting access to the bucket itself, not the objects inside.
  • Overusing Wildcards: * in actions or resources can grant more permissions than intended.
  • Ignoring Explicit Deny: Always check for explicit denies; they override allows.
  • Not Checking Conditions: Conditions can dramatically change the policy’s behavior.

7. Useful Tools to Help


8. Tips for Writing and Reading Better Policies

  • Start with least privilege.
  • Review and audit policies regularly.
  • Use specific actions and resources.
  • Add conditions to tighten access.
  • Test policies with IAM Policy Simulator.
  • Document the purpose of each policy.

9. Practice: Reading a Complex Policy

Try reading this policy:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ListAndGetS3",
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::example-bucket",
        "arn:aws:s3:::example-bucket/*"
      ]
    },
    {
      "Sid": "RestrictIP",
      "Effect": "Deny",
      "Action": "s3:*",
      "Resource": "arn:aws:s3:::example-bucket/*",
      "Condition": {
        "NotIpAddress": {
          "aws:SourceIp": "203.0.113.0/24"
        }
      }
    }
  ]
}

Interpretation:

  • Allow listing and getting objects in example-bucket.
  • Deny any S3 action if the request doesn’t come from the specified IP range.

This policy ensures secure, IP-restricted access.


10. Final Thoughts

IAM policies are powerful, but with power comes responsibility. Misconfigured policies can lead to serious security risks.

By understanding the structure and meaning behind each part of an IAM policy, you can read them like a pro. Start small, read carefully, and practice often.

And remember: when in doubt, simulate.

Happy policy reading!


Did you find this guide useful? Feel free to share it with others who want to get better at AWS security!

This post is licensed under CC BY 4.0 by the author.