AWS for DevOps — IAM & Security Exercises

3 minute read

2.6 — Practice Exercises

Test your understanding of IAM users, groups, roles, policies, MFA, and AWS Organizations.


Exercise 1 — Create a Read-Only Developer Group

Create an IAM group called ReadOnlyDevs, attach the ReadOnlyAccess managed policy, and add a user called junior-dev to it.

Show Answer
# Create the user
$ aws iam create-user --user-name junior-dev

# Create the group
$ aws iam create-group --group-name ReadOnlyDevs

# Attach the AWS managed read-only policy
$ aws iam attach-group-policy \
    --group-name ReadOnlyDevs \
    --policy-arn arn:aws:iam::aws:policy/ReadOnlyAccess

# Add the user to the group
$ aws iam add-user-to-group \
    --group-name ReadOnlyDevs \
    --user-name junior-dev

# Verify
$ aws iam list-groups-for-user --user-name junior-dev \
    --query 'Groups[*].GroupName'
[
    "ReadOnlyDevs"
]

Why this works: The ReadOnlyAccess managed policy allows Get*, List*, and Describe* actions on almost all services. The user inherits these permissions through group membership.


Exercise 2 — Write a Least-Privilege Policy

Write a custom IAM policy that allows a Lambda function to:

  • Read items from a DynamoDB table called Orders in ap-south-1
  • Write logs to CloudWatch
Show Answer
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DynamoDBRead",
      "Effect": "Allow",
      "Action": [
        "dynamodb:GetItem",
        "dynamodb:Query",
        "dynamodb:Scan"
      ],
      "Resource": "arn:aws:dynamodb:ap-south-1:123456789012:table/Orders"
    },
    {
      "Sid": "CloudWatchLogs",
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:ap-south-1:123456789012:log-group:/aws/lambda/*"
    }
  ]
}

Key points:

  • DynamoDB resource is scoped to the specific table — not *
  • CloudWatch Logs resource is scoped to Lambda log groups
  • No dynamodb:PutItem or dynamodb:DeleteItem — we only need read access
  • The Scan action is included because some read patterns require it, but you could remove it if your function only uses GetItem and Query

Exercise 3 — Role Trust Policy

An ECS task needs to pull images from ECR and write logs to CloudWatch. Write the trust policy and explain what permission policy you would attach.

Show Answer

Trust policy (who can assume the role):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ecs-tasks.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Permission policy (what the role can do):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ECRPull",
      "Effect": "Allow",
      "Action": [
        "ecr:GetAuthorizationToken",
        "ecr:BatchCheckLayerAvailability",
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage"
      ],
      "Resource": "*"
    },
    {
      "Sid": "CloudWatchLogs",
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:123456789012:log-group:/ecs/*"
    }
  ]
}

Explanation:

  • The trust policy principal is ecs-tasks.amazonaws.com — this is different from ecs.amazonaws.com (which is the ECS service itself)
  • ecr:GetAuthorizationToken requires "Resource": "*" because it doesn’t apply to a specific repository
  • CloudWatch Logs are scoped to ECS log groups

Exercise 4 — MFA-Required Policy

Write a policy that denies all actions (except IAM self-service) unless the user has authenticated with MFA. Explain when this policy would be useful.

Show Answer
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowSelfService",
      "Effect": "Allow",
      "Action": [
        "iam:CreateVirtualMFADevice",
        "iam:EnableMFADevice",
        "iam:ListMFADevices",
        "iam:ResyncMFADevice",
        "iam:ChangePassword",
        "iam:GetUser"
      ],
      "Resource": "arn:aws:iam::*:user/${aws:username}"
    },
    {
      "Sid": "DenyAllIfNoMFA",
      "Effect": "Deny",
      "NotAction": [
        "iam:CreateVirtualMFADevice",
        "iam:EnableMFADevice",
        "iam:ListMFADevices",
        "iam:ResyncMFADevice",
        "iam:ChangePassword",
        "iam:GetUser",
        "sts:GetSessionToken"
      ],
      "Resource": "*",
      "Condition": {
        "BoolIfExists": {
          "aws:MultiFactorAuthPresent": "false"
        }
      }
    }
  ]
}

When to use: Attach this to a group to force all members to set up MFA before they can do anything. On first login, the user can only set up MFA. Once MFA is active, they get their normal permissions.

Key details:

  • NotAction means “deny everything except these actions”
  • BoolIfExists handles the case where MFA status doesn’t exist (e.g., long-term access keys without MFA session)
  • sts:GetSessionToken is needed so users can generate MFA-authenticated session tokens via CLI

Exercise 5 — Organization & SCP Design

Your company has three environments: dev, staging, and production. Design an AWS Organizations structure and write an SCP that prevents anyone in production from launching EC2 instances outside ap-south-1 and ap-southeast-1.

Show Answer

Organization structure:

  Root
  │
  ├── OU: Workloads
  │   ├── OU: Production
  │   │   ├── Account: prod-web
  │   │   └── Account: prod-data
  │   ├── OU: Staging
  │   │   └── Account: staging
  │   └── OU: Development
  │       ├── Account: dev-team-alpha
  │       └── Account: dev-team-beta
  │
  └── OU: Security
      ├── Account: logging
      └── Account: audit

SCP for the Production OU:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyEC2OutsideAllowedRegions",
      "Effect": "Deny",
      "Action": [
        "ec2:RunInstances",
        "ec2:CreateVpc",
        "ec2:CreateSubnet"
      ],
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "aws:RequestedRegion": [
            "ap-south-1",
            "ap-southeast-1"
          ]
        }
      }
    }
  ]
}
# Attach the SCP to the Production OU
$ aws organizations create-policy \
    --name RegionRestriction \
    --type SERVICE_CONTROL_POLICY \
    --content file://region-scp.json \
    --description "Restrict EC2 to ap-south-1 and ap-southeast-1"

$ aws organizations attach-policy \
    --policy-id p-regionrestrict \
    --target-id ou-abc1-prod1234

Why this structure:

  • Production OU gets the strictest SCPs — region restriction, no resource deletion
  • Staging OU mirrors production policies for realistic testing
  • Development OU has relaxed SCPs — developers can experiment
  • Security OU is separate — logging and audit accounts are isolated from workload accounts
  • SCPs on Production do not affect Development — each OU has independent policies

Next Chapter → Compute (EC2 & Auto Scaling)

Updated: