Attaching an IAM Instance Profile to an EC2 instance is the only secure way for your EC2 instances to access other AWS services without embedding long-lived credentials directly into your application code or configuration files.
Let’s see it in action. Imagine you have an EC2 instance that needs to write logs to an S3 bucket. Normally, you’d need an IAM user with access keys, and then you’d have to securely store and manage those keys on the instance. Instead, we’ll use an instance profile.
First, create an IAM role that your EC2 instance will assume. This role defines the permissions.
aws iam create-role --role-name my-ec2-s3-writer-role --assume-role-policy-document file://trust-policy.json
The trust-policy.json would look like this, allowing EC2 to assume this role:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
Next, attach a policy to this role that grants the necessary permissions. For example, to write objects to a specific S3 bucket:
aws iam put-role-policy --role-name my-ec2-s3-writer-role --policy-name S3WriteAccess --policy-document file://s3-write-policy.json
And s3-write-policy.json:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-secure-log-bucket",
"arn:aws:s3:::my-secure-log-bucket/*"
]
}
]
}
Now, create an instance profile. This is essentially a container for the IAM role that EC2 can use.
aws iam create-instance-profile --instance-profile-name MyEC2S3WriterProfile
Then, associate the IAM role with the instance profile:
aws iam add-role-to-instance-profile --instance-profile-name MyEC2S3WriterProfile --role-name my-ec2-s3-writer-role
Finally, when you launch or modify an EC2 instance, you specify this instance profile. If launching via the AWS CLI:
aws ec2 run-instances \
--image-id ami-0abcdef1234567890 \
--instance-type t2.micro \
--key-name MyKeyPair \
--subnet-id subnet-0123456789abcdef0 \
--iam-instance-profile Name=MyEC2S3WriterProfile
Once the instance is running with the profile attached, any AWS SDK or CLI command run from that instance will automatically pick up temporary credentials associated with my-ec2-s3-writer-role. For example, from the instance’s shell:
aws s3 cp my-local-log.txt s3://my-secure-log-bucket/logs/
This command will succeed without any explicit credentials being configured on the instance, because the EC2 service automatically injects temporary security credentials into the instance’s metadata service (accessible at http://169.254.169.254/latest/meta-data/iam/security-credentials/). The AWS SDKs and CLI are hardcoded to check this endpoint for credentials when running on EC2.
The core problem this solves is the credential management nightmare. Instead of rotating access keys, distributing them, and risking exposure, you delegate permissions to the instance itself. The instance profile acts as a bridge, allowing the EC2 service to assume a specific IAM role on behalf of the instance. This role dictates what actions the instance can perform. When AWS services interact with your EC2 instance (or when code running on the instance makes AWS API calls), the underlying infrastructure ensures that the instance is acting with the permissions granted by the associated role. This is a fundamental security best practice for cloud-native applications running on AWS.
The instance metadata service is a critical component here. It’s not just a convenience; it’s how the AWS SDKs and CLI discover and utilize the temporary credentials. These credentials are automatically refreshed by the EC2 service, meaning you never have to worry about credential expiration on your instance itself.
Many people are unaware that you can attach or modify an instance profile on a running EC2 instance. While it’s best practice to set it at launch, you can detach an existing profile and attach a new one to a running instance, which requires a reboot of the instance for the change to take effect. This is done using the aws ec2 modify-instance-attribute command with the --iam-instance-profile parameter.
The next step is understanding how to restrict these permissions granularly using IAM condition keys, allowing you to limit access based on VPC, source IP, or even specific tags.