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

ScenarioActionRestart Required?
No role attached at launchAssociate a new Instance Profile❌ No
Wrong role attachedReplace the existing Instance Profile❌ No
Need to remove role entirelyDisassociate 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

graph LR IP["Instance Profile (attached to EC2)"] Role["IAM Role (inside Profile)"] EC2["EC2 Instance"] IMDS["IMDS 169.254.169.254"] SDK["AWS SDK / CLI (on instance)"] STS["AWS STS"] S3["AWS Services (S3, DynamoDB, etc.)"] EC2 -->|"hosts"| IMDS IP -->|"contains"| Role IP -->|"attached to"| EC2 Role -->|"assumed via"| STS STS -->|"issues temp credentials to"| IMDS SDK -->|"fetches credentials from"| IMDS SDK -->|"signs API calls to"| S3
  1. Instance Profile is attached to the EC2 instance — this is the link between EC2 and IAM.
  2. The EC2 instance's Instance Metadata Service (IMDS) exposes temporary credentials at a well-known local endpoint.
  3. The AWS SDK / CLI running on the instance automatically fetches and rotates these credentials from IMDS.
  4. The SDK uses those credentials to sign API calls to AWS services (e.g., S3, DynamoDB).
  5. 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.com to 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

  1. Navigate to EC2 → Instances and select your instance.
  2. Click Actions → Security → Modify IAM Role.
  3. In the dropdown, select the desired Instance Profile (IAM Role name).
  4. 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

sequenceDiagram participant Operator as "Operator / CI Pipeline" participant EC2API as "EC2 Control Plane" participant IMDS as "IMDS on Instance" participant STS as "AWS STS" participant App as "App on Instance" Operator->>EC2API: associate-iam-instance-profile EC2API->>EC2API: "Link Instance Profile to instance metadata" EC2API-->>Operator: "Association ID + State: associated" IMDS->>STS: "Request temp credentials for new role" STS-->>IMDS: "AccessKeyId, SecretKey, Token, Expiration" App->>IMDS: "GET /latest/meta-data/iam/security-credentials/" IMDS-->>App: "Fresh credentials for new role" App->>App: "SDK uses new credentials on next refresh"
  1. You call associate-iam-instance-profile via Console, CLI, or SDK.
  2. The EC2 control plane links the Instance Profile (and its embedded IAM Role) to the instance metadata.
  3. IMDS immediately begins serving fresh STS-issued temporary credentials for the new role at the metadata endpoint.
  4. 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

PitfallRoot CauseFix
InvalidParameterValue on associateInstance already has a profile attachedUse replace-iam-instance-profile-association instead
UnauthorizedOperationOperator missing iam:PassRoleAdd iam:PassRole with correct resource ARN to operator policy
App still gets AccessDenied after attachSDK cached old (empty) credentialsWait for SDK credential refresh cycle or restart the app process
Role not visible in Console dropdownInstance Profile not created for the roleCreate Instance Profile via CLI and add role to it

Glossary

TermDefinition
IAM RoleAn AWS identity with permission policies, assumable by trusted entities. Not a user.
Instance ProfileA 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:PassRoleAn 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=required in instance metadata options.
  • 🏗️ For new infrastructure, use IaC (CloudFormation or Terraform) to always declare the IamInstanceProfile property at launch — eliminate this class of mistake entirely.

Related Posts

Comments

Popular posts from this blog

EC2 No Internet Access in Custom VPC: Attaching an Internet Gateway and Fixing Route Tables

EC2 SSH Connection Timeout: The Exact Security Group Rules You Need to Fix It

IAM User vs. IAM Role: Why Your EC2 Instance Should Never Use a User