AWS for DevOps — IAM & Security Exercises
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
Ordersinap-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:PutItemordynamodb:DeleteItem— we only need read access - The
Scanaction is included because some read patterns require it, but you could remove it if your function only usesGetItemandQuery
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 fromecs.amazonaws.com(which is the ECS service itself) ecr:GetAuthorizationTokenrequires"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:
NotActionmeans “deny everything except these actions”BoolIfExistshandles the case where MFA status doesn’t exist (e.g., long-term access keys without MFA session)sts:GetSessionTokenis 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)