Skip to content
AWS aws iam 5 min read

EC2 Instance Profiles

When an application running on an EC2 (Elastic Compute Cloud, AWS’s virtual server) needs to call other AWS services, it needs credentials. The wrong way is to copy an access key onto the server. The right way is an instance profile: a small container that links an IAM (Identity and Access Management) role to the instance, so AWS delivers temporary, auto-rotating credentials straight to your code. This page explains what an instance profile is, how to attach one, and why you should never store long-lived keys on a server again.

What an instance profile actually is

An IAM role grants permissions, but a role cannot be attached to an EC2 instance directly. EC2 needs a wrapper object called an instance profile. Think of the instance profile as an envelope and the role as the letter inside it: EC2 understands envelopes, IAM understands letters.

When you launch an instance with an instance profile attached, AWS publishes temporary security credentials (an access key, a secret key, and a session token) to the Instance Metadata Service (IMDS) — a special internal endpoint reachable only from inside that instance at http://169.254.169.254. Those credentials are short-lived and AWS rotates them automatically several times a day. Your application never sees a permanent key.

Key point: an instance profile holds exactly one role. When you create a role in the AWS Console, the matching instance profile is created for you automatically with the same name. When you use the CLI directly, you must create and wire the profile yourself.

When to use this (and when not to)

Use an instance profile whenever code running on an EC2 instance needs to call AWS — reading from an S3 (Simple Storage Service) bucket, writing to DynamoDB, publishing to SNS, and so on. This is the standard, recommended pattern.

Do not use it for code running outside EC2. For a Lambda function use a Lambda execution role; for ECS tasks use a task role; for a developer’s laptop use IAM Identity Center or short-lived CLI sessions. Instance profiles are specific to EC2 (and a few EC2-backed services like Elastic Beanstalk).

How the SDK discovers credentials

Every modern AWS SDK and the AWS CLI follow a default credential provider chain. They look for credentials in order: environment variables, shared config files, then the instance metadata service. Because IMDS is last-but-reliable, you simply write normal SDK code with no credentials at all, and it “just works” on an EC2 instance with an instance profile.

import boto3

# No keys passed in. The SDK finds instance-profile credentials via IMDS.
s3 = boto3.client("s3")
for bucket in s3.list_buckets()["Buckets"]:
    print(bucket["Name"])

You can confirm the instance is using a role by querying IMDS directly. AWS recommends IMDSv2, which requires a short-lived token first.

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/

Output:

my-app-role

That name is the role delivering credentials. Querying one level deeper returns the live, temporary keys (which AWS rotates for you).

Attaching a role using the Console

Use this when you are working interactively or launching a single instance.

  1. Open the EC2 console and choose Instances in the left menu.
  2. Select your instance (for example i-0a1b2c3d4e5f).
  3. Choose Actions -> Security -> Modify IAM role.
  4. In the IAM role dropdown, pick the role you want (for example my-app-role). If the list is empty, create a role first with the EC2 trusted entity type.
  5. Choose Update IAM role.

The change takes effect within a minute or two — no reboot needed. To attach a role while creating a new instance, expand Advanced details in the launch wizard and select the role under IAM instance profile.

Attaching a role using the AWS CLI

The CLI command is associate-iam-instance-profile. Note it takes an instance profile name, not a role name. If you created the role in the Console, the profile shares the role’s name.

aws ec2 associate-iam-instance-profile \
  --instance-id i-0a1b2c3d4e5f \
  --iam-instance-profile Name=my-app-role

Output:

{
    "IamInstanceProfileAssociation": {
        "AssociationId": "iip-assoc-0a1b2c3d4e5f0a1b2",
        "InstanceId": "i-0a1b2c3d4e5f",
        "IamInstanceProfile": {
            "Arn": "arn:aws:iam::123456789012:instance-profile/my-app-role",
            "Id": "AIPAEXAMPLE0A1B2C3D4"
        },
        "State": "associating"
    }
}

Creating the profile yourself (CLI-only roles)

If you created the role via the CLI, the instance profile does not exist yet. Create it, then add the role to it.

# 1. Create the empty instance profile
aws iam create-instance-profile --instance-profile-name my-app-role

# 2. Put the role inside it
aws iam add-role-to-instance-profile \
  --instance-profile-name my-app-role \
  --role-name my-app-role

The role must trust the EC2 service in its trust policy ("Service": "ec2.amazonaws.com"), otherwise EC2 cannot assume it.

Infrastructure as Code

In production, wire this with templates so it is repeatable. Here is the CloudFormation form.

Resources:
  AppRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess

  AppInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref AppRole

The gotcha: never bake keys into an AMI or user data

The whole reason instance profiles exist is to kill long-lived keys. A baked-in key is a permanent secret sitting on disk; if the AMI (Amazon Machine Image, a saved server template) is shared or the instance is compromised, that key leaks and never expires until someone notices.

Security warning: never put aws_access_key_id in an AMI, in user data, in environment variables, or in a config file on the instance. Instance-profile credentials from IMDS are temporary and rotate automatically, so a leak is far less damaging. Always enforce IMDSv2 (HttpTokens=required) to block server-side request forgery attacks that try to steal those credentials.

There is no extra charge for instance profiles, roles, or IMDS — it is all free. You only pay for the EC2 instance itself.

Best Practices

  • Attach a role to every EC2 instance that calls AWS, and remove all static keys from the instance.
  • Give the role only the permissions the app needs (least privilege), not broad access like AdministratorAccess.
  • Enforce IMDSv2 by setting --metadata-options HttpTokens=required on the instance.
  • Use a separate role per application or environment so a compromise stays contained.
  • Define roles and instance profiles in CloudFormation or Terraform so they are version-controlled and reproducible.
  • Never log or print the temporary credentials your SDK retrieves from IMDS.
  • Audit attached roles regularly with aws ec2 describe-iam-instance-profile-associations.
Last updated June 15, 2026
Was this helpful?