EC2 Instance Profile Attached
Overview
This check verifies that EC2 instances have an IAM instance profile attached. Instance profiles allow applications running on EC2 instances to securely access AWS services using temporary credentials, rather than embedding long-term access keys on the host.
Think of an instance profile as an identity badge for your EC2 instance. When your application needs to talk to other AWS services (like S3 or DynamoDB), the instance profile provides temporary, automatically-rotated credentials instead of requiring you to store permanent passwords on the server.
Risk
Without an instance profile, applications often rely on hardcoded access keys stored directly on the instance. This creates several security risks:
- Credential exposure: Long-term access keys stored on instances can be discovered by attackers who gain access to the host
- Difficult rotation: Static keys embedded in code, configuration files, or AMIs are hard to rotate and may persist in backups or logs
- No automatic expiration: Unlike temporary credentials from instance profiles, static keys remain valid until manually revoked
- Lateral movement: Compromised credentials can be used from anywhere, not just from the EC2 instance
- Audit challenges: It is harder to trace which instance performed an action when using shared static credentials
Instance profiles provide temporary credentials that rotate automatically and are only accessible from the attached instance.
Remediation Steps
Prerequisites
You need:
- AWS Console access with permissions to modify EC2 instances and IAM roles
- An existing IAM role that you want to attach (or permissions to create one)
Required IAM permissions (for administrators)
Your IAM user or role needs these permissions:
ec2:AssociateIamInstanceProfileec2:DescribeIamInstanceProfileAssociationsec2:DescribeInstancesiam:PassRole(on the role being attached)iam:GetRoleiam:GetInstanceProfileiam:CreateInstanceProfile(if creating a new profile)iam:AddRoleToInstanceProfile(if creating a new profile)
AWS Console Method
Step 1: Create an IAM Role (if needed)
If you already have an IAM role for your instance, skip to Step 2.
- Go to IAM Console in us-east-1
- Click Roles in the left sidebar
- Click Create role
- Select AWS service as the trusted entity type
- Select EC2 as the use case
- Click Next
- Search for and select the policies your application needs (e.g.,
AmazonS3ReadOnlyAccess) - Click Next
- Enter a role name like
my-ec2-application-role - Click Create role
Step 2: Attach the Instance Profile to Your EC2 Instance
- Go to EC2 Console in us-east-1
- Click Instances in the left sidebar
- Select the instance that needs an instance profile
- Click Actions > Security > Modify IAM role
- In the dropdown, select the IAM role you want to attach
- Click Update IAM role
The instance profile is now attached. Your application can immediately use AWS SDK calls without providing explicit credentials.
AWS CLI (optional)
Check Current Instance Profile Status
First, check if the instance already has an instance profile:
aws ec2 describe-iam-instance-profile-associations \
--filters "Name=instance-id,Values=i-1234567890abcdef0" \
--region us-east-1
If the output shows an empty IamInstanceProfileAssociations list, the instance has no profile attached.
Create an IAM Role and Instance Profile (if needed)
If you do not have an existing role, create one:
# Create a trust policy file
cat > trust-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
# Create the IAM role
aws iam create-role \
--role-name my-ec2-application-role \
--assume-role-policy-document file://trust-policy.json \
--region us-east-1
# Attach a policy to the role (adjust policy as needed)
aws iam attach-role-policy \
--role-name my-ec2-application-role \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \
--region us-east-1
# Create an instance profile
aws iam create-instance-profile \
--instance-profile-name my-ec2-application-role \
--region us-east-1
# Add the role to the instance profile
aws iam add-role-to-instance-profile \
--instance-profile-name my-ec2-application-role \
--role-name my-ec2-application-role \
--region us-east-1
Attach the Instance Profile to the EC2 Instance
aws ec2 associate-iam-instance-profile \
--instance-id i-1234567890abcdef0 \
--iam-instance-profile Name=my-ec2-application-role \
--region us-east-1
Expected output:
{
"IamInstanceProfileAssociation": {
"InstanceId": "i-1234567890abcdef0",
"State": "associating",
"AssociationId": "iip-assoc-0e7736511a163c209",
"IamInstanceProfile": {
"Id": "AIPAJBLK7RKJKWDXVHIEC",
"Arn": "arn:aws:iam::123456789012:instance-profile/my-ec2-application-role"
}
}
}
CloudFormation (optional)
This template creates an IAM role with an instance profile and an EC2 instance with the profile attached:
AWSTemplateFormatVersion: '2010-09-09'
Description: EC2 instance with IAM instance profile attached
Parameters:
InstanceType:
Type: String
Default: t3.micro
Description: EC2 instance type
ImageId:
Type: AWS::EC2::Image::Id
Description: AMI ID for the instance
SubnetId:
Type: AWS::EC2::Subnet::Id
Description: Subnet ID for the instance
Resources:
EC2InstanceRole:
Type: AWS::IAM::Role
Properties:
RoleName: ec2-application-role
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
Tags:
- Key: Purpose
Value: EC2ApplicationRole
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: ec2-application-role
Roles:
- !Ref EC2InstanceRole
EC2Instance:
Type: AWS::EC2::Instance
Properties:
InstanceType: !Ref InstanceType
ImageId: !Ref ImageId
SubnetId: !Ref SubnetId
IamInstanceProfile: !Ref EC2InstanceProfile
Tags:
- Key: Name
Value: instance-with-profile
Outputs:
InstanceId:
Description: ID of the EC2 instance
Value: !Ref EC2Instance
InstanceProfileArn:
Description: ARN of the instance profile
Value: !GetAtt EC2InstanceProfile.Arn
RoleArn:
Description: ARN of the IAM role
Value: !GetAtt EC2InstanceRole.Arn
Deploy with:
aws cloudformation deploy \
--template-file ec2-instance-profile.yaml \
--stack-name ec2-with-instance-profile \
--parameter-overrides \
ImageId=ami-0abcdef1234567890 \
SubnetId=subnet-0123456789abcdef0 \
--capabilities CAPABILITY_NAMED_IAM \
--region us-east-1
Terraform (optional)
# Variables
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t3.micro"
}
variable "ami_id" {
description = "AMI ID for the instance"
type = string
}
variable "subnet_id" {
description = "Subnet ID for the instance"
type = string
}
# IAM Role for EC2
resource "aws_iam_role" "ec2_role" {
name = "ec2-application-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})
tags = {
Purpose = "EC2ApplicationRole"
}
}
# Attach a managed policy to the role
resource "aws_iam_role_policy_attachment" "s3_read_only" {
role = aws_iam_role.ec2_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
}
# Instance profile
resource "aws_iam_instance_profile" "ec2_profile" {
name = "ec2-application-role"
role = aws_iam_role.ec2_role.name
}
# EC2 Instance with instance profile
resource "aws_instance" "example" {
ami = var.ami_id
instance_type = var.instance_type
subnet_id = var.subnet_id
iam_instance_profile = aws_iam_instance_profile.ec2_profile.name
tags = {
Name = "instance-with-profile"
}
}
# Outputs
output "instance_id" {
description = "ID of the EC2 instance"
value = aws_instance.example.id
}
output "instance_profile_arn" {
description = "ARN of the instance profile"
value = aws_iam_instance_profile.ec2_profile.arn
}
output "role_arn" {
description = "ARN of the IAM role"
value = aws_iam_role.ec2_role.arn
}
Deploy with:
terraform init
terraform plan -var="ami_id=ami-0abcdef1234567890" -var="subnet_id=subnet-0123456789abcdef0"
terraform apply -var="ami_id=ami-0abcdef1234567890" -var="subnet_id=subnet-0123456789abcdef0"
Verification
After attaching the instance profile, verify the configuration:
-
Check in the AWS Console:
- Go to EC2 > Instances
- Select your instance
- In the Details tab, look for IAM Role - it should show the role name
- Click the role name to verify the attached policies
-
Test from the instance (if you have SSH access):
- Connect to the instance
- Run
aws sts get-caller-identity- it should return the role ARN - The AWS CLI and SDKs will automatically use the instance profile credentials
CLI verification commands
Check if the instance has an instance profile attached:
aws ec2 describe-iam-instance-profile-associations \
--filters "Name=instance-id,Values=i-1234567890abcdef0" \
--region us-east-1 \
--query 'IamInstanceProfileAssociations[0].{State:State,ProfileArn:IamInstanceProfile.Arn}'
Expected output for a properly configured instance:
{
"State": "associated",
"ProfileArn": "arn:aws:iam::123456789012:instance-profile/my-ec2-application-role"
}
Check all instances without instance profiles:
aws ec2 describe-instances \
--filters "Name=instance-state-name,Values=running" \
--query 'Reservations[].Instances[?IamInstanceProfile==`null`].{InstanceId:InstanceId,Name:Tags[?Key==`Name`].Value|[0]}' \
--output table \
--region us-east-1
Additional Resources
- AWS Documentation: IAM Roles for Amazon EC2
- AWS Documentation: Instance Profiles
- AWS Blog: Granting an IAM Role to an Existing Amazon EC2 Instance
- AWS Well-Architected Framework: Identity and Access Management
- Trend Micro Cloud Conformity: EC2 Instance Using IAM Roles
Notes
-
No restart required: Attaching an instance profile to a running instance does not require a reboot. The credentials become available immediately.
-
One profile per instance: An EC2 instance can only have one instance profile attached at a time. To change profiles, you must first disassociate the current one or replace it.
-
Principle of least privilege: When creating the IAM role, grant only the permissions your application actually needs. Avoid using overly permissive policies like
AdministratorAccess. -
Application code changes may be needed: If your application currently uses hardcoded credentials, you will need to update it to use the default credential provider chain. Most AWS SDKs automatically detect and use instance profile credentials when no explicit credentials are provided.
-
Remove static credentials: After confirming the instance profile works, remove any access keys from your application configuration, environment variables, or AWS credentials files on the instance.
-
iam:PassRole governance: Control which roles can be attached to instances by limiting the
iam:PassRolepermission. This prevents users from escalating privileges by attaching overly permissive roles. -
Separate roles per workload: Use different IAM roles for different applications or workloads, even if they run on similar infrastructure. This limits the blast radius if one application is compromised.