How to Attach or Replace an IAM Role on a Running EC2 Instance (No Restart Required)
You launched an EC2 instance, deployed your application, and then realized you forgot to attach an IAM Role — now your app can't call S3, SSM, or any AWS service. The good news: AWS allows you to attach or replace an IAM Instance Profile on a running EC2 instance with zero downtime.
TL;DR
| Scenario | Action | Restart Required? |
|---|---|---|
| No role attached at launch | Associate a new Instance Profile | ❌ No |
| Wrong role attached | Replace the existing Instance Profile | ❌ No |
| Need to remove role entirely | Disassociate the Instance Profile | ❌ No |
Key Concepts: IAM Role vs. Instance Profile
These two terms are often used interchangeably but are distinct AWS constructs. Understanding the difference is critical before you proceed.
- IAM Role: The identity object that holds permission policies. It defines what actions are allowed.
- Instance Profile: A container that wraps an IAM Role and is the actual resource attached to an EC2 instance. When you create a role for EC2 via the AWS Console, an Instance Profile with the same name is automatically created. Via the CLI/API, you must create them separately.
Analogy: Think of the IAM Role as a job description (permissions), and the Instance Profile as the employee badge holder. EC2 doesn't wear the job description directly — it wears the badge holder, which contains the job description inside.
Architecture: How EC2 Consumes IAM Credentials
- Instance Profile is attached to the EC2 instance — this is the link between EC2 and IAM.
- The EC2 instance's Instance Metadata Service (IMDS) exposes temporary credentials at a well-known local endpoint.
- The AWS SDK / CLI running on the instance automatically fetches and rotates these credentials from IMDS.
- The SDK uses those credentials to sign API calls to AWS services (e.g., S3, DynamoDB).
- AWS STS issues the short-lived credentials that back the role assumption.
Step-by-Step: Attach an IAM Role to a Running Instance
Prerequisites
- An existing IAM Role with a trust policy that allows
ec2.amazonaws.comto assume it. - An Instance Profile that wraps that role (auto-created by Console; manual step for CLI).
- IAM permissions for the operator:
ec2:AssociateIamInstanceProfile,iam:PassRole,iam:GetInstanceProfile.
Option A: AWS Management Console
- Navigate to EC2 → Instances and select your instance.
- Click Actions → Security → Modify IAM Role.
- In the dropdown, select the desired Instance Profile (IAM Role name).
- Click Update IAM Role. The change is effective immediately — no reboot needed.
Option B: AWS CLI
This is the recommended approach for automation and scripting pipelines.
Step 1: (If needed) Create an Instance Profile and attach your role to it
🔽 [Click to expand] — Create Instance Profile via CLI
# Create the instance profile
aws iam create-instance-profile \
--instance-profile-name MyAppInstanceProfile
# Attach your existing IAM role to the profile
aws iam add-role-to-instance-profile \
--instance-profile-name MyAppInstanceProfile \
--role-name MyAppEC2Role
Step 2: Associate the Instance Profile with a running EC2 instance
aws ec2 associate-iam-instance-profile \
--instance-id i-0abcd1234efgh5678 \
--iam-instance-profile Name=MyAppInstanceProfile
Step 3: Verify the association
aws ec2 describe-iam-instance-profile-associations \
--filters Name=instance-id,Values=i-0abcd1234efgh5678
Expected output will show an association with "State": "associated".
Option C: Replace an Already-Attached Instance Profile
If the instance already has a profile and you need to swap it out, use replace-iam-instance-profile-association. You first need the Association ID from the describe command above.
# Get the association ID first
ASSOCIATION_ID=$(aws ec2 describe-iam-instance-profile-associations \
--filters Name=instance-id,Values=i-0abcd1234efgh5678 \
--query 'IamInstanceProfileAssociations[0].AssociationId' \
--output text)
# Replace with the new profile
aws ec2 replace-iam-instance-profile-association \
--association-id $ASSOCIATION_ID \
--iam-instance-profile Name=MyNewInstanceProfile
Step-by-Step Flow: What Happens Under the Hood
- You call
associate-iam-instance-profilevia Console, CLI, or SDK. - The EC2 control plane links the Instance Profile (and its embedded IAM Role) to the instance metadata.
- IMDS immediately begins serving fresh STS-issued temporary credentials for the new role at the metadata endpoint.
- Any process on the instance that uses the AWS SDK will pick up the new credentials on the next credential refresh cycle — no application restart required.
IAM Policy for the Operator (Least Privilege)
The human or automation principal performing this action needs the following minimum permissions:
🔽 [Click to expand] — Least Privilege IAM Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowInstanceProfileAssociation",
"Effect": "Allow",
"Action": [
"ec2:AssociateIamInstanceProfile",
"ec2:ReplaceIamInstanceProfileAssociation",
"ec2:DescribeIamInstanceProfileAssociations",
"ec2:DescribeInstances"
],
"Resource": "*"
},
{
"Sid": "AllowPassRoleToEC2",
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::123456789012:role/MyAppEC2Role",
"Condition": {
"StringEquals": {
"iam:PassedToService": "ec2.amazonaws.com"
}
}
},
{
"Sid": "AllowGetInstanceProfile",
"Effect": "Allow",
"Action": "iam:GetInstanceProfile",
"Resource": "arn:aws:iam::123456789012:instance-profile/MyAppInstanceProfile"
}
]
}
Verify Credentials Inside the Instance
SSH into your instance and confirm the role is active:
# Check which role identity the instance is using
aws sts get-caller-identity
# Or directly query IMDS (IMDSv2)
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/
The last command returns the role name currently active on the instance. A follow-up call appending the role name to that URL returns the actual temporary credentials (AccessKeyId, SecretAccessKey, Token, Expiration).
Common Pitfalls
| Pitfall | Root Cause | Fix |
|---|---|---|
InvalidParameterValue on associate | Instance already has a profile attached | Use replace-iam-instance-profile-association instead |
UnauthorizedOperation | Operator missing iam:PassRole | Add iam:PassRole with correct resource ARN to operator policy |
App still gets AccessDenied after attach | SDK cached old (empty) credentials | Wait for SDK credential refresh cycle or restart the app process |
| Role not visible in Console dropdown | Instance Profile not created for the role | Create Instance Profile via CLI and add role to it |
Glossary
| Term | Definition |
|---|---|
| IAM Role | An AWS identity with permission policies, assumable by trusted entities. Not a user. |
| Instance Profile | A container resource that holds exactly one IAM Role and is directly attached to an EC2 instance. |
| IMDS (Instance Metadata Service) | A local HTTP endpoint (169.254.169.254) on every EC2 instance that provides instance metadata including IAM credentials. |
| iam:PassRole | An IAM permission required to delegate a role to an AWS service (like EC2). Prevents privilege escalation. |
| STS (Security Token Service) | AWS service that issues short-lived, temporary security credentials used by IAM roles. |
Next Steps
- 📖 Official Docs: IAM Roles for Amazon EC2
- 🔒 Enforce IMDSv2 on all instances to prevent SSRF-based credential theft: set
HttpTokens=requiredin instance metadata options. - 🏗️ For new infrastructure, use IaC (CloudFormation or Terraform) to always declare the
IamInstanceProfileproperty at launch — eliminate this class of mistake entirely.
Comments
Post a Comment