Published on

AWS Identity Management: Understanding Users, Groups, Roles, and Policies

Authors

When building applications on AWS, managing who can access what resources is critical for security and compliance. AWS Identity and Access Management (IAM) provides the foundation for access control in your AWS environment. Understanding these core IAM components - Users, Groups, Roles, and Policies - is essential for any cloud developer or administrator.

The Building Blocks of AWS Identity Management

"AWS IAM is the control center for managing access to AWS resources. It's where you define who can do what in your AWS environment, enabling you to implement the principle of least privilege."

Let's break down each component and understand how they work together to create a secure and manageable environment.

IAM Users: Individual Identities

IAM Users are individual identities that represent people or services that interact with your AWS resources. Each user has:

  • A unique name within your AWS account
  • Credentials for authentication (password and/or access keys)
  • Permissions granted through direct policy attachments or group memberships

When to Create IAM Users

✅ Create IAM Users for

  • Individual team members requiring AWS console access
  • Services that need programmatic access to AWS resources
  • Providing limited access to contractors
  • Audit tracking of individual actions

❌ Don't Create IAM Users for

  • Applications running on EC2 instances (use Roles instead)
  • Cross-account access (use Roles)
  • Temporary access needs (use Roles with STS)
  • Anonymous or unauthenticated access (use other mechanisms)

Creating and Managing IAM Users

Here's a basic AWS CLI example to create a new IAM user:

# Create a new user
aws iam create-user --user-name developer-jane

# Attach a policy directly to the user
aws iam attach-user-policy --user-name developer-jane --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess

# Create access keys for programmatic access
aws iam create-access-key --user-name developer-jane

Security Best Practice: Limit the number of IAM users with programmatic access keys, and rotate access keys regularly. For users who only need console access, don't create access keys at all.

IAM Groups: Organizing Users

IAM Groups are collections of IAM users. Instead of managing permissions for each user individually, you can assign permissions to groups and then add users to those groups. This makes permission management more scalable and less error-prone.

Benefits of Using IAM Groups

  1. Simplified permission management: Update permissions once for multiple users
  2. Organizational clarity: Create groups that mirror your organizational structure
  3. Reduced administrative overhead: Onboard new users quickly by adding them to existing groups
  4. Consistent permissions: Ensure users with similar roles have identical access levels

Common Group Structure Examples

├── Administrators
│   ├── Full AWS access
│   └── Members: senior admins, CTO
├── Developers
│   ├── EC2, S3, RDS access
│   ├── Limited to development environments
│   └── Members: development team
├── DataScientists
│   ├── S3, Athena, SageMaker access
│   └── Members: data science team
└── ReadOnlyUsers
    ├── Read-only access to specified services
    └── Members: junior team members, auditors

Managing IAM Groups with AWS CLI

# Create a new group
aws iam create-group --group-name Developers

# Attach a policy to the group
aws iam attach-group-policy --group-name Developers --policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess

# Add a user to the group
aws iam add-user-to-group --user-name developer-jane --group-name Developers

IAM Roles: Temporary Access

IAM Roles are similar to users but are not associated with a specific person. Instead, roles are assumable by anyone or any service that needs them temporarily. This makes roles perfect for:

  1. Granting AWS services permission to act on your behalf
  2. Providing cross-account access
  3. Enabling federated identity access (e.g., Active Directory or web identity federation)
  4. Implementing temporary access for applications running on EC2

The Trust Relationship

What makes roles unique is their trust relationship, which defines who can assume the role. A trust relationship is a policy document that specifies:

  • Which entities can assume the role
  • Under what conditions they can assume it
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Common Use Cases for IAM Roles

🖥️ EC2 Instance Roles

Allow applications running on EC2 instances to make API requests without storing access keys. Credentials are automatically rotated and delivered through the instance metadata service.

🔄 Cross-Account Access

Enable users from one AWS account to access resources in another AWS account securely, without needing to create users in multiple accounts.

🔐 Federated Access

Allow users from your corporate directory or web identity providers (Google, Facebook, etc.) to access AWS resources securely without creating IAM users.

⚙️ Service Roles

Permit AWS services like Lambda or CloudFormation to perform actions on your behalf, such as creating resources or accessing other AWS services.

Creating and Assuming Roles

Here's how to create a role for EC2 instances using AWS CLI:

# Create a role trust policy file first (trust-policy.json)
# Then create the role
aws iam create-role \
  --role-name S3AccessRole \
  --assume-role-policy-document file://trust-policy.json

# Attach policy to the role
aws iam attach-role-policy \
  --role-name S3AccessRole \
  --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess

To assume a role programmatically:

import boto3

# Create an STS client
sts_client = boto3.client('sts')

# Assume role
assumed_role = sts_client.assume_role(
    RoleArn="arn:aws:iam::123456789012:role/CrossAccountRole",
    RoleSessionName="AssumeRoleSession"
)

# Get temporary credentials
credentials = assumed_role['Credentials']

# Use these credentials to create a new session
s3_client = boto3.client(
    's3',
    aws_access_key_id=credentials['AccessKeyId'],
    aws_secret_access_key=credentials['SecretAccessKey'],
    aws_session_token=credentials['SessionToken']
)

# Now use s3_client with the assumed role permissions

IAM Policies: The Permission Language

IAM Policies are JSON documents that define permissions. They specify what actions are allowed or denied on which AWS resources and under what conditions. Policies can be attached to users, groups, or roles.

Types of IAM Policies

  1. Identity-based policies: Attached directly to IAM identities (users, groups, roles)
  2. Resource-based policies: Attached to resources like S3 buckets or SQS queues
  3. Service control policies (SCPs): Used in AWS Organizations to set permission boundaries
  4. Permission boundaries: Set the maximum permissions an IAM entity can have

Anatomy of an IAM Policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowS3ListBuckets",
      "Effect": "Allow",
      "Action": "s3:ListAllMyBuckets",
      "Resource": "*"
    },
    {
      "Sid": "AllowS3AccessToSpecificBucket",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::my-bucket/*",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": "203.0.113.0/24"
        }
      }
    }
  ]
}

Policy Elements Explained

  • Version: Policy language version (always "2012-10-17" for current policies)
  • Statement: Array of individual permission statements
  • Sid: Optional identifier for the statement
  • Effect: Whether to "Allow" or "Deny" access
  • Action: The specific API operations allowed or denied
  • Resource: The AWS resources to which the actions apply
  • Condition: Optional conditions for when the policy is in effect

AWS Managed vs. Customer Managed Policies

FeatureAWS ManagedCustomer Managed
Created byAWSYou
MaintenanceAWS updates automaticallyYou must update manually
CustomizationNo modifications allowedFully customizable
Common use caseStandard job functionsCustom requirements

Putting It All Together: IAM Best Practices

Implementing Least Privilege

The principle of least privilege is the foundation of secure IAM management. Grant only the permissions required for specific tasks, and nothing more. Here's a practical approach:

  1. Start with minimum permissions
  2. Analyze AWS CloudTrail logs to identify additional required permissions
  3. Gradually add permissions as needed
  4. Use AWS Access Analyzer to identify unused permissions

IAM Security Checklist

Essential IAM Security Best Practices:

  • Secure the root account with MFA and minimal usage
  • Implement MFA for all IAM users
  • Rotate credentials regularly
  • Remove unused users and permissions
  • Use roles for EC2 instances instead of embedding credentials
  • Use groups to assign permissions to users
  • Establish a strong password policy
  • Use conditions in policies to restrict access further
  • Monitor and audit IAM usage with CloudTrail

Real-World IAM Architecture Example

Let's look at a typical IAM structure for a mid-sized development team:

├── IAM Users
│   ├── Individual team members
│   └── Service accounts
├── IAM Groups
│   ├── Administrators
│   ├── Developers
│   ├── DataTeam
│   └── ReadOnly
├── IAM Roles
│   ├── EC2-App-Role
│   ├── Lambda-Execution-Role
│   ├── Cross-Account-Access
│   └── Federated-User-Role
└── IAM Policies
    ├── AWS Managed Policies
    └── Customer Managed Policies
        ├── Dev-Environment-Access
        ├── Prod-ReadOnly-Access
        └── Database-Management

Implementation with Infrastructure as Code

Here's a Terraform example to create this IAM structure:

# Create IAM groups
resource "aws_iam_group" "developers" {
  name = "Developers"
}

resource "aws_iam_group" "admins" {
  name = "Administrators"
}

# Create IAM policies
resource "aws_iam_policy" "dev_environment_access" {
  name        = "DevelopmentEnvironmentAccess"
  description = "Permissions for development environment resources"
  policy      = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect    = "Allow"
        Action    = [
          "ec2:Describe*",
          "s3:Get*",
          "s3:List*",
          "rds:Describe*"
        ]
        Resource  = "*"
        Condition = {
          StringEquals = {
            "aws:RequestedRegion": "us-west-2"
          }
          StringLike = {
            "aws:ResourceTag/Environment": "Dev"
          }
        }
      }
    ]
  })
}

# Attach policy to group
resource "aws_iam_group_policy_attachment" "dev_policy" {
  group      = aws_iam_group.developers.name
  policy_arn = aws_iam_policy.dev_environment_access.arn
}

# Create IAM role for EC2
resource "aws_iam_role" "app_role" {
  name = "EC2-App-Role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect    = "Allow"
        Principal = {
          Service = "ec2.amazonaws.com"
        }
        Action    = "sts:AssumeRole"
      }
    ]
  })
}

# Attach policy to role
resource "aws_iam_role_policy_attachment" "app_s3_access" {
  role       = aws_iam_role.app_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
}

Advanced IAM Topics

Permission Boundaries

Permission boundaries are an advanced feature that set the maximum permissions an IAM entity can have. They're useful for delegating IAM administration while maintaining control over maximum access levels.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:*",
        "cloudwatch:*",
        "ec2:*"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Deny",
      "Action": [
        "iam:*",
        "organizations:*"
      ],
      "Resource": "*"
    }
  ]
}

Resource-Based Policies

In addition to identity-based policies, some AWS services support resource-based policies. These are attached directly to resources rather than IAM identities. S3 bucket policies are a common example:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:user/developer-jane"
      },
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::my-bucket/*"
    }
  ]
}

Conclusion

AWS IAM provides a robust framework for managing access to your AWS resources. By understanding and properly implementing Users, Groups, Roles, and Policies, you can create a secure yet flexible environment that follows the principle of least privilege.

Remember that IAM is not a set-it-and-forget-it service. Regular reviews, ongoing adjustments, and continuous monitoring are essential parts of maintaining a secure AWS environment as your applications and organization evolve.

Last updated: Monday, April 28, 2025