How to Attach an IAM Role to an Existing EC2 Instance (Without Restarting)
Forgetting to attach an IAM role at launch is one of the most common EC2 mistakes — your instance is running, your application is live, and suddenly you realize it has no permissions to call S3, Secrets Manager, or any other AWS service. The good news: attaching or replacing an IAM role on a running EC2 instance requires no reboot and takes effect within seconds.
TL;DR — Attaching an IAM Role to an Existing EC2 Instance
| Scenario | Action | Downtime Required? |
|---|---|---|
| No role attached | Associate an instance profile | No |
| Wrong role attached | Replace the instance profile association | No |
| Role permissions insufficient | Update the IAM role's policy | No |
How IAM Roles on EC2 Actually Work
An IAM role is not attached to an EC2 instance directly — it is wrapped in an instance profile, which is the container object that EC2 understands. When you create a role for EC2 in the console, AWS silently creates a matching instance profile with the same name. The instance profile is what gets associated with the instance, and it exposes temporary credentials through the Instance Metadata Service (IMDS) at the well-known endpoint. The AWS SDKs and CLI automatically retrieve and rotate these credentials from IMDS, so your application code never needs to manage keys manually.
- Instance Profile is the EC2-facing container for an IAM role. One role maps to one instance profile (when created via console).
- IMDS serves temporary credentials to any process on the instance. The SDK credential chain checks this automatically.
- STS issues the short-lived credentials that IMDS surfaces. These rotate automatically — no application restart needed.
- When you associate or replace an instance profile on a running instance, the new credentials become available at IMDS within seconds.
Prerequisites: Create the IAM Role and Instance Profile
Before attaching anything, you need a role with an EC2 trust policy. If you already have one, skip to the next section. The trust policy must allow ec2.amazonaws.com to assume the role — without this, the association will succeed but the instance cannot obtain credentials.
# Step 1: Create the trust policy document
cat > ec2-trust-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
# Step 2: Create the IAM role
aws iam create-role \
--role-name MyEC2Role \
--assume-role-policy-document file://ec2-trust-policy.json
# Step 3: Attach a permissions policy (example: S3 read-only)
aws iam attach-role-policy \
--role-name MyEC2Role \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
# Step 4: Create the instance profile
aws iam create-instance-profile \
--instance-profile-name MyEC2InstanceProfile
# Step 5: Add the role to the instance profile
aws iam add-role-to-instance-profile \
--instance-profile-name MyEC2InstanceProfile \
--role-name MyEC2Role
Think of the instance profile as the badge holder and the IAM role as the badge itself. EC2 only accepts badge holders — you cannot hand it a bare badge.
How to Attach an IAM Role to a Running EC2 Instance (No Role Currently Attached)
With the instance profile ready, associate it with your running instance using associate-iam-instance-profile. You need the EC2 instance ID and the instance profile name or ARN. This operation does not stop or reboot the instance.
# Retrieve your instance ID if needed
aws ec2 describe-instances \
--filters "Name=tag:Name,Values=MyInstance" \
--query 'Reservations[*].Instances[*].InstanceId' \
--output text
# Associate the instance profile with the running instance
aws ec2 associate-iam-instance-profile \
--instance-id i-0abcd1234efgh5678 \
--iam-instance-profile Name=MyEC2InstanceProfile
The command returns an IamInstanceProfileAssociation object with an association ID and a state of associating, which transitions to associated within seconds. Verify the association before moving on — a successful API call does not guarantee the profile is fully active.
# Verify the association is in 'associated' state
aws ec2 describe-iam-instance-profile-associations \
--filters "Name=instance-id,Values=i-0abcd1234efgh5678"
How to Replace an IAM Role on a Running EC2 Instance
If the instance already has a role attached and you need to swap it — perhaps the original role had overly broad permissions and you're tightening scope — use replace-iam-instance-profile-association. You need the existing association ID, not the instance ID, for this operation.
# Step 1: Get the current association ID
aws ec2 describe-iam-instance-profile-associations \
--filters "Name=instance-id,Values=i-0abcd1234efgh5678" \
--query 'IamInstanceProfileAssociations[*].AssociationId' \
--output text
# Step 2: Replace with the new instance profile
aws ec2 replace-iam-instance-profile-association \
--association-id iip-assoc-0123456789abcdef0 \
--iam-instance-profile Name=MyNewEC2InstanceProfile
The replacement is atomic from EC2's perspective — there is no window where the instance has no profile. The old credentials remain valid until their natural expiry (STS issues short-lived tokens), and new credentials reflecting the new role become available at IMDS almost immediately.
- describe-iam-instance-profile-associations — retrieve the association ID tied to the instance.
- replace-iam-instance-profile-association — atomically swaps the profile using the association ID.
- IMDS surfaces new credentials within seconds. Old STS tokens remain valid until expiry.
- Application picks up new credentials on the next SDK refresh cycle — no restart required.
Required IAM Permissions for the Operator
The AWS principal performing these operations — your IAM user, role, or federated identity — needs specific permissions. A common failure mode is having EC2 permissions but missing the iam:PassRole permission, which is required whenever you attach a role to an AWS resource.
🔽 Click to expand — Operator 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/MyEC2Role",
"Condition": {
"StringEquals": {
"iam:PassedToService": "ec2.amazonaws.com"
}
}
}
]
}
The iam:PassRole condition scoped to ec2.amazonaws.com follows least privilege — it prevents the operator from passing this role to other services like Lambda or ECS, even if they have broad EC2 permissions elsewhere.
Verifying Credentials Are Available Inside the Instance
After association, confirm the instance can actually retrieve credentials. SSH into the instance and query IMDS directly, or use the AWS CLI which will automatically use the instance profile credential chain.
# Option 1: Query IMDS v2 directly (IMDSv2 requires a session token)
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/
# Option 2: Use AWS CLI (uses instance profile automatically)
aws sts get-caller-identity
The first IMDS call returns the role name if credentials are available. The second confirms the effective identity. If the role name appears but get-caller-identity fails, the role's trust policy likely does not allow ec2.amazonaws.com — revisit the trust relationship.
A Real Failure Pattern: The Silent PassRole Block
Here is a failure pattern that wastes significant debugging time. The associate-iam-instance-profile call succeeds — no error, association ID returned, state shows associated. But inside the instance, IMDS returns a 404 on the credentials endpoint. The console shows the role attached. Everything looks correct.
The misdiagnosis: engineers assume the role trust policy is wrong and spend time editing it. The actual cause: an SCP (Service Control Policy) at the organization level is blocking sts:AssumeRole for the target role in that account. The EC2 service can associate the profile metadata, but when it tries to call STS to generate credentials, the SCP silently blocks it. The association object exists; the credentials do not.
The fix requires checking two layers — not one:
# Layer 1: Confirm the role trust policy allows EC2
aws iam get-role \
--role-name MyEC2Role \
--query 'Role.AssumeRolePolicyDocument'
# Layer 2: Check for SCP restrictions (requires Organizations access)
aws organizations list-policies-for-target \
--target-id \
--filter SERVICE_CONTROL_POLICY \
--query 'Policies[*].Name'
SCPs that deny sts:AssumeRole broadly, or that restrict which roles can be assumed in an account, will silently break instance profile credential delivery even when the EC2 association appears healthy.
Doing This via the AWS Console
If you prefer the console over CLI, the path is: EC2 → Instances → select your instance → Actions → Security → Modify IAM role. Choose the instance profile from the dropdown and click Update IAM role. The console operation maps directly to associate-iam-instance-profile or replace-iam-instance-profile-association depending on whether a role is currently attached.
Wrap-Up: Attaching an IAM Role to an EC2 Instance Without Downtime
Attaching or replacing an IAM role on a running EC2 instance is a safe, non-disruptive operation. The key steps are: ensure the role has an EC2 trust policy, wrap it in an instance profile, associate the profile using associate-iam-instance-profile (or replace via association ID), and verify credentials are live inside the instance via IMDS or aws sts get-caller-identity. If credentials don't appear despite a successful association, check SCP restrictions at the organization level before assuming the trust policy is at fault.
For further reading, see the AWS documentation on IAM roles for EC2 and the iam:PassRole reference.
Glossary
| Term | Definition |
|---|---|
| Instance Profile | An EC2-specific container that holds exactly one IAM role and is the object directly associated with an EC2 instance. |
| IMDS (Instance Metadata Service) | An HTTP endpoint at 169.254.169.254 that provides instance metadata and temporary IAM credentials to processes running on the instance. |
| iam:PassRole | An IAM permission required by the operator to attach a role to an AWS resource. Without it, association calls are denied regardless of EC2 permissions. |
| Association ID | A unique identifier for an instance-profile-to-instance binding, required when replacing an existing association. |
| SCP (Service Control Policy) | An AWS Organizations policy that sets permission guardrails at the account or OU level, capable of blocking actions even when IAM allows them. |
Comments
Post a Comment