Setting Up Ec2 Instance Profiles
AWSOfficialPublished by AWS
- Skill
- Workflow
- Cloud and DevOps
- Apache-2.0
- Medium risk
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a standardized workflow for configuring EC2 instances to interact with AWS services securely. By replacing hardcoded credentials with IAM instance profiles, it enables instances to assume roles and retrieve temporary credentials automatically. The process covers identifying required permissions, managing IAM roles with least-privilege policies, attaching profiles to instances, and verifying the configuration.
What it does
- Guides users through the creation or reuse of IAM roles for EC2 instances.
- Ensures IAM roles have the correct trust policies to allow EC2 service assumption.
- Automates the attachment of instance profiles to target EC2 instances.
- Provides troubleshooting steps for common credential and permission issues.
- Helps identify and remove insecure hardcoded credentials from applications.
When to use it
- When an EC2 instance needs to access AWS services like S3, DynamoDB, or CloudWatch.
- When you need to eliminate hardcoded AWS credentials from your application code.
- When setting up secure, temporary credential management for cloud-hosted applications.
- When migrating from static access keys to IAM-based authentication.
Advantages
- Enhances security by removing the need for long-lived hardcoded credentials.
- Reduces operational overhead by automating temporary credential rotation.
- Ensures adherence to the principle of least privilege through role-based access.
- Provides a clear, repeatable process for consistent cloud infrastructure configuration.
Usage examples
- “I need to give my EC2 instance i-0123456789abcdef0 in us-east-1 access to S3 and DynamoDB.”
- The agent will initiate the setup workflow, verify the instance, and guide you through creating or attaching an IAM role with the necessary permissions.
- “How do I replace the existing IAM role on my EC2 instance with a new one?”
- The agent will check the current instance profile, prompt for confirmation to replace it, and execute the update procedure safely.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/aws/setting-up-ec2-instance-profiles/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip setting-up-ec2-instance-profiles.zip -d .agents/skills
Documentation by AWS
Setting Up EC2 Instance Profiles
Overview
Domain expertise for granting EC2 instances secure access to AWS services using IAM roles and instance profiles. Covers the full lifecycle: identifying required permissions, creating or reusing IAM roles with least-privilege policies, creating instance profiles, attaching them to EC2 instances, and verifying credential availability.
Configure an EC2 instance profile
To set up an IAM role and instance profile for an EC2 instance, follow the procedure exactly. See EC2 instance profile setup procedure.
Troubleshooting
Instance not found
Verify the instance ID and region are correct. List instances with aws ec2 describe-instances --region <region>.
Instance already has a profile
The procedure handles replacement — it will prompt before disassociating the existing profile.
Credentials not available after attachment
Instance profile propagation can take 30–60 seconds. Applications may need a restart to pick up new credentials.
Access denied errors
Check that the role's policies include the required actions and resource ARNs. Review CloudTrail logs for the specific denied action.
Application still uses hardcoded credentials
Remove credentials from config files, environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY), and ~/.aws/credentials. The SDK default credential chain will then use the instance profile.
Files
- ATTRIBUTION.mdAdded by AutoGineer947 B
- LICENSELicence file added by AutoGineer9.9 KiB
- NOTICELicence file added by AutoGineer67 B
- SKILL.md1.8 KiB
- references/ec2-instance-profile-setup.md33.0 KiB
references/ec2-instance-profile-setup.md
EC2 Instance Profile Setup
Overview
This SOP guides you through the complete process of granting an EC2 instance permissions to call AWS services securely using IAM roles and instance profiles. Instead of embedding AWS credentials in your application code, instance profiles allow EC2 instances to assume IAM roles and obtain temporary credentials automatically. This SOP helps identify required permissions, creates or uses an existing IAM role, sets up the instance profile, and attaches it to the target EC2 instance.
Parameters
Prompt the user in a single message to provide all required parameters at once. Clearly list the required parameters and their descriptions, and include any optional parameters with their default values. Do not proceed until you have received and confirmed all required parameters. If any required parameter is missing or unclear, you MUST explicitly request the missing information before moving forward.
instance_id (required): The ID of the EC2 instance to configure (e.g., "i-1234567890abcdef0")
region (required): The AWS region where the instance is running (e.g., "us-east-1", "eu-west-1")
services_needed (required): Comma-separated list of AWS services the instance needs to access (e.g., "s3,dynamodb,sqs", "s3,lambda,cloudwatch")
role_name (optional): Name for the IAM role to create or reuse (default: "{instance_id}-role")
Only proceed to the steps below if you have all required information.
Steps
1. Verify Dependencies
Check for required tools and warn the user if any are missing.
Constraints:
You MUST verify the following tools are available in your context:
call_aws
You MUST ONLY check for tool existence and MUST NOT attempt to run the tools because running tools during verification could cause unintended side effects, consume resources unnecessarily, or trigger actions before the user is ready
You MUST inform the user about any missing tools with a clear message
You MUST ask if the user wants to proceed anyway despite missing tools
You MUST respect the user's decision to proceed or abort
2. Verify EC2 Instance Exists
Confirm the target EC2 instance exists and retrieve its current configuration.
Constraints:
You MUST verify the instance exists using:
aws ec2 describe-instances --instance-ids ${instance_id} --region ${region}You MUST check if the instance already has an IAM instance profile attached by examining the
IamInstanceProfilefield in the responseYou MUST extract the instance name from tags (if available) for better identification
You MUST inform the user of the instance's current state (running, stopped, etc.)
You MUST warn the user if the instance already has an instance profile attached and ask if they want to replace it
You MUST handle the case where the instance does not exist and provide a clear error message
You MUST retain the
describe-instancesoutput for reuse in later steps (e.g., to reference association IDs in Step 8)
3. Check for Existing IAM Role
Determine whether to create a new IAM role or reuse an existing one, then verify the selected option.
Constraints:
You MUST ask the user if they want to reuse an existing IAM role or create a new one
You MUST require the user to provide the role name if they choose to reuse an existing role and one was not already supplied
If the user opts to create a new role, you MUST proceed to Step 4 and skip the remaining checks in this step
If the user opts to reuse an existing role:
You MUST verify the role exists using:
aws iam get-role --role-name ${role_name} --region ${region}You MUST retrieve the role's trust policy to verify it allows EC2 service to assume it
You MUST check the trust policy contains:
{ "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" }You MUST list all policies attached to the role using:
aws iam list-attached-role-policies --role-name ${role_name}You MUST list all inline policies using:
aws iam list-role-policies --role-name ${role_name}You MUST present the existing permissions to the user for review
You MUST ask the user if they want to add additional permissions or use the role as-is
You MUST handle the case where the role does not exist and inform the user
You MUST verify the role has the correct trust relationship for EC2, and if not, ask the user if they want to update it
4. Identify Required Permissions
Analyze the requested services and determine appropriate IAM permissions.
Constraints:
You MUST skip this step if the user chooses to reuse an existing role without modifications
You MUST analyze each service in
services_neededand recommend appropriate permissionsYou MUST use the principle of least privilege - recommend specific actions rather than full access when possible
You MUST provide permission recommendations based on common use cases:
s3: For general S3 access, recommend
s3:GetObject,s3:PutObject,s3:ListBucketon specific bucketsdynamodb: For DynamoDB access, recommend
dynamodb:GetItem,dynamodb:PutItem,dynamodb:Query,dynamodb:Scanon specific tablessqs: For SQS access, recommend
sqs:SendMessage,sqs:ReceiveMessage,sqs:DeleteMessageon specific queuessns: For SNS access, recommend
sns:Publishon specific topicslambda: For Lambda invocation, recommend
lambda:InvokeFunctionon specific functionscloudwatch: For CloudWatch Logs, recommend
logs:CreateLogGroup,logs:CreateLogStream,logs:PutLogEventssecretsmanager: For Secrets Manager, recommend
secretsmanager:GetSecretValueon specific secretsssm: For Systems Manager Parameter Store, recommend
ssm:GetParameter,ssm:GetParameterson specific parameterskms: For KMS encryption, recommend
kms:Decrypt,kms:Encrypton specific keysec2: For EC2 operations, recommend specific actions like
ec2:DescribeInstances,ec2:DescribeTags
You MUST NOT recommend FullAccess or overly broad managed policies (e.g.,
AmazonS3FullAccess,AmazonDynamoDBFullAccess,AmazonSQSFullAccess,AmazonSNSFullAccess,SecretsManagerReadWrite,CloudWatchLogsFullAccess)You MUST ask the user if they want to:
Create custom policies with specific permissions (RECOMMENDED - most secure, principle of least privilege)
Use AWS managed read-only policies (broader but acceptable for read-only use cases, e.g.,
AmazonS3ReadOnlyAccess)Provide their own custom policy JSON
You MUST present the recommended permissions to the user in a clear, organized format
You MUST ask the user to confirm or modify the permissions before proceeding
You MUST warn users about overly permissive policies (e.g.,
*:*actions or resources)
5. Create or Update IAM Role
Create a new IAM role or update an existing one with the identified permissions.
Constraints:
You MUST skip role creation if the user chooses to reuse an existing role without modifications
You MUST create the trust policy document that allows EC2 to assume the role:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }You MUST save the trust policy to a temporary file or use inline JSON
You MUST create the IAM role using:
aws iam create-role --role-name ${role_name} --assume-role-policy-document file://trust-policy.json --description "IAM role for EC2 instance ${instance_id} to access ${services_needed}"You MUST handle the case where the role already exists with a clear message
You MUST add tags to the role for better tracking:
aws iam tag-role --role-name ${role_name} --tags Key=ManagedBy,Value=ec2-instance-profile-setup Key=InstanceId,Value=${instance_id} Key=CreatedDate,Value=$(date +%Y-%m-%d)You MUST wait for the role to be created before proceeding (typically immediate, but verify with describe command)
You MUST verify the role was created successfully using:
aws iam get-role --role-name ${role_name}
6. Attach Policies to IAM Role
Attach the necessary AWS managed policies or create and attach custom inline policies.
Constraints:
You MUST attach each AWS managed policy using:
aws iam attach-role-policy --role-name ${role_name} --policy-arn ${policy_arn}You MUST use proper policy ARNs in the format:
arn:aws:iam::aws:policy/${policy_name}Common managed policy ARNs to use (PREFER LEAST PRIVILEGE — avoid FullAccess policies):
S3 Read Only:
arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess(acceptable for read-only use cases)DynamoDB Read Only:
arn:aws:iam::aws:policy/AmazonDynamoDBReadOnlyAccess(acceptable for read-only use cases)SQS Read Only:
arn:aws:iam::aws:policy/AmazonSQSReadOnlyAccess(acceptable for queue monitoring/inspection only — consumers that process messages need a custom policy withsqs:ReceiveMessageandsqs:DeleteMessage)SSM Managed Instance Core:
arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
You MUST NOT recommend or attach FullAccess or overly broad managed policies (e.g.,
AmazonS3FullAccess,AmazonDynamoDBFullAccess,AmazonSQSFullAccess,AmazonSNSFullAccess,SecretsManagerReadWrite,CloudWatchLogsFullAccess). Instead, create custom policies scoped to specific resources.You MUST prefer custom inline policies over managed policies for write access:
aws iam put-role-policy --role-name ${role_name} --policy-name ${policy_name} --policy-document file://custom-policy.jsonYou MUST validate that all policies were attached successfully
You MUST list all attached policies to confirm:
aws iam list-attached-role-policies --role-name ${role_name}You MUST also list inline policies to confirm:
aws iam list-role-policies --role-name ${role_name}You MUST handle errors such as invalid policy ARNs or permission issues
You MUST inform the user of all attached policies
7. Create Instance Profile
Create an instance profile that wraps the IAM role for EC2 use.
Constraints:
You MUST check if an instance profile with the same name already exists using:
aws iam get-instance-profile --instance-profile-name ${role_name}You MUST handle the case where the instance profile already exists:
If it exists and contains the correct role, you MAY reuse it
If it exists but contains a different role, you MUST ask the user if they want to remove the old role and add the new one
If it exists with the same role, you MAY skip creation and proceed to attachment
You MUST create the instance profile if it doesn't exist:
aws iam create-instance-profile --instance-profile-name ${role_name}You MUST add the IAM role to the instance profile:
aws iam add-role-to-instance-profile --instance-profile-name ${role_name} --role-name ${role_name}You MUST verify the instance profile was created successfully using:
aws iam get-instance-profile --instance-profile-name ${role_name}You MUST wait for the instance profile to be fully created before proceeding (check that the role is listed in the instance profile)
You MUST extract the instance profile ARN for the next step
8. Attach Instance Profile to EC2 Instance
Associate the instance profile with the target EC2 instance.
Constraints:
You MUST check if the instance already has an instance profile attached (from step 2)
You MUST handle existing instance profile associations:
If an instance profile is already attached, you MUST first disassociate it using:
aws ec2 disassociate-iam-instance-profile --association-id ${association_id}You MUST wait for the disassociation to complete before proceeding
You MUST attach the new instance profile using:
aws ec2 associate-iam-instance-profile --instance-id ${instance_id} --iam-instance-profile Name=${role_name} --region ${region}You MUST verify the attachment was successful using:
aws ec2 describe-instances --instance-ids ${instance_id} --region ${region}You MUST check that the
IamInstanceProfilefield now contains the correct instance profile ARNYou MUST inform the user that the attachment was successful
You MUST note that the instance profile association takes effect immediately for new credential requests, but existing application sessions may need to refresh their credentials
9. Verify Configuration and Test Access
Confirm the instance profile is properly configured and test that credentials are accessible.
Constraints:
You MUST verify the complete configuration by checking:
Instance profile is attached to the instance
Instance profile contains the correct role
Role has the expected policies attached
Trust relationship allows EC2 to assume the role
You MUST provide instructions for testing the configuration from within the instance using IMDSv2 (token-based):
# SSH into the instance and run: # 1. Get an IMDSv2 session token (valid for 6 hours) TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") # 2. Verify instance metadata service can provide credentials curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/ # 3. Retrieve temporary credentials (will show role name) curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/${role_name} # 4. Test AWS CLI with the instance profile (no credentials needed in CLI config) aws sts get-caller-identity # 5. Test access to a specific service (example for S3) aws s3 lsYou MUST NOT use IMDSv1 (plain curl without token) — always use IMDSv2 with a session token
You MUST explain that applications using AWS SDKs will automatically use these credentials
You MUST provide code examples for common SDK languages to verify automatic credential resolution:
Python (boto3):
import boto3 # No credentials needed - automatically uses instance profile s3 = boto3.client('s3') print(s3.list_buckets())Node.js (AWS SDK v3):
import { S3Client, ListBucketsCommand } from "@aws-sdk/client-s3"; // No credentials needed - automatically uses instance profile const client = new S3Client({ region: "us-east-1" }); const command = new ListBucketsCommand({}); const response = await client.send(command); console.log(response);Java (AWS SDK v2):
import software.amazon.awssdk.services.s3.S3Client; // No credentials needed - automatically uses instance profile S3Client s3 = S3Client.builder().region(Region.US_EAST_1).build(); s3.listBuckets();
You MUST remind the user to remove any hardcoded AWS credentials from their application code
You MUST warn about credential caching - applications may need to be restarted to pick up the new credentials
10. Generate Configuration Summary Report
Create a comprehensive report documenting the setup.
Constraints:
You MUST create a detailed summary report containing:
Instance ID and region
IAM role name and ARN
Instance profile name and ARN
List of all attached policies (managed and inline)
Services granted access
Trust policy configuration
Verification test results
Instructions for testing from within the instance
Security best practices and recommendations
Next steps for the user
You MUST include security recommendations:
Regularly review and audit IAM permissions
Use resource-level permissions when possible (specify exact ARNs)
Enable CloudTrail to log API calls made by the instance
Consider using IAM policy conditions for additional security (e.g., IP restrictions, time-based access)
Rotate credentials regularly (automatic with instance profiles)
Monitor for unusual API activity using CloudWatch
You MUST provide instructions for updating permissions in the future:
# To add more policies: aws iam attach-role-policy --role-name ${role_name} --policy-arn ${new_policy_arn} # To remove policies: aws iam detach-role-policy --role-name ${role_name} --policy-arn ${policy_arn} # To update inline policies: aws iam put-role-policy --role-name ${role_name} --policy-name ${policy_name} --policy-document file://updated-policy.jsonYou MUST provide instructions for cleanup if needed:
# To remove the instance profile from the instance: aws ec2 disassociate-iam-instance-profile --association-id ${association_id} # To delete the instance profile: aws iam remove-role-from-instance-profile --instance-profile-name ${role_name} --role-name ${role_name} aws iam delete-instance-profile --instance-profile-name ${role_name} # To delete the role (must detach policies first): aws iam detach-role-policy --role-name ${role_name} --policy-arn ${policy_arn} aws iam delete-role --role-name ${role_name}You MUST format the report in a clear, well-organized manner
You MUST present the report to the user
Examples
Example Input
instance_id: i-0abcd1234efgh5678
region: us-east-1
services_needed: s3,dynamodb,cloudwatch
role_name: web-server-roleDuring Step 3, the user chose to create a new IAM role.
Example Output
# EC2 Instance Profile Setup Report
**Instance ID:** i-0abcd1234efgh5678
**Region:** us-east-1
**IAM Role:** web-server-role
**Instance Profile:** web-server-role
## Configuration Summary
### Instance Details
- **Instance ID:** i-0abcd1234efgh5678
- **Instance Name:** web-server-prod-01
- **Instance State:** running
- **Previous Instance Profile:** None
- **New Instance Profile:** web-server-role
### IAM Role Configuration
- **Role Name:** web-server-role
- **Role ARN:** arn:aws:iam::123456789012:role/web-server-role
- **Trust Policy:** Configured to allow EC2 service to assume role
- **Created:** 2025-10-13
### Attached Policies
#### Least Privilege Policy Examples
**SECURITY BEST PRACTICE: Always use the minimum permissions required for your use case.**
#### Custom S3 Policy (Specific Bucket Access)
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-app-bucket/*"
}
]
}Custom CloudWatch Logs Policy (Specific Log Group)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:log-group:/aws/ec2/my-app:*"
}
]
}Custom DynamoDB Policy (Specific Table Access)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:UpdateItem"
],
"Resource": "arn:aws:dynamodb:*:*:table/my-app-table"
}
]
}AWS Managed Read-Only Policies (Acceptable for Read-Only Use Cases)
AmazonS3ReadOnlyAccess
ARN:
arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccessGrants: Read-only access to all S3 buckets
AmazonDynamoDBReadOnlyAccess
ARN:
arn:aws:iam::aws:policy/AmazonDynamoDBReadOnlyAccessGrants: Read-only access to all DynamoDB tables
Use custom policy for write access scoped to specific tables
Custom Inline Policies
CloudWatchLogsWrite (scoped to specific log group):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:log-group:/aws/ec2/web-server-prod-01:*"
}
]
}Instance Profile Configuration
Instance Profile Name: web-server-role
Instance Profile ARN: arn:aws:iam::123456789012:instance-profile/web-server-role
Associated IAM Role: web-server-role
Attached to Instance: Yes
Verification Results
Instance Profile Attachment: ✓ Success
Instance profile successfully attached to instance i-0abcd1234efgh5678
Configuration is active and ready to use
Role and Policy Validation: ✓ Success
IAM role exists and is properly configured
Trust policy allows EC2 service to assume role
All requested policies are attached
Credential Availability: Ready to Test
Follow the test instructions below to verify from within the instance.
Testing Instructions
From Within the EC2 Instance
SSH into your instance and run these commands:
# 1. Get an IMDSv2 session token (valid for 6 hours)
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
# 2. Check if instance profile is available
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Expected output: web-server-role
# 3. Retrieve temporary credentials
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/web-server-role
# Expected output: JSON with AccessKeyId, SecretAccessKey, Token
# 4. Verify AWS CLI can use the credentials
aws sts get-caller-identity
# Expected output: Your account ID, user ID, and role ARN
# 5. Test S3 access
aws s3 ls
# Expected output: List of S3 buckets (if any exist)
# 6. Test DynamoDB access
aws dynamodb list-tables
# Expected output: List of DynamoDB tables (if any exist)
# 7. Test CloudWatch Logs write access
aws logs create-log-group --log-group-name /aws/ec2/web-server-prod-01
# Expected output: (none on success; ResourceAlreadyExistsException if it already exists)
Application Code Examples
Python (boto3)
import boto3
# No explicit credentials needed - boto3 automatically uses instance profile
s3 = boto3.client('s3')
dynamodb = boto3.resource('dynamodb')
# Test S3 access
buckets = s3.list_buckets()
print(f"Found {len(buckets['Buckets'])} S3 buckets")
# Test DynamoDB access
table = dynamodb.Table('your-table-name')
response = table.get_item(Key={'id': '123'})
print(response)Node.js (AWS SDK v3)
import { S3Client, ListBucketsCommand } from "@aws-sdk/client-s3";
import { DynamoDBClient, ListTablesCommand } from "@aws-sdk/client-dynamodb";
// No explicit credentials needed - SDK automatically uses instance profile
const s3Client = new S3Client({ region: "us-east-1" });
const dynamoClient = new DynamoDBClient({ region: "us-east-1" });
// Test S3 access
const s3Response = await s3Client.send(new ListBucketsCommand({}));
console.log(`Found ${s3Response.Buckets.length} S3 buckets`);
// Test DynamoDB access
const dynamoResponse = await dynamoClient.send(new ListTablesCommand({}));
console.log(`Found ${dynamoResponse.TableNames.length} DynamoDB tables`);Java (AWS SDK v2)
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.regions.Region;
// No explicit credentials needed - SDK automatically uses instance profile
S3Client s3 = S3Client.builder()
.region(Region.US_EAST_1)
.build();
DynamoDbClient dynamoDb = DynamoDbClient.builder()
.region(Region.US_EAST_1)
.build();
// Test S3 access
var s3Response = s3.listBuckets();
System.out.println("Found " + s3Response.buckets().size() + " S3 buckets");
// Test DynamoDB access
var dynamoResponse = dynamoDb.listTables();
System.out.println("Found " + dynamoResponse.tableNames().size() + " DynamoDB tables");Security Best Practices
Implemented
✓ Using IAM roles instead of hardcoded credentials
✓ Instance profile provides automatic credential rotation
✓ Credentials are temporary and expire automatically
Recommended Additional Steps
Apply Least Privilege Principle
Current setup uses managed policies with broad permissions
Consider creating custom policies with specific resource ARNs
Example for S3 bucket-specific access:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::your-specific-bucket", "arn:aws:s3:::your-specific-bucket/*" ] } ] }
Enable CloudTrail Logging
# Track all API calls made using this role aws cloudtrail create-trail --name instance-audit-trail \ --s3-bucket-name your-cloudtrail-bucket aws cloudtrail start-logging --name instance-audit-trailSet Up CloudWatch Alarms
Monitor for unusual API activity
Alert on unauthorized access attempts
Track resource usage patterns
Regular Permission Audits
Review attached policies quarterly
Remove unused permissions
Use AWS IAM Access Analyzer to identify unused access
Use Resource Tags
Tag resources accessed by this instance
Implement tag-based access control policies
Track costs by application/instance
Consider Using IAM Policy Conditions
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject" ], "Resource": "arn:aws:s3:::your-specific-bucket/*", "Condition": { "StringEquals": { "aws:PrincipalTag/Environment": "production" } } } ] }
Use with tag-based access control (ABAC): tag the IAM role with Environment=production and add aws:PrincipalTag conditions to the IAM policy (as shown above) to restrict access based on the role's tags. This lets you manage access across many instances without updating policies individually.
Managing Permissions
Adding More Permissions
# Attach additional managed policy
aws iam attach-role-policy \
--role-name web-server-role \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
# Add custom inline policy (RECOMMENDED - most secure)
aws iam put-role-policy \
--role-name web-server-role \
--policy-name CustomS3Access \
--policy-document file://custom-policy.jsonRemoving Permissions
# Detach managed policy
aws iam detach-role-policy \
--role-name web-server-role \
--policy-arn arn:aws:iam::aws:policy/AmazonDynamoDBReadOnlyAccess
# Delete inline policy
aws iam delete-role-policy \
--role-name web-server-role \
--policy-name CustomS3AccessUpdating Existing Policies
# Update inline policy (overwrites existing)
aws iam put-role-policy \
--role-name web-server-role \
--policy-name CustomS3Access \
--policy-document file://updated-policy.jsonCleanup Instructions
If you need to remove this configuration:
# 1. Disassociate instance profile from instance
aws ec2 disassociate-iam-instance-profile \
--association-id iip-assoc-0abcd1234efgh5678
# 2. Remove role from instance profile
aws iam remove-role-from-instance-profile \
--instance-profile-name web-server-role \
--role-name web-server-role
# 3. Delete instance profile
aws iam delete-instance-profile \
--instance-profile-name web-server-role
# 4. Detach all policies from role
aws iam delete-role-policy \
--role-name web-server-role \
--policy-name CloudWatchLogsWrite
aws iam detach-role-policy \
--role-name web-server-role \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
aws iam detach-role-policy \
--role-name web-server-role \
--policy-arn arn:aws:iam::aws:policy/AmazonDynamoDBReadOnlyAccess
aws iam detach-role-policy \
--role-name web-server-role \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
# 5. Delete the IAM role
aws iam delete-role --role-name web-server-roleNext Steps
Remove Hardcoded Credentials: Search your application code for hardcoded AWS credentials and remove them
Test Application: Restart your application and verify it can access AWS services using the instance profile
Monitor Activity: Check CloudTrail logs to ensure the instance is making expected API calls
Refine Permissions: After testing, consider tightening permissions to follow least privilege principle
Document Configuration: Add this configuration to your infrastructure documentation and deployment scripts
Implement Monitoring: Set up CloudWatch alarms for permission-denied errors and unusual activity
Troubleshooting
Application Cannot Access AWS Services
Ensure the application is using the AWS SDK's default credential provider chain
Check that no explicit credentials are configured in application config files
Verify the instance profile is attached:
aws ec2 describe-instances --instance-ids i-0abcd1234efgh5678Test credential retrieval from metadata service (see testing instructions above)
Restart the application to refresh credential cache
Access Denied Errors
Verify the required permissions are attached to the role
Check for deny policies that might override allow permissions
Ensure resource-level permissions include the specific resources being accessed
Review CloudTrail logs to identify the specific denied action
Test with broader permissions temporarily to isolate the issue
Instance Profile Not Available in Metadata Service
Wait 30-60 seconds after attaching instance profile for propagation
Verify the instance profile is properly associated with the instance
Check that IMDSv2 is configured — use token-based requests (see testing instructions above)
Ensure security groups allow outbound traffic to metadata service (should be default)
Role Assumption Failures
Verify the trust policy allows ec2.amazonaws.com as principal
Check that the role has not been deleted or modified
Ensure IAM service is available in the region
Summary
Successfully configured EC2 instance i-0abcd1234efgh5678 to securely access AWS services using IAM role web-server-role. The instance can now access S3, DynamoDB, and CloudWatch Logs without hardcoded credentials. Follow the testing instructions above to verify the configuration and remove any existing hardcoded credentials from your application code.
## Troubleshooting
### EC2 Instance Does Not Exist
If the specified instance ID is not found, verify you are using the correct instance ID and region. Use `aws ec2 describe-instances --region ${region}` to list all instances in the region.
### Instance Already Has an Instance Profile
If the instance already has an instance profile attached, the script will prompt you to confirm whether you want to replace it. Replacing an instance profile will immediately change the permissions available to applications running on the instance.
### IAM Role Name Conflicts
If a role with the specified name already exists but has different configurations, you MUST prompt the user either to choose a different name or to confirm updating the existing role. Consider using descriptive, unique names like `{application}-{environment}-{instance-name}-role`.
### Permission Denied Errors
Ensure your AWS credentials have the necessary IAM permissions to create roles, instance profiles, and attach policies. Required permissions include:
- `iam:CreateRole`
- `iam:GetRole`
- `iam:AttachRolePolicy`
- `iam:CreateInstanceProfile`
- `iam:AddRoleToInstanceProfile`
- `ec2:AssociateIamInstanceProfile`
- `ec2:DescribeInstances`
- `ec2:DisassociateIamInstanceProfile`
### Instance Profile Takes Time to Propagate
After attaching an instance profile, it may take 30-60 seconds for the credentials to become available in the instance metadata service. Applications may need to retry credential requests or be restarted.
### Trust Policy Validation Failures
If you're using an existing role and the trust policy doesn't allow EC2 to assume it, you'll need to update the trust policy using:
```bash
aws iam update-assume-role-policy --role-name ${role_name} --policy-document file://trust-policy.jsonOverly Permissive Policies
If you receive warnings about overly permissive policies, consider using more restrictive permissions with specific resource ARNs rather than wildcard (*) resources. This follows the principle of least privilege and reduces security risks.
Application Still Uses Hardcoded Credentials
Even after setting up an instance profile, applications may continue using hardcoded credentials if they are explicitly configured. You must remove any AWS credentials from:
Application configuration files
Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
Credential files (~/.aws/credentials)
Multiple Roles Need to Be Combined
If your application needs permissions from multiple existing roles, you cannot attach multiple instance profiles to a single instance. Instead, you must create a new role that combines all required permissions from the multiple roles.
Region-Specific Resources
Ensure that the policies grant access to resources in the correct regions. Some AWS services are region-specific, and you may need to specify resources with region-aware ARNs.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
What our scanner noticed· Medium risk
- References to credential files
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- AWS
- Publisher
- AWS (Official)
- Source
- aws/agent-toolkit-for-aws · skills/specialized-skills/ec2-skills/setting-up-ec2-instance-profiles · commit 0a218f8
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- NOTICE (added by AutoGineer from NOTICE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by AWS or AWS.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download setting-up-ec2-instance-profiles.zip