Lab 02: IAM Users, Groups, Policies, and Roles
Objective
Create and configure IAM identities (users, user groups, and roles), attach managed and customer managed policies, and observe how IAM policy evaluation determines access.
Architecture Diagram
This lab builds the following IAM identity structure within your AWS account:
AWS Account (us-east-1)
|
├── IAM User Group: Developers
| ├── Attached Policy: AmazonS3ReadOnlyAccess (AWS managed)
| └── Member: dev-user-01
|
├── IAM User: dev-user-01
| ├── Inherits: AmazonS3ReadOnlyAccess (from Developers group)
| └── Inline Deny Policy: DenyAllS3 (attached in Step 6)
|
├── Customer Managed Policy: BootcampS3BucketPolicy
| └── Allows s3:GetObject, s3:PutObject on a specific bucket
|
└── IAM Role: BootcampEC2S3ReadRole
├── Trust Policy: EC2 service
└── Attached Policy: AmazonS3ReadOnlyAccess (AWS managed)
You will create each identity, test permissions using the AWS CLI and the IAM Policy Simulator, and then demonstrate that an explicit deny always overrides an allow.
Prerequisites
- Completed Lab 01: AWS Account Setup and Console Tour
- Signed in to the AWS Management Console as the
bootcamp-adminIAM user - AWS CloudShell available (or the AWS CLI installed and configured locally)
Duration
60 minutes
Instructions
Step 1: Create an IAM User Group with a Managed Policy
In this step, you create a user group called "Developers" and attach the AmazonS3ReadOnlyAccess AWS managed policy to it.
- Sign in to the AWS Management Console as
bootcamp-admin. - In the search bar at the top, type
IAMand select IAM from the results. - In the left navigation pane, choose User groups.
- Choose Create group.
- In the User group name field, enter
Developers. - In the Attach permissions policies section, type
AmazonS3ReadOnlyAccessin the search box. - Select the checkbox next to AmazonS3ReadOnlyAccess.
- Choose Create user group.
Expected result: The User groups page displays the Developers group. The Attached policies column shows 1.
- To verify using the CLI, open CloudShell and run:
aws iam get-group --group-name Developers --region us-east-1
Expected output (the Users array is empty because you have not added any users yet):
{
"Users": [],
"Group": {
"Path": "/",
"GroupName": "Developers",
"GroupId": "AGPAEXAMPLE123456",
"Arn": "arn:aws:iam::123456789012:group/Developers",
"CreateDate": "2024-01-15T10:00:00+00:00"
},
"IsTruncated": false
}
- Verify the attached policy:
aws iam list-attached-group-policies --group-name Developers
Expected output:
{
"AttachedPolicies": [
{
"PolicyName": "AmazonS3ReadOnlyAccess",
"PolicyArn": "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
}
],
"IsTruncated": false
}
Step 2: Create an IAM User and Add to the Developers Group
In this step, you create an IAM user named dev-user-01 and add the user to the Developers group.
- In the IAM console left navigation pane, choose Users.
- Choose Create user.
- In the User name field, enter
dev-user-01. - Select the checkbox Provide user access to the AWS Management Console.
- Select I want to create an IAM user.
- Choose Custom password and enter a strong password. Record this password for later use in this lab.
- Clear the User must create a new password at next sign-in checkbox.
- Choose Next.
- On the Set permissions page, select Add user to group.
- Select the checkbox next to the Developers group.
- Choose Next.
- Review the user details and choose Create user.
- On the confirmation page, note the Console sign-in URL. You will use it later to sign in as
dev-user-01. - Choose Return to users list.
Expected result: The Users page lists dev-user-01. Choosing the user name shows Developers under the Groups tab.
- Verify using the CLI:
aws iam list-groups-for-user --user-name dev-user-01
Expected output:
{
"Groups": [
{
"Path": "/",
"GroupName": "Developers",
"GroupId": "AGPAEXAMPLE123456",
"Arn": "arn:aws:iam::123456789012:group/Developers",
"CreateDate": "2024-01-15T10:00:00+00:00"
}
]
}
Step 3: Test Permissions for dev-user-01
In this step, you create access keys for dev-user-01 and verify that the user can list S3 buckets (allowed by AmazonS3ReadOnlyAccess) but cannot launch EC2 instances (no EC2 permissions granted).
- In the IAM console, choose Users, then choose dev-user-01.
- Choose the Security credentials tab.
- Scroll to the Access keys section and choose Create access key.
- Select Command Line Interface (CLI) as the use case.
- Select the confirmation checkbox at the bottom and choose Next.
- Choose Create access key.
- Copy the Access key ID and Secret access key values. You will need both in the next step.
Warning: This is the only time you can view the secret access key. If you lose it, you must create a new access key pair.
- In CloudShell, configure a named profile for
dev-user-01:
aws configure --profile dev-user-01
-
When prompted, enter the following values:
- AWS Access Key ID: paste the access key ID from step 7
- AWS Secret Access Key: paste the secret access key from step 7
- Default region name:
us-east-1 - Default output format:
json
-
Test S3 read access by listing buckets:
aws s3 ls --profile dev-user-01
Expected result: The command succeeds. If you have S3 buckets in the account, they are listed. If you have no buckets, the command returns an empty result with no error.
- Test that
dev-user-01cannot create EC2 instances:
aws ec2 run-instances \
--image-id ami-0c02fb55956c7d316 \
--instance-type t2.micro \
--region us-east-1 \
--profile dev-user-01
Expected result: The command fails with an error similar to:
An error occurred (UnauthorizedOperation) when calling the RunInstances operation:
You are not authorized to perform this operation.
This confirms that dev-user-01 has S3 read-only access (inherited from the Developers group) but no EC2 permissions. IAM denies any action that is not explicitly allowed.
Tip: The
UnauthorizedOperationerror is the expected behavior. IAM follows a default-deny model: if no policy explicitly allows an action, the action is denied.
Step 4: Create a Customer Managed Policy
In this step, you create a customer managed policy that allows specific S3 actions on a specific bucket. This demonstrates how to write fine-grained permissions.
- First, create an S3 bucket to use as the policy target. In CloudShell, run:
aws s3 mb s3://bootcamp-lab02-$(aws sts get-caller-identity --query Account --output text) --region us-east-1
Expected result: Output similar to:
make_bucket: bootcamp-lab02-123456789012
Tip: The command appends your AWS account ID to the bucket name to ensure uniqueness. S3 bucket names must be globally unique across all AWS accounts.
- In the IAM console left navigation pane, choose Policies.
- Choose Create policy.
- Choose the JSON tab (or toggle to the JSON policy editor).
- Replace the default policy content with the following JSON. Replace
ACCOUNT_IDwith your 12-digit AWS account ID:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowS3ActionsOnBootcampBucket",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::bootcamp-lab02-ACCOUNT_ID",
"arn:aws:s3:::bootcamp-lab02-ACCOUNT_ID/*"
]
}
]
}
Tip: You can find your account ID by running
aws sts get-caller-identity --query Account --output textin CloudShell.
- Choose Next.
- In the Policy name field, enter
BootcampS3BucketPolicy. - In the Description field, enter
Allows GetObject, PutObject, and ListBucket on the bootcamp-lab02 bucket. - Choose Create policy.
Expected result: The Policies page displays BootcampS3BucketPolicy in the list of customer managed policies.
- Verify using the CLI:
aws iam list-policies --scope Local --query "Policies[?PolicyName=='BootcampS3BucketPolicy']"
Expected output:
[
{
"PolicyName": "BootcampS3BucketPolicy",
"PolicyId": "ANPAEXAMPLE123456",
"Arn": "arn:aws:iam::123456789012:policy/BootcampS3BucketPolicy",
"Path": "/",
"DefaultVersionId": "v1",
"AttachmentCount": 0,
"IsAttachable": true,
"CreateDate": "2024-01-15T10:30:00+00:00",
"UpdateDate": "2024-01-15T10:30:00+00:00"
}
]
Step 5: Create an IAM Role for EC2 with S3 Read Access
In this step, you create an IAM role that allows EC2 instances to read from S3. This role uses an instance profile so that EC2 instances can assume the role and receive temporary credentials automatically.
- In the IAM console left navigation pane, choose Roles.
- Choose Create role.
- Under Trusted entity type, select AWS service.
- Under Use case, select EC2.
- Choose Next.
- In the Permissions policies search box, type
AmazonS3ReadOnlyAccess. - Select the checkbox next to AmazonS3ReadOnlyAccess.
- Choose Next.
- In the Role name field, enter
BootcampEC2S3ReadRole. - In the Description field, enter
Allows EC2 instances to read from S3 buckets. - Choose Create role.
Expected result: The Roles page displays BootcampEC2S3ReadRole. The role has AmazonS3ReadOnlyAccess attached and a trust policy that allows the EC2 service to assume it.
- Verify the role and its trust policy using the CLI:
aws iam get-role --role-name BootcampEC2S3ReadRole --query "Role.AssumeRolePolicyDocument"
Expected output:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
This trust policy means only the EC2 service can assume this role. When you attach this role to an EC2 instance, the instance automatically receives temporary credentials to access S3.
- Verify the attached permissions policy:
aws iam list-attached-role-policies --role-name BootcampEC2S3ReadRole
Expected output:
{
"AttachedPolicies": [
{
"PolicyName": "AmazonS3ReadOnlyAccess",
"PolicyArn": "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
}
],
"IsTruncated": false
}
Tip: Using IAM roles with EC2 instances eliminates the need to store access keys on the instance. The instance receives temporary credentials that rotate automatically. This is a core IAM security best practice.
Step 6: Test the Role Using the IAM Policy Simulator
The IAM Policy Simulator lets you test the effect of policies without actually making API calls. In this step, you use it to verify that BootcampEC2S3ReadRole can read from S3 but cannot write.
- Open the IAM Policy Simulator in a new browser tab.
- In the left panel under Users, Groups, and Roles, expand Roles.
- Select BootcampEC2S3ReadRole.
- In the Policy Simulator section on the right, under Select service, choose S3.
- Under Select actions, select the following actions:
GetObjectListBucketPutObjectDeleteObject
- Choose Run Simulation.
Expected result:
| Action | Result |
|---|---|
| s3:GetObject | allowed |
| s3:ListBucket | allowed |
| s3:PutObject | denied |
| s3:DeleteObject | denied |
The AmazonS3ReadOnlyAccess policy allows read actions (GetObject, ListBucket) but does not allow write actions (PutObject, DeleteObject). Since IAM denies any action not explicitly allowed, the write actions are denied.
- You can also test the simulation from the CLI:
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):role/BootcampEC2S3ReadRole \
--action-names s3:GetObject s3:PutObject s3:ListBucket s3:DeleteObject
Expected output (abbreviated):
{
"EvaluationResults": [
{
"EvalActionName": "s3:GetObject",
"EvalDecision": "allowed"
},
{
"EvalActionName": "s3:PutObject",
"EvalDecision": "implicitDeny"
},
{
"EvalActionName": "s3:ListBucket",
"EvalDecision": "allowed"
},
{
"EvalActionName": "s3:DeleteObject",
"EvalDecision": "implicitDeny"
}
]
}
Tip: Notice the difference between
implicitDenyand an explicit deny. An implicit deny means no policy allows the action. An explicit deny means a policy statement with"Effect": "Deny"actively blocks the action. You will see an explicit deny in the next step.
Step 7: Demonstrate Explicit Deny Overrides Allow
In this step, you attach an inline deny policy to dev-user-01 that denies all S3 actions. Even though the Developers group grants AmazonS3ReadOnlyAccess, the explicit deny overrides the allow. This demonstrates the IAM policy evaluation logic.
- In CloudShell, create an inline policy that denies all S3 actions for
dev-user-01:
aws iam put-user-policy \
--user-name dev-user-01 \
--policy-name DenyAllS3 \
--policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyAllS3Actions",
"Effect": "Deny",
"Action": "s3:*",
"Resource": "*"
}
]
}'
Expected result: The command completes with no output (exit code 0).
- Verify the inline policy was attached:
aws iam list-user-policies --user-name dev-user-01
Expected output:
{
"PolicyNames": [
"DenyAllS3"
],
"IsTruncated": false
}
- Now test S3 access as
dev-user-01:
aws s3 ls --profile dev-user-01
Expected result: The command fails with an error similar to:
An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied
- Compare this to the result in Step 3, where the same command succeeded. The
Developersgroup still hasAmazonS3ReadOnlyAccessattached, but the explicit deny in theDenyAllS3inline policy overrides it.
This is the key principle of IAM policy evaluation:
- By default, all requests are denied (implicit deny).
- An explicit allow in a policy overrides the implicit deny.
- An explicit deny in any policy always overrides any allow.
Warning: Explicit deny policies are powerful. A single deny statement anywhere in the policy chain blocks access, regardless of how many other policies allow the action. Use deny policies carefully and document them clearly.
- Use the IAM Policy Simulator to visualize this. Open the IAM Policy Simulator.
- In the left panel, expand Users and select dev-user-01.
- Under Select service, choose S3.
- Under Select actions, select
ListAllMyBuckets. - Choose Run Simulation.
Expected result: The result shows denied with the DenyAllS3 policy listed as the source of the denial. This confirms that the explicit deny overrides the allow from AmazonS3ReadOnlyAccess.
Validation
Confirm the following:
- The
Developersuser group exists with theAmazonS3ReadOnlyAccesspolicy attached - The
dev-user-01IAM user exists and is a member of theDevelopersgroup - Running
aws s3 ls --profile dev-user-01returnsAccessDenied(because theDenyAllS3inline policy is active) - The
BootcampS3BucketPolicycustomer managed policy exists and targets thebootcamp-lab02-*bucket - The
BootcampEC2S3ReadRoleIAM role exists with a trust policy allowingec2.amazonaws.comandAmazonS3ReadOnlyAccessattached - The IAM Policy Simulator shows
s3:GetObjectasallowedands3:PutObjectasdeniedforBootcampEC2S3ReadRole - The IAM Policy Simulator shows
s3:ListAllMyBucketsasdeniedfordev-user-01(explicit deny fromDenyAllS3)
Cleanup
Delete all resources created in this lab to avoid unintended configuration in your account.
- Remove the inline deny policy from dev-user-01:
aws iam delete-user-policy --user-name dev-user-01 --policy-name DenyAllS3
- Delete the access keys for dev-user-01:
aws iam list-access-keys --user-name dev-user-01 --query "AccessKeyMetadata[].AccessKeyId" --output text
Use the access key ID from the output in the following command:
aws iam delete-access-key --user-name dev-user-01 --access-key-id ACCESS_KEY_ID
- Remove dev-user-01 from the Developers group:
aws iam remove-user-from-group --user-name dev-user-01 --group-name Developers
- Delete the dev-user-01 user:
First, delete the login profile (console password):
aws iam delete-login-profile --user-name dev-user-01
Then delete the user:
aws iam delete-user --user-name dev-user-01
- Detach the policy from the Developers group and delete the group:
aws iam detach-group-policy --group-name Developers --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
aws iam delete-group --group-name Developers
- Delete the customer managed policy:
aws iam delete-policy --policy-arn arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):policy/BootcampS3BucketPolicy
- Delete the IAM role:
aws iam detach-role-policy --role-name BootcampEC2S3ReadRole --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
aws iam delete-role --role-name BootcampEC2S3ReadRole
- Delete the S3 bucket:
aws s3 rb s3://bootcamp-lab02-$(aws sts get-caller-identity --query Account --output text) --force
Warning: The
--forceflag deletes all objects in the bucket before removing the bucket. Verify you are deleting the correct bucket.
- Remove the dev-user-01 CLI profile:
aws configure --profile dev-user-01 set aws_access_key_id ""
aws configure --profile dev-user-01 set aws_secret_access_key ""
Challenge (Optional)
Using only concepts from Modules 01 and 02, complete the following:
- Create a second user group called
Auditorswith theSecurityAuditAWS managed policy attached. - Create an IAM user called
audit-user-01and add the user to theAuditorsgroup. - Sign in as
audit-user-01(or use a CLI profile) and verify that the user can view IAM policies and CloudTrail logs but cannot create or modify any resources. - Use the IAM Policy Simulator to test which actions
audit-user-01is allowed to perform on the IAM and CloudTrail services.
This exercise reinforces the concept of least privilege: auditors need read access to security configurations but should never be able to modify them.
Tip: Remember to delete the
Auditorsgroup andaudit-user-01user after completing the challenge.
AWS Bootcamp: From Novice to Architect Author: Samuel Ogunti License: CC BY-NC 4.0