Chapter 03: Deep Dive into IAM Roles

Deep dive into AWS IAM role mechanisms, including service roles, cross-account access, and federated identity advanced applications

Haiyue
22min

Chapter 03: Deep Dive into IAM Roles

Learning Objectives

  1. Understand IAM role concepts and use cases
  2. Create and configure service roles
  3. Set up cross-account role access
  4. Configure federated identity roles
  5. Master role switching and temporary credentials

Role System Architecture Diagram

🔄 正在渲染 Mermaid 图表...

3.1 IAM Role Fundamentals

3.1.1 Differences Between Roles and Users

tip Core Differences

FeatureIAM UserIAM Role
Identity TypePermanentTemporary
CredentialsLong-termTemporary
Use CaseHuman AccessService-to-Service
Permission AcquisitionDirect AttachmentAssume to Get
SecurityRequires Key ManagementAuto Rotation

3.1.2 How Roles Work

🔄 正在渲染 Mermaid 图表...

3.1.3 Creating Basic IAM Roles

import boto3
import json
from datetime import datetime, timedelta

def create_basic_role(role_name, trusted_entity, description=""):
    """
    Create basic IAM role

    Args:
        role_name: Role name
        trusted_entity: Trusted entity (service or account)
        description: Role description
    """
    iam = boto3.client('iam')

    # Basic trust policy templates
    trust_policy_templates = {
        'ec2': {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "ec2.amazonaws.com"
                    },
                    "Action": "sts:AssumeRole"
                }
            ]
        },
        'lambda': {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "lambda.amazonaws.com"
                    },
                    "Action": "sts:AssumeRole"
                }
            ]
        },
        'cross-account': {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "AWS": f"arn:aws:iam::{trusted_entity}:root"
                    },
                    "Action": "sts:AssumeRole"
                }
            ]
        }
    }

    try:
        # Select trust policy
        if trusted_entity in trust_policy_templates:
            trust_policy = trust_policy_templates[trusted_entity]
        elif trusted_entity.startswith('arn:aws:iam::'):
            # Cross-account role
            trust_policy = {
                "Version": "2012-10-17",
                "Statement": [
                    {
                        "Effect": "Allow",
                        "Principal": {
                            "AWS": trusted_entity
                        },
                        "Action": "sts:AssumeRole"
                    }
                ]
            }
        else:
            # Custom service
            trust_policy = {
                "Version": "2012-10-17",
                "Statement": [
                    {
                        "Effect": "Allow",
                        "Principal": {
                            "Service": trusted_entity
                        },
                        "Action": "sts:AssumeRole"
                    }
                ]
            }

        # Create role
        response = iam.create_role(
            RoleName=role_name,
            AssumeRolePolicyDocument=json.dumps(trust_policy),
            Description=description,
            MaxSessionDuration=3600,  # 1 hour
            Tags=[
                {
                    'Key': 'CreatedBy',
                    'Value': 'IAM-Automation'
                },
                {
                    'Key': 'CreatedDate',
                    'Value': datetime.now().strftime('%Y-%m-%d')
                }
            ]
        )

        print(f"✅ Role {role_name} created successfully")
        print(f"   ARN: {response['Role']['Arn']}")

        return response['Role']

    except Exception as e:
        print(f"❌ Failed to create role: {str(e)}")
        return None

# Examples of creating different types of roles
ec2_role = create_basic_role("EC2-S3-Access-Role", "ec2", "Role for EC2 instances to access S3")
lambda_role = create_basic_role("Lambda-Execution-Role", "lambda", "Lambda function execution role")

3.2 Service Roles Explained

3.2.1 EC2 Instance Roles

def create_ec2_instance_role():
    """
    Create EC2 instance role and instance profile
    """
    iam = boto3.client('iam')

    # 1. Create role
    trust_policy = {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Principal": {
                    "Service": "ec2.amazonaws.com"
                },
                "Action": "sts:AssumeRole"
            }
        ]
    }

    try:
        # Create role
        role_response = iam.create_role(
            RoleName='EC2-WebServer-Role',
            AssumeRolePolicyDocument=json.dumps(trust_policy),
            Description='Role for EC2 web servers with S3 and CloudWatch access'
        )

        role_arn = role_response['Role']['Arn']
        print(f"✅ EC2 role created successfully: {role_arn}")

        # 2. Attach AWS managed policies
        managed_policies = [
            'arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess',
            'arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy'
        ]

        for policy_arn in managed_policies:
            iam.attach_role_policy(
                RoleName='EC2-WebServer-Role',
                PolicyArn=policy_arn
            )
            print(f"✅ Policy attached: {policy_arn}")

        # 3. Create custom policy
        custom_policy = {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Action": [
                        "s3:PutObject",
                        "s3:PutObjectAcl"
                    ],
                    "Resource": "arn:aws:s3:::my-web-logs/*"
                },
                {
                    "Effect": "Allow",
                    "Action": [
                        "ssm:GetParameter",
                        "ssm:GetParameters",
                        "ssm:GetParametersByPath"
                    ],
                    "Resource": "arn:aws:ssm:*:*:parameter/webserver/*"
                }
            ]
        }

        policy_response = iam.create_policy(
            PolicyName='EC2-WebServer-CustomPolicy',
            PolicyDocument=json.dumps(custom_policy),
            Description='Custom policy for web server specific permissions'
        )

        # Attach custom policy
        iam.attach_role_policy(
            RoleName='EC2-WebServer-Role',
            PolicyArn=policy_response['Policy']['Arn']
        )

        # 4. Create instance profile
        iam.create_instance_profile(
            InstanceProfileName='EC2-WebServer-Profile'
        )

        # 5. Add role to instance profile
        iam.add_role_to_instance_profile(
            InstanceProfileName='EC2-WebServer-Profile',
            RoleName='EC2-WebServer-Role'
        )

        print("✅ EC2 instance profile created")

        # 6. Example of using role when launching EC2 instance
        ec2_launch_example = """
        # Specify instance profile when launching EC2 instance
        import boto3

        ec2 = boto3.client('ec2')

        response = ec2.run_instances(
            ImageId='ami-0abcdef1234567890',
            MinCount=1,
            MaxCount=1,
            InstanceType='t3.micro',
            IamInstanceProfile={
                'Name': 'EC2-WebServer-Profile'
            },
            SecurityGroupIds=['sg-12345678'],
            SubnetId='subnet-12345678'
        )
        """

        print("📝 EC2 instance launch example:")
        print(ec2_launch_example)

        return role_arn

    except Exception as e:
        print(f"❌ Failed to create EC2 role: {str(e)}")
        return None

# Test permissions in EC2 role
def test_ec2_role_permissions():
    """
    Sample code to test role permissions in EC2 instance
    """
    test_code = """
    # This code should run in EC2 instance
    import boto3

    # EC2 instance automatically uses attached IAM role
    s3 = boto3.client('s3')
    ssm = boto3.client('ssm')

    try:
        # Test S3 read permission
        buckets = s3.list_buckets()
        print(f"✅ S3 access successful, found {len(buckets['Buckets'])} buckets")

        # Test SSM parameter access
        parameters = ssm.get_parameters_by_path(
            Path='/webserver/',
            Recursive=True
        )
        print(f"✅ SSM parameter access successful, found {len(parameters['Parameters'])} parameters")

        # Test uploading logs to S3
        s3.put_object(
            Bucket='my-web-logs',
            Key=f'access-logs/{datetime.now().strftime("%Y/%m/%d")}/access.log',
            Body='Sample log content'
        )
        print("✅ Log upload to S3 successful")

    except Exception as e:
        print(f"❌ Permission test failed: {str(e)}")
    """

    print("📝 Permission test code in EC2 instance:")
    print(test_code)

create_ec2_instance_role()
test_ec2_role_permissions()

3.2.2 Lambda Execution Roles

def create_lambda_execution_role(function_name, additional_permissions=None):
    """
    Create Lambda function execution role

    Args:
        function_name: Lambda function name
        additional_permissions: List of additional permissions
    """
    iam = boto3.client('iam')

    role_name = f"Lambda-{function_name}-ExecutionRole"

    # Lambda basic trust policy
    trust_policy = {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Principal": {
                    "Service": "lambda.amazonaws.com"
                },
                "Action": "sts:AssumeRole"
            }
        ]
    }

    try:
        # 1. Create role
        role_response = iam.create_role(
            RoleName=role_name,
            AssumeRolePolicyDocument=json.dumps(trust_policy),
            Description=f'Execution role for Lambda function {function_name}'
        )

        role_arn = role_response['Role']['Arn']
        print(f"✅ Lambda execution role created successfully: {role_arn}")

        # 2. Attach basic Lambda execution policy
        iam.attach_role_policy(
            RoleName=role_name,
            PolicyArn='arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
        )

        # 3. Attach additional permissions as needed
        if additional_permissions:
            # Create custom policy
            custom_policy = {
                "Version": "2012-10-17",
                "Statement": additional_permissions
            }

            policy_response = iam.create_policy(
                PolicyName=f'Lambda-{function_name}-CustomPolicy',
                PolicyDocument=json.dumps(custom_policy),
                Description=f'Custom permissions for Lambda function {function_name}'
            )

            # Attach custom policy
            iam.attach_role_policy(
                RoleName=role_name,
                PolicyArn=policy_response['Policy']['Arn']
            )

            print(f"✅ Custom policy attached")

        return role_arn

    except Exception as e:
        print(f"❌ Failed to create Lambda role: {str(e)}")
        return None

# Create Lambda roles for specific scenarios
def create_specialized_lambda_roles():
    """
    Create Lambda roles for specific scenarios
    """

    # 1. S3 processing Lambda role
    s3_permissions = [
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::my-lambda-bucket/*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": "arn:aws:s3:::my-lambda-bucket"
        }
    ]

    s3_role = create_lambda_execution_role("S3Processor", s3_permissions)

    # 2. DynamoDB processing Lambda role
    dynamodb_permissions = [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:GetItem",
                "dynamodb:PutItem",
                "dynamodb:UpdateItem",
                "dynamodb:DeleteItem",
                "dynamodb:Query",
                "dynamodb:Scan"
            ],
            "Resource": "arn:aws:dynamodb:*:*:table/MyTable*"
        }
    ]

    dynamodb_role = create_lambda_execution_role("DynamoDBProcessor", dynamodb_permissions)

    # 3. SNS/SQS processing Lambda role
    messaging_permissions = [
        {
            "Effect": "Allow",
            "Action": [
                "sns:Publish"
            ],
            "Resource": "arn:aws:sns:*:*:MyTopic"
        },
        {
            "Effect": "Allow",
            "Action": [
                "sqs:ReceiveMessage",
                "sqs:DeleteMessage",
                "sqs:GetQueueAttributes",
                "sqs:SendMessage"
            ],
            "Resource": "arn:aws:sqs:*:*:MyQueue"
        }
    ]

    messaging_role = create_lambda_execution_role("MessagingProcessor", messaging_permissions)

    print("\n📋 Lambda roles creation summary:")
    print(f"  S3 processing role: {s3_role}")
    print(f"  DynamoDB processing role: {dynamodb_role}")
    print(f"  Messaging processing role: {messaging_role}")

create_specialized_lambda_roles()

3.3 Cross-Account Role Access

3.3.1 Setting Up Cross-Account Roles

def create_cross_account_role(trusted_account_id, role_name, external_id=None):
    """
    Create cross-account access role

    Args:
        trusted_account_id: Trusted AWS account ID
        role_name: Role name
        external_id: External ID (optional, enhances security)
    """
    iam = boto3.client('iam')

    # Build trust policy
    trust_statement = {
        "Effect": "Allow",
        "Principal": {
            "AWS": f"arn:aws:iam::{trusted_account_id}:root"
        },
        "Action": "sts:AssumeRole"
    }

    # Add condition if external ID provided
    if external_id:
        trust_statement["Condition"] = {
            "StringEquals": {
                "sts:ExternalId": external_id
            }
        }

    trust_policy = {
        "Version": "2012-10-17",
        "Statement": [trust_statement]
    }

    try:
        # Create cross-account role
        role_response = iam.create_role(
            RoleName=role_name,
            AssumeRolePolicyDocument=json.dumps(trust_policy),
            Description=f'Cross-account role for account {trusted_account_id}',
            MaxSessionDuration=3600,
            Tags=[
                {
                    'Key': 'Type',
                    'Value': 'CrossAccount'
                },
                {
                    'Key': 'TrustedAccount',
                    'Value': trusted_account_id
                }
            ]
        )

        role_arn = role_response['Role']['Arn']
        print(f"✅ Cross-account role created successfully: {role_arn}")

        # Attach example permission policy
        cross_account_policy = {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Action": [
                        "s3:ListBucket",
                        "s3:GetObject"
                    ],
                    "Resource": [
                        "arn:aws:s3:::shared-data-bucket",
                        "arn:aws:s3:::shared-data-bucket/*"
                    ]
                },
                {
                    "Effect": "Allow",
                    "Action": [
                        "ec2:DescribeInstances",
                        "ec2:DescribeSecurityGroups"
                    ],
                    "Resource": "*"
                }
            ]
        }

        # Create and attach policy
        policy_response = iam.create_policy(
            PolicyName=f'{role_name}-Policy',
            PolicyDocument=json.dumps(cross_account_policy),
            Description=f'Policy for cross-account role {role_name}'
        )

        iam.attach_role_policy(
            RoleName=role_name,
            PolicyArn=policy_response['Policy']['Arn']
        )

        print(f"✅ Cross-account policy attached")

        # Provide usage instructions
        usage_info = f"""
        📝 Cross-Account Role Usage Instructions:

        1. Role ARN: {role_arn}
        2. External ID: {external_id if external_id else 'None'}
        3. Trusted Account: {trusted_account_id}

        Use this role in trusted account:

        import boto3

        sts = boto3.client('sts')

        # Assume role
        response = sts.assume_role(
            RoleArn='{role_arn}',
            RoleSessionName='CrossAccountSession'{external_id_param}
        )

        # Use temporary credentials
        credentials = response['Credentials']

        s3 = boto3.client(
            's3',
            aws_access_key_id=credentials['AccessKeyId'],
            aws_secret_access_key=credentials['SecretAccessKey'],
            aws_session_token=credentials['SessionToken']
        )
        """

        external_id_param = f",\n            ExternalId='{external_id}'" if external_id else ""

        print(usage_info.format(
            role_arn=role_arn,
            external_id_param=external_id_param
        ))

        return role_arn

    except Exception as e:
        print(f"❌ Failed to create cross-account role: {str(e)}")
        return None

def assume_cross_account_role(role_arn, session_name, external_id=None, duration=3600):
    """
    Assume cross-account role

    Args:
        role_arn: Role ARN
        session_name: Session name
        external_id: External ID
        duration: Session duration (seconds)
    """
    sts = boto3.client('sts')

    try:
        # Build AssumeRole parameters
        assume_role_params = {
            'RoleArn': role_arn,
            'RoleSessionName': session_name,
            'DurationSeconds': duration
        }

        if external_id:
            assume_role_params['ExternalId'] = external_id

        # Assume role
        response = sts.assume_role(**assume_role_params)

        credentials = response['Credentials']
        assumed_role_user = response['AssumedRoleUser']

        print(f"✅ Successfully assumed role")
        print(f"   User ARN: {assumed_role_user['Arn']}")
        print(f"   Credentials expiration time: {credentials['Expiration']}")

        # Create client using temporary credentials
        temp_session = boto3.Session(
            aws_access_key_id=credentials['AccessKeyId'],
            aws_secret_access_key=credentials['SecretAccessKey'],
            aws_session_token=credentials['SessionToken']
        )

        # Test permissions
        s3_client = temp_session.client('s3')
        try:
            buckets = s3_client.list_buckets()
            print(f"✅ S3 access test successful, can access {len(buckets['Buckets'])} buckets")
        except Exception as e:
            print(f"⚠️ S3 access test failed: {str(e)}")

        return temp_session

    except Exception as e:
        print(f"❌ Failed to assume role: {str(e)}")
        return None

# Example of creating cross-account role
cross_account_role = create_cross_account_role(
    trusted_account_id="123456789012",
    role_name="CrossAccount-DataAccess-Role",
    external_id="unique-external-id-12345"
)

Summary

This chapter provided an in-depth introduction to various aspects of AWS IAM roles:

  1. Role Fundamentals: Understanding the differences between roles and users and how they work
  2. Service Roles: Creating and configuring EC2 instance roles and Lambda execution roles
  3. Cross-Account Access: Secure cross-account role setup and advanced condition configuration
  4. Federated Identity: Implementation of SAML 2.0 and OIDC federated identity
  5. Session Management: Using temporary credentials and session policies
  6. Security Best Practices: Role permission auditing and monitoring alerts

Through this chapter, you should be able to:

  • Proficiently create and manage various types of IAM roles
  • Implement secure cross-account access
  • Configure federated identity integration
  • Manage role sessions and temporary credentials
  • Audit and monitor role usage

In the next chapter, we will learn the detailed syntax and advanced features of IAM policies.