Apply IAM Roles to EC2
Apply IAM roles for EC2 access — Cloud security essentials.
Focus: apply iam roles for ec2 access
You've stared at a hardcoded AWS access key in a config file and felt that familiar dread — one leaked key in a repo, a Slack paste, or a log line, and anyone can impersonate your application and drain your resources. If you're building on EC2, storing credentials on the instance is the single worst security habit you can carry into production. This lesson shows you the right way: apply IAM roles for EC2 access so your instances get short-term, automatically rotated credentials — with zero keys to manage, leak, or rotate by hand.
The problem this lesson solves
Hardcoded credentials on EC2 instances are a ticking time bomb. Every developer who has ssh'd into a box and found aws_access_key_id in a .env file knows the panic. Here's why it's so dangerous:
- Static keys never expire — unless someone remembers to rotate them (they won't).
- Keys are scattered — every instance, every config file, every backup is a potential breach.
- An attacker with a key gets full access — no IP restrictions, no multi-factor, just a valid signature.
Even using the AWS CLI's aws configure on an instance is a trap: the key lives on disk, readable by any process or user that compromises the box.
The industry standard — and the practice every AWS Well-Architected review checks for — is IAM roles. Instead of shipping secrets, you grant the instance a permission set that AWS issues credentials to on demand. The instance never sees a long-lived key.
Pro tip: If you're new to AWS security, think of an IAM role as a temporary badge, not a key. The badge is valid for a while, gets refreshed automatically, and is useless if stolen because it expires quickly.
Core concept / mental model
An IAM role is a set of permissions that a trusted entity (like an EC2 instance) can assume. It's not a user — there's no login, no password, no long-term access key. Instead, the role has two key components:
- Trust policy — who can assume the role (e.g., the EC2 service).
- Permissions policy — what they can do once they assume it (e.g., read from S3, write to DynamoDB).
When you launch an EC2 instance with an instance profile, the instance can request temporary credentials from the instance metadata service. These credentials are:
- Short-lived (default 1–6 hours, configurable)
- Automatically rotated by AWS
- Scoped to exactly the permissions the role grants
Here's a word-diagram to anchor the model:
[EC2 instance] --(assume role)--> [IAM Role] --(grants permissions)--> [S3, DynamoDB, etc.]
|
+-- credentials from temporary metadata service
The instance never stores a secret. The role is the single source of truth for access.
Key mental shift: Stop asking "who is this user?" and start asking "what job does this instance do?" Roles map to jobs, not people.
How it works step by step
Applying an IAM role to EC2 is a three-stage process:
- Create a role in IAM with a trust policy that allows the EC2 service to assume it.
- Attach permissions to the role — attach AWS managed policies (like
AmazonS3ReadOnlyAccess) or write a custom JSON policy. - Attach the role to an EC2 instance at launch (via the console, CLI, or infrastructure-as-code). If the instance already exists, you can attach or replace a role (console and CLI both support it, but note this stops the instance briefly).
Once attached, the instance can immediately use the AWS SDK/CLI without any configuration — the SDK automatically checks the metadata service for credentials.
How credentials are delivered
When an application on the instance calls the AWS SDK, it asks the metadata service (at http://169.254.169.254/latest/meta-data/iam/security-credentials/) for a token. The service returns a set of temporary credentials — access key, secret key, and a session token — that are valid for the role's duration. The SDK caches these and refreshes them before expiry.
This is why roles are called a best practice: the credentials are ephemeral, scoped, and easy to audit.
Hands-on walkthrough
Let's build the whole thing with the AWS CLI so you can repeat it in scripts or CI/CD. We'll create a role that can only read from a specific S3 bucket, attach it to an EC2 instance, and verify access.
Step 1: Create the IAM role
# Create the trust policy file that allows EC2 to assume the role
cat > trust-policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "ec2.amazonaws.com" },
"Action": "sts:AssumeRole"
}
]
}
EOF
# Create the role
export ROLE_NAME="my-app-s3-reader"
aws iam create-role \
--role-name "$ROLE_NAME" \
--assume-role-policy-document file://trust-policy.json
Step 2: Attach a permissions policy
# Attach a managed policy (read-only for S3)
aws iam attach-role-policy \
--role-name "$ROLE_NAME" \
--policy-arn "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
Or, for a custom scoped policy that only allows reading one bucket:
cat > bucket-policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::my-private-bucket",
"arn:aws:s3:::my-private-bucket/*"
]
}
]
}
EOF
aws iam put-role-policy \
--role-name "$ROLE_NAME" \
--policy-name "ReadMyBucket" \
--policy-document file://bucket-policy.json
Pro tip: Prefer scoped custom policies over broad managed ones. The principle of least privilege means the role can only touch what it needs — nothing more.
Step 3: Create an instance profile and attach the role
# Create an instance profile (the container for the role on an instance)
aws iam create-instance-profile --instance-profile-name "my-app-profile"
# Add the role to the profile
aws iam add-role-to-instance-profile \
--instance-profile-name "my-app-profile" \
--role-name "$ROLE_NAME"
# Launch an EC2 instance with the profile
aws ec2 run-instances \
--image-id "ami-0abcdef1234567890" \
--instance-type "t3.micro" \
--key-name "my-keypair" \
--iam-instance-profile "Name=my-app-profile"
If you already have an instance running, attach the role in place:
aws ec2 associate-iam-instance-profile \
--instance-id "i-0abcd1234efgh5678" \
--iam-instance-profile "Name=my-app-profile"
Step 4: Verify from inside the instance
SSH into the instance and test:
# Check the instance's identity
aws sts get-caller-identity
# Confirm you're using role credentials
aws sts get-session-token --duration-seconds 3600
Expected output of get-caller-identity:
{
"UserId": "AROA1234567890EXAMPLE:assumed-role-session-name",
"Arn": "arn:aws:iam::123456789012:role/my-app-s3-reader",
"Account": "123456789012"
}
That Arn ending in role/my-app-s3-reader proves the instance is using the role — no keys in sight.
To test S3 access:
aws s3 ls s3://my-private-bucket/
If it works, you're good. If you get an AccessDenied, likely the role policy is too restrictive (which is good) or the bucket policy is blocking you.
Compare options / when to choose what
Here's how IAM roles stack up against the alternatives:
| Option | Credential lifecycle | Security risk | Management burden | Best for |
|---|---|---|---|---|
| IAM role on EC2 | Short-term, auto-rotated | Low | Low (AWS handles rotation) | Any production workload |
| Long-term access keys | Static until manually rotated | High — keys leak and persist | High — you must track and rotate | Legacy apps, non-AWS environments |
| Instance profile with a user's key | Static, scoped to a user | High — user key is powerful | Medium | Avoid — use roles instead |
| AWS SSO / federation | Short-term, via IdP | Low | Medium | Human access, multi-account |
The clear winner for EC2 workloads is roles. They're the default recommendation in every AWS security model. If you must use static keys (for example, for on-premises servers), isolate them, rotate often, and use a secrets manager.
When to choose each:
- Team training / dev sandbox — roles from day one to build good habits.
- Legacy app on a VM — hardcoded keys may be the only option, but call it tech debt.
- Multi-account architecture — use roles + AWS SSO for humans, roles for EC2.
Troubleshooting & edge cases
"Can't connect to the metadata service"
The instance can't reach 169.254.169.254. Causes:
- Ensure you're on an EC2 instance — not a local machine or a container without the metadata endpoint.
- Check security groups — they must allow outbound HTTP to that IP (usually allowed by default).
- Verify the instance profile exists — a broken profile can cause the metadata service to return nothing.
"AccessDenied" when calling S3/DynamoDB
- Role policy too narrow — double-check the
ResourceandActionvalues. - Bucket policy blocks — even with a role, S3 bucket policies can deny access. Use
aws sts get-caller-identityto confirm the role ARN, then inspect both policies. - Session token missing — if you're using the CLI, run
aws configureto make sure you haven't overridden with static keys.
"The instance profile is not valid"
- Role not added to profile — you must add the role after creating the profile.
- Region mismatch — instance profiles are regional; if you launch in a different region, create a new one.
Attaching a role to a running instance
- Expect a brief reboot — the console warns you, and you'll notice a short downtime.
- Detach and re-attach — if you change permissions, a re-attach isn't needed; permissions update in minutes, but the credentials token refresh might take a bit.
Pro tip: Always test role permissions from the instance with
aws sts get-caller-identityfirst. It's the fastest way to confirm which identity is in play.
What you learned & what's next
You now know how to apply IAM roles for EC2 access — the gold standard for securing AWS workloads. You can:
- Explain why hardcoded keys are a liability.
- Create a role with a trust policy and permissions.
- Attach it to an EC2 instance via the CLI.
- Verify access and troubleshoot common failures.
This is the foundation for deeper IAM patterns — next, you'll expand your security lens to more complex trust policies, cross-account roles, and the broader Cloud security essentials track. Arm yourself with least privilege, and your instances will sleep easy.
Next step: In the upcoming lesson, you'll learn how to enforce least privilege at the network layer with security groups — combining IAM roles with network controls for defense in depth.
Practice recap
Try this: create a role with read-only access to one S3 bucket, launch a fresh EC2 instance, and run aws s3 ls from inside it. Then attempt to write — it should fail. This proves your role grants exactly the permissions you configured. Next, explore the AWS console's IAM wizard for a visual alternative to the CLI.
Common mistakes
- Hardcoding access keys in user data scripts or environment variables instead of using an IAM role.
- Creating an instance profile without first adding the role to it — the profile is just a container, and the role must be attached before it works.
- Using a broad managed policy (like
AdministratorAccess) on a role instead of scoping permissions to the specific actions and resources the application needs. - Forgetting to attach the role at launch or assuming it will automatically propagate to running instances — for existing instances you must explicitly associate the instance profile.
Variations
- AWS Systems Manager (SSM) with a role that grants it access — a managed alternative that also gives you session manager and patch management.
- Using EC2 Instance Connect with a short-lived SSH key — but note this only covers remote access, not the application's AWS API calls.
- Infrastructure-as-code (CloudFormation, Terraform) to declare roles and instance profiles for repeatable, auditable deployments.
Real-world use cases
- A production web server fetching user uploads from S3 — the instance uses a role with
s3:GetObjecton a specific bucket. - A batch processing worker reading messages from SQS and writing results to DynamoDB — role grants only those actions on the exact queues/tables.
- An ETL job in an EC2 instance that assumes a cross-account role to write into a data lake in a separate AWS account.
Key takeaways
- IAM roles are the only secure way to give EC2 instances access to AWS APIs — they issue short-term, automatically rotated credentials.
- A role is a permission set with a trust policy; an instance profile is the container that attaches the role to an EC2 instance.
- Attach only the minimal permissions the application needs — principle of least privilege is non-negotiable.
- Test identity immediately with
aws sts get-caller-identityto confirm the role is active. - Attaching a role to a running instance causes a brief restart — plan for downtime.
- Roles simplify auditing and rotation — there are no static keys to manage or leak.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.