Chapter 4.3 β Hands-on companion to Chapter 4: IAM: The Gatekeeper of AWS. Aarav has EC2 access β but do we really know what that means? Today we open the policy, read what's inside, and write a tighter one.
The Policy We've Been Ignoring
In Chapter 4.1, we attached AmazonEC2FullAccess to Aarav.
In Chapter 4.2, we moved that to the Developers group.
But here's something we haven't done yet β actually looked at what AmazonEC2FullAccess allows.
Spoiler: it's a lot.
AmazonEC2FullAccess lets Aarav start instances, stop instances, terminate instances, create snapshots, modify security groups, allocate elastic IPs β essentially everything you can do with EC2.
Right now, Pixel & Spoon is a small startup. Aarav is trustworthy. It's fine.
But what happens when the team grows? What if you hire a junior developer who should only be able to view servers, not terminate them? Or a contractor who needs to start and stop instances, but never delete anything permanently?
AWS-managed policies like AmazonEC2FullAccess are broad by design β they cover everything in a service. Custom policies let you grant exactly what someone needs, nothing more.
That's what this chapter is about.
π What's Actually Inside a Policy?
Before we write one, let's read one.
In the AWS Console, search for IAM β click Policies in the left sidebar β search for AmazonEC2FullAccess β click on it β click the JSON tab.

You'll see something like this (simplified):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:*",
"Resource": "*"
}
]
}
Let's read this in plain English:
"Version" β The policy language version. Always "2012-10-17". Don't change this.
"Statement" β A list of permission rules. One policy can have many statements.
"Effect" β "Allow" or "Deny". This one allows.
"Action" β What is being allowed. "ec2:*" means every EC2 action β all of them.
"Resource" β Which specific resources. "*" means all resources, no restrictions.
So AmazonEC2FullAccess in plain English says:
"Allow everything EC2 can do, on every resource, with no conditions."
That's powerful. Too powerful for a junior developer.
π οΈ Step-by-Step: Creating a Custom Policy
Let's create a policy called PixelSpoon-Developer-EC2 that gives Aarav only what he actually needs day-to-day:
- View all instances β
- Start and stop instances β
- Connect to instances β
- Terminate instances β (too destructive β requires deliberate action)
- Modify security groups β (that's Rohit's job)
Step 1 β Open IAM β Policies
Search IAM in the console β left sidebar β Policies β orange Create policy button.
Step 2 β Switch to the JSON Editor
At the top of the policy editor, click the JSON tab.
Clear what's there and paste in this custom policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:Describe*",
"ec2:StartInstances",
"ec2:StopInstances",
"ec2:GetConsoleOutput",
"ec2-instance-connect:SendSSHPublicKey"
],
"Resource": "*"
}
]
}
What each action does:
ec2:Describe* β View/list any EC2 resource (instances, AMIs, etc.)
ec2:StartInstances β Power on a stopped instance
ec2:StopInstances β Power off a running instance
ec2:GetConsoleOutput β Read the instance's console log for debugging
ec2-instance-connect:SendSSHPublicKey β Connect via browser-based EC2 Instance Connect
π‘ Notice
ec2-instance-connectis a separate service prefix fromec2β it's easy to assume it lives underec2:but AWS treats Instance Connect as its own service in IAM. This is a common mistake that causes "access denied" errors even when EC2 permissions look correct.
Notice what's not in the list β ec2:TerminateInstances, ec2:AuthorizeSecurityGroupIngress, ec2:DeleteSnapshot. Aarav simply can't do those things, even accidentally.
Click Next.
Step 3 β Name and Describe the Policy
Policy name: PixelSpoon-Developer-EC2
Description: EC2 access for Pixel and Spoon developers. View, start, stop, and connect only. No termination or security group modifications.
A good description matters β six months from now, you or someone else will look at this policy and need to understand what it does without decoding the JSON.
Click Create policy.
π Attach the New Policy to the Developers Group
Now let's swap out AmazonEC2FullAccess from the Developers group and replace it with our tighter custom policy.
Step 1 β Go to IAM β User groups β click Developers.
Step 2 β Click the Permissions tab β click on the check box next to AmazonEC2FullAccess and click Remove to remove it.
Step 3 β Click Add permissions β Attach policies β search for PixelSpoon-Developer-EC2 β tick it β click Attach policies.
Or you can filter by type Customer managed policies to find it faster.
Done. Aarav now has exactly what he needs β and nothing that could cause an incident at 2am.
π§ AWS Managed vs Customer Managed β When to Use Which
Now that you've written one custom policy, here's a simple rule of thumb:
AWS Managed Policies
β
Great for getting started quickly
β
Maintained and updated by AWS
β Often broader than you actually need
Use for: prototypes, personal learning, non-critical access
Customer Managed Policies
β
Exactly the permissions you define
β
Reusable across users, groups, and roles
β
Auditable β you know exactly what's allowed
β Takes more time to write and maintain
Use for: real teams, production environments, sensitive roles
At Pixel & Spoon, we'll keep AdministratorAccess on Rohit's Admins group for now β he's the only one in it and he genuinely needs broad access. But as the company grows, even that will get tightened.
π οΈ Try It Yourself
π οΈ Task: Write a read-only policy for Divya
Pixel & Spoon just brought on Divya as a data analytics intern.
She needs to VIEW EC2 instances and S3 buckets β but touch nothing.
Goal: Create a custom policy called PixelSpoon-Intern-ReadOnly
that allows only Describe/List/Get actions on EC2 and S3.
Hints:
- ec2:Describe* covers all EC2 viewing actions
- s3:GetObject, s3:ListBucket cover S3 read access
- Effect should be "Allow" on both
- Resource: "*" is fine for now
Then create an IAM user "divya-intern" and attach this policy directly
- remember to ad MFA
(she's temporary, so no group needed).
This is where IAM Role would be better, but we'll cover that in the next chapter.
Click to reveal the answer
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"ec2:Describe*",
"s3:ListBucket"
],
"Resource": "*"
}
]
}
πΊοΈ Continue the Hands-On Series
Ch 4.1 β β Creating IAM Users
Ch 4.2 β β Creating and Managing IAM Groups
Ch 4.3 β β Writing and Attaching IAM Policies (you are here)
Ch 4.4 β Creating IAM Roles: Human & Service
Next up β Karthik the auditor needs temporary access to Pixel & Spoon's AWS account. And the CI/CD bot needs to deploy code automatically without a human logging in. Both of these need Roles β not users, not groups.
π Chapter 4.4: Creating IAM Roles β (Read it here)
Resources I'm learning from:
- roadmap.sh/aws β my learning roadmap
- AWS Official Overview Whitepaper β straight from the source





Top comments (0)