Claude Skill

ec2

AWS EC2 virtual machine management — instances, security groups, key pairs, AMIs, EBS volumes, Auto Scaling Groups, Spot Instances, Session Manager, placement groups, and instance lifecycle automation. Trigger on ANY of these, even when EC2 isn't named explicitly: - Launching or

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download itsmostafa-aws-agent-skills-skills_ec2-e786d25.zip · 10 KB
Part of itsmostafa/aws-agent-skills — 17 skills

Install

skills CLI npx skills add https://github.com/itsmostafa/aws-agent-skills/tree/main/skills/ec2
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install itsmostafa-aws-agent-skills@llmmart
Git git clone https://github.com/itsmostafa/aws-agent-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole itsmostafa/aws-agent-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

AWS EC2

Amazon Elastic Compute Cloud (EC2) provides resizable compute capacity in the cloud.

Advanced patterns (Auto Scaling, Spot Fleets, Session Manager, Instance Connect, IMDS, Placement Groups, scheduled scaling): see instance-management.md.

Table of Contents

Core Concepts

Instance Types

Category Example Use Case
General Purpose t3, m6i, t4g (Graviton) Web servers, dev environments
Compute Optimized c6i, c7g (Graviton) Batch processing, gaming
Memory Optimized r6i, r7g (Graviton) Databases, caching
Storage Optimized i3, d3 Data warehousing
Accelerated p4d, g5 ML, graphics

Graviton (ARM) instances (t4g, m7g, c7g, r7g) are ~20% cheaper than x86 equivalents for the same performance — worth considering for new workloads.

Purchasing Options

Option Description
On-Demand Pay by the hour/second
Reserved 1-3 year commitment, up to 72% discount
Spot Unused capacity, up to 90% discount — can be interrupted with 2-minute notice
Savings Plans Flexible commitment-based discount

AMI (Amazon Machine Image)

Template containing OS, software, and configuration for launching instances. Use SSM Parameter Store to look up the latest official AMIs rather than hardcoding IDs:

# Latest Amazon Linux 2 AMI
aws ssm get-parameter \
  --name /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 \
  --query 'Parameter.Value' --output text

# Latest Amazon Linux 2023
aws ssm get-parameter \
  --name /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
  --query 'Parameter.Value' --output text

# Latest Ubuntu 22.04
aws ssm get-parameter \
  --name /aws/service/canonical/ubuntu/server/22.04/stable/current/amd64/hvm/ebs-gp2/ami-id \
  --query 'Parameter.Value' --output text

Security Groups

Virtual firewalls controlling inbound and outbound traffic. Changes take effect immediately — no restart required.

Common Patterns

Launch an Instance

# Create key pair
aws ec2 create-key-pair \
  --key-name my-key \
  --query 'KeyMaterial' \
  --output text > my-key.pem
chmod 400 my-key.pem

# Create security group
aws ec2 create-security-group \
  --group-name web-server-sg \
  --description "Web server security group" \
  --vpc-id vpc-12345678

# Allow SSH and HTTP
aws ec2 authorize-security-group-ingress \
  --group-id sg-12345678 \
  --protocol tcp \
  --port 22 \
  --cidr 10.0.0.0/8

aws ec2 authorize-security-group-ingress \
  --group-id sg-12345678 \
  --protocol tcp \
  --port 80 \
  --cidr 0.0.0.0/0

# Launch instance
aws ec2 run-instances \
  --image-id ami-0123456789abcdef0 \
  --instance-type t3.micro \
  --key-name my-key \
  --security-group-ids sg-12345678 \
  --subnet-id subnet-12345678 \
  --associate-public-ip-address \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=web-server}]'

# Wait until running, then get IP
aws ec2 wait instance-running --instance-ids i-1234567890abcdef0
aws ec2 describe-instances \
  --instance-ids i-1234567890abcdef0 \
  --query 'Reservations[].Instances[].PublicIpAddress' --output text

boto3:

import boto3

ec2 = boto3.resource('ec2')

instances = ec2.create_instances(
    ImageId='ami-0123456789abcdef0',
    InstanceType='t3.micro',
    KeyName='my-key',
    SecurityGroupIds=['sg-12345678'],
    SubnetId='subnet-12345678',
    MinCount=1,
    MaxCount=1,
    TagSpecifications=[{
        'ResourceType': 'instance',
        'Tags': [{'Key': 'Name', 'Value': 'web-server'}]
    }]
)

instance = instances[0]
instance.wait_until_running()
instance.reload()
print(f"Instance ID: {instance.id}")
print(f"Public IP: {instance.public_ip_address}")

User Data Script

OS package manager note:

  • Amazon Linux 2: use amazon-linux-extras install nginx1 -y — yum install nginx fails because nginx is not in the default AL2 repos
  • Amazon Linux 2023: use dnf install -y nginx
  • Ubuntu: use apt-get install -y nginx
  • Amazon Linux 2 / RHEL: httpd (Apache) is always available via yum install -y httpd
# Amazon Linux 2 — nginx via amazon-linux-extras
aws ec2 run-instances \
  --image-id ami-0123456789abcdef0 \
  --instance-type t3.micro \
  --key-name my-key \
  --security-group-ids sg-12345678 \
  --subnet-id subnet-12345678 \
  --user-data '#!/bin/bash
amazon-linux-extras install nginx1 -y
systemctl start nginx
systemctl enable nginx
'

# Amazon Linux 2 — httpd (Apache, simpler alternative)
# --user-data '#!/bin/bash
# yum install -y httpd
# systemctl start httpd
# systemctl enable httpd
# echo "<h1>Hello from $(hostname -f)</h1>" > /var/www/html/index.html
# '

Attach IAM Role

# Create instance profile
aws iam create-instance-profile \
  --instance-profile-name web-server-profile

aws iam add-role-to-instance-profile \
  --instance-profile-name web-server-profile \
  --role-name web-server-role

# Launch with profile
aws ec2 run-instances \
  --image-id ami-0123456789abcdef0 \
  --instance-type t3.micro \
  --iam-instance-profile Name=web-server-profile \
  ...

Create AMI from Instance

aws ec2 create-image \
  --instance-id i-1234567890abcdef0 \
  --name "my-custom-ami-$(date +%Y%m%d)" \
  --description "Custom AMI with web server" \
  --no-reboot

Auto Scaling Group with Spot (Modern Approach)

The recommended way to use Spot Instances at scale is via Auto Scaling Groups with a mixed-instances policy — not the legacy request-spot-instances API. This supports instance diversification to minimize interruptions.

See instance-management.md for the full setup. Quick example:

# 1. Create launch template with IMDSv2
aws ec2 create-launch-template \
  --launch-template-name my-lt \
  --launch-template-data '{
    "ImageId": "ami-0123456789abcdef0",
    "SecurityGroupIds": ["sg-12345678"],
    "IamInstanceProfile": {"Name": "my-profile"},
    "MetadataOptions": {"HttpTokens": "required", "HttpEndpoint": "enabled"}
  }'

# 2. Create ASG with mixed-instances (Spot + On-Demand diversification)
aws autoscaling create-auto-scaling-group \
  --auto-scaling-group-name my-asg \
  --min-size 0 --max-size 20 --desired-capacity 2 \
  --vpc-zone-identifier "subnet-111,subnet-222" \
  --mixed-instances-policy '{
    "LaunchTemplate": {
      "LaunchTemplateSpecification": {"LaunchTemplateName": "my-lt", "Version": "$Latest"},
      "Overrides": [
        {"InstanceType": "c5.xlarge"},
        {"InstanceType": "c5.2xlarge"},
        {"InstanceType": "c5a.xlarge"}
      ]
    },
    "InstancesDistribution": {
      "OnDemandBaseCapacity": 0,
      "OnDemandPercentageAboveBaseCapacity": 0,
      "SpotAllocationStrategy": "capacity-optimized"
    }
  }'

EBS Volume Management

# Create volume
aws ec2 create-volume \
  --availability-zone us-east-1a \
  --size 100 \
  --volume-type gp3 \
  --iops 3000 \
  --throughput 125 \
  --encrypted

# Attach to instance
aws ec2 attach-volume \
  --volume-id vol-12345678 \
  --instance-id i-1234567890abcdef0 \
  --device /dev/sdf

# Create snapshot
aws ec2 create-snapshot \
  --volume-id vol-12345678 \
  --description "Daily backup"

CLI Reference

Instance Management

Command Description
aws ec2 run-instances Launch instances
aws ec2 describe-instances List instances
aws ec2 start-instances Start stopped instances
aws ec2 stop-instances Stop running instances
aws ec2 reboot-instances Reboot instances
aws ec2 terminate-instances Terminate instances
aws ec2 modify-instance-attribute Modify instance settings

Security Groups

Command Description
aws ec2 create-security-group Create security group
aws ec2 describe-security-groups List security groups
aws ec2 authorize-security-group-ingress Add inbound rule
aws ec2 revoke-security-group-ingress Remove inbound rule
aws ec2 authorize-security-group-egress Add outbound rule

AMIs

Command Description
aws ec2 describe-images List AMIs
aws ec2 create-image Create AMI from instance
aws ec2 copy-image Copy AMI to another region
aws ec2 deregister-image Delete AMI

EBS Volumes

Command Description
aws ec2 create-volume Create EBS volume
aws ec2 attach-volume Attach to instance
aws ec2 detach-volume Detach from instance
aws ec2 create-snapshot Create snapshot
aws ec2 modify-volume Resize/modify volume

Best Practices

Security

  • Use IAM roles instead of access keys on instances
  • Restrict security groups — principle of least privilege
  • Use private subnets for backend instances
  • Enable IMDSv2 to prevent SSRF attacks
  • Encrypt EBS volumes at rest
# Require IMDSv2 on existing instance
aws ec2 modify-instance-metadata-options \
  --instance-id i-1234567890abcdef0 \
  --http-tokens required \
  --http-endpoint enabled

Performance

  • Right-size instances — monitor and adjust
  • Use EBS-optimized instances
  • Choose appropriate EBS volume type (gp3 is the default good choice; io2 for high IOPS)
  • Use placement groups for low-latency networking (see instance-management.md)

Cost Optimization

  • Use Spot Instances for fault-tolerant workloads (batch, ML training, CI)
  • Stop/terminate unused instances
  • Use Reserved Instances or Savings Plans for steady-state workloads
  • Delete unused EBS volumes and snapshots
  • Consider Graviton (t4g, m7g, c7g) — ~20% cheaper for same performance

Reliability

  • Use Auto Scaling Groups for high availability (see instance-management.md)
  • Deploy across multiple AZs
  • Use Elastic Load Balancer for traffic distribution
  • Implement health checks

Troubleshooting

Cannot SSH to Instance

First: identify the error type — it points to different root causes:

Error What it means Primary suspects
Connection refused Network is reachable, but SSH daemon is not listening sshd crashed, sshd not installed, OS firewall (ufw/iptables) blocking, wrong port
Connection timed out Packets never arrive Security group blocks port 22, NACL blocks traffic, no public IP, wrong IP
Permission denied Connected, but auth failed Wrong key file, wrong username, key not authorized

Common username by OS:

OS Default SSH user
Amazon Linux 2 / 2023 ec2-user
Ubuntu ubuntu
Debian admin
CentOS / RHEL ec2-user or centos
Windows Administrator

Diagnostic commands:

# 1. Check instance state and public IP
aws ec2 describe-instances \
  --instance-ids i-1234567890abcdef0 \
  --query "Reservations[].Instances[].{State:State.Name,PublicIP:PublicIpAddress,StatusChecks:State.Name}"

# 2. Check instance status (system + instance checks)
aws ec2 describe-instance-status --instance-ids i-1234567890abcdef0

# 3. Check security group rules for port 22
aws ec2 describe-security-groups \
  --group-ids sg-12345678 \
  --query "SecurityGroups[].IpPermissions[?ToPort==\`22\`]"

# 4. Get console output to see boot logs, sshd errors, OOM events
aws ec2 get-console-output \
  --instance-id i-1234567890abcdef0 \
  --latest \
  --query Output --output text

If connection refused — get inside via Session Manager to fix sshd:

# Requires SSM agent on instance + AmazonSSMManagedInstanceCore policy
aws ssm start-session --target i-1234567890abcdef0

# Once inside, diagnose:
systemctl status ssh        # Ubuntu
systemctl status sshd       # Amazon Linux
df -h                       # Check disk full
sudo sshd -t                # Test sshd config for syntax errors
sudo journalctl -u ssh -n 50  # Recent sshd logs

Use Session Manager instead of SSH (no open ports, no key pair needed):

aws ssm start-session --target i-1234567890abcdef0

# Port forwarding via SSM
aws ssm start-session \
  --target i-1234567890abcdef0 \
  --document-name AWS-StartPortForwardingSession \
  --parameters '{"portNumber":["22"],"localPortNumber":["2222"]}'

Instance Won't Start

Causes:

  • Reached instance limits
  • Insufficient capacity in AZ
  • EBS volume issue
  • Invalid AMI
# Check instance state reason
aws ec2 describe-instances \
  --instance-ids i-1234567890abcdef0 \
  --query "Reservations[].Instances[].StateReason"

Instance Unreachable

# Check instance status
aws ec2 describe-instance-status \
  --instance-ids i-1234567890abcdef0

# Get console output
aws ec2 get-console-output \
  --instance-id i-1234567890abcdef0 \
  --latest

# Get screenshot (for Windows/GUI issues)
aws ec2 get-console-screenshot \
  --instance-id i-1234567890abcdef0

High CPU/Memory

# Enable detailed monitoring
aws ec2 monitor-instances \
  --instance-ids i-1234567890abcdef0

# Check CloudWatch metrics (cross-platform date command)
START=$(date -u -v-1H +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u --date='1 hour ago' +%Y-%m-%dT%H:%M:%SZ)
aws cloudwatch get-metric-statistics \
  --namespace AWS/EC2 \
  --metric-name CPUUtilization \
  --dimensions Name=InstanceId,Value=i-1234567890abcdef0 \
  --start-time "$START" \
  --end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  --period 300 \
  --statistics Average

References

Files (aws-agent-skills)
  • evals
    • evals.json 4.3 KB
      {
        "skill_name": "ec2",
        "evals": [
          {
            "id": 1,
            "prompt": "I'm spinning up my first EC2 instance to host a web app. I need a t3.micro running Amazon Linux 2 in us-east-1a with a public IP, ports 22 and 80 open, and nginx installed automatically on launch. I don't have a key pair or security group yet. Give me the complete AWS CLI commands from scratch to get this running.",
            "expected_output": "Complete AWS CLI commands: key pair creation, security group setup, and run-instances with user data that correctly installs nginx on Amazon Linux 2 using amazon-linux-extras (not yum install nginx, which fails on AL2).",
            "files": [],
            "expectations": [
              "Provides aws ec2 create-key-pair command with --query KeyMaterial and output redirect to .pem file",
              "Includes chmod 400 on the key file",
              "Creates a security group with aws ec2 create-security-group",
              "Adds ingress rule for port 22 (SSH) with authorize-security-group-ingress",
              "Adds ingress rule for port 80 (HTTP) with authorize-security-group-ingress",
              "Provides aws ec2 run-instances with --image-id, --instance-type t3.micro, --key-name, --security-group-ids",
              "User data installs nginx using amazon-linux-extras install nginx1 (NOT yum install nginx, which fails on Amazon Linux 2 since nginx is not in the default repos)"
            ]
          },
          {
            "id": 2,
            "prompt": "I can't SSH into my EC2 instance. Here's the situation: instance ID is i-0abc1234def56789, it's running Ubuntu 22.04, has a public IP 54.89.23.17, I have the key pair file, port 22 should be open in the security group, but I keep getting 'Connection refused'. This instance was working fine two days ago. Walk me through diagnosing and fixing this.",
            "expected_output": "Structured troubleshooting that starts by distinguishing 'Connection refused' (sshd down = OS-level) from 'Connection timed out' (firewall/network), uses specific CLI commands for diagnosis, and offers Session Manager as an agentless fallback to fix sshd.",
            "files": [],
            "expectations": [
              "Distinguishes 'Connection refused' from 'Connection timed out' — explaining that 'refused' means the network path is open but sshd is not listening (OS-level issue, not a firewall issue)",
              "Provides aws ec2 describe-security-groups or describe-instances command to inspect security group rules",
              "Shows aws ec2 get-console-output command to inspect boot logs (with --latest flag preferred)",
              "Mentions Session Manager (aws ssm start-session) as a way to access the instance without SSH to fix the sshd daemon",
              "Mentions the correct username for Ubuntu is 'ubuntu' (not 'ec2-user')",
              "Addresses sshd-specific causes: sshd crashed/not running, disk full, or sshd config error — at least one of these"
            ]
          },
          {
            "id": 3,
            "prompt": "I need to set up an Auto Scaling Group for ML training jobs that can scale from 0 to 20 Spot Instances. The instances should be c5.xlarge with a fallback to c5.2xlarge for diversification. They need a launch template with IMDSv2 enforced. My VPC ID is vpc-0aabbcc, subnets are subnet-111 and subnet-222, security group is sg-abc, AMI is ami-0deadbeef, and I use instance profile ml-trainer-profile. Give me the full CLI commands to set this up.",
            "expected_output": "Launch template creation with IMDSv2, then ASG with mixed-instances policy for Spot diversification across c5.xlarge and c5.2xlarge, min=0 max=20, using both subnets.",
            "files": [],
            "expectations": [
              "Creates a launch template with aws ec2 create-launch-template including the provided AMI (ami-0deadbeef), security group (sg-abc), and IAM instance profile (ml-trainer-profile)",
              "Sets MetadataOptions with HttpTokens required (IMDSv2) in the launch template JSON",
              "Creates an Auto Scaling Group with aws autoscaling create-auto-scaling-group using --mixed-instances-policy (not the legacy request-spot-instances API)",
              "Sets min-size 0 and max-size 20 on the ASG",
              "Uses both subnet IDs (subnet-111 and subnet-222) in vpc-zone-identifier",
              "Mixed instances policy Overrides array includes both c5.xlarge and c5.2xlarge for instance type diversification",
              "Sets SpotAllocationStrategy to capacity-optimized or price-capacity-optimized in InstancesDistribution"
            ]
          }
        ]
      }
      
  • instance-management.md 9.2 KB
    # EC2 Instance Management
    
    Advanced instance lifecycle and management patterns.
    
    ## Instance Lifecycle
    
    ### States
    
    ```
    pending → running → stopping → stopped
                    ↓
               shutting-down → terminated
    ```
    
    ### Start/Stop Automation
    
    ```python
    import boto3
    
    ec2 = boto3.client('ec2')
    
    def stop_instances_by_tag(tag_key, tag_value):
        """Stop all instances with specific tag."""
        response = ec2.describe_instances(
            Filters=[
                {'Name': f'tag:{tag_key}', 'Values': [tag_value]},
                {'Name': 'instance-state-name', 'Values': ['running']}
            ]
        )
    
        instance_ids = []
        for reservation in response['Reservations']:
            for instance in reservation['Instances']:
                instance_ids.append(instance['InstanceId'])
    
        if instance_ids:
            ec2.stop_instances(InstanceIds=instance_ids)
            print(f"Stopped: {instance_ids}")
    
    # Stop all dev instances
    stop_instances_by_tag('Environment', 'dev')
    ```
    
    ### Scheduled Start/Stop with EventBridge
    
    ```bash
    # Create Lambda function for start/stop
    # Then create EventBridge rules
    
    # Stop at 7 PM
    aws events put-rule \
      --name "stop-dev-instances" \
      --schedule-expression "cron(0 19 ? * MON-FRI *)"
    
    aws events put-targets \
      --rule "stop-dev-instances" \
      --targets "Id"="1","Arn"="arn:aws:lambda:us-east-1:123456789012:function:StopInstances"
    
    # Start at 7 AM
    aws events put-rule \
      --name "start-dev-instances" \
      --schedule-expression "cron(0 7 ? * MON-FRI *)"
    
    aws events put-targets \
      --rule "start-dev-instances" \
      --targets "Id"="1","Arn"="arn:aws:lambda:us-east-1:123456789012:function:StartInstances"
    ```
    
    ## Auto Scaling
    
    ### Create Launch Template
    
    ```bash
    aws ec2 create-launch-template \
      --launch-template-name web-server-template \
      --version-description "v1" \
      --launch-template-data '{
        "ImageId": "ami-0123456789abcdef0",
        "InstanceType": "t3.micro",
        "KeyName": "my-key",
        "SecurityGroupIds": ["sg-12345678"],
        "IamInstanceProfile": {"Name": "web-server-profile"},
        "UserData": "IyEvYmluL2Jhc2gKeXVtIHVwZGF0ZSAteQo=",
        "TagSpecifications": [{
          "ResourceType": "instance",
          "Tags": [{"Key": "Name", "Value": "web-server"}]
        }],
        "MetadataOptions": {
          "HttpTokens": "required",
          "HttpEndpoint": "enabled"
        }
      }'
    ```
    
    ### Create Auto Scaling Group
    
    ```bash
    aws autoscaling create-auto-scaling-group \
      --auto-scaling-group-name web-asg \
      --launch-template LaunchTemplateName=web-server-template,Version='$Latest' \
      --min-size 2 \
      --max-size 10 \
      --desired-capacity 2 \
      --vpc-zone-identifier "subnet-12345678,subnet-87654321" \
      --target-group-arns arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/web-tg/1234567890123456 \
      --health-check-type ELB \
      --health-check-grace-period 300 \
      --tags "Key=Environment,Value=production,PropagateAtLaunch=true"
    ```
    
    ### Scaling Policies
    
    ```bash
    # Target tracking (CPU)
    aws autoscaling put-scaling-policy \
      --auto-scaling-group-name web-asg \
      --policy-name cpu-target-tracking \
      --policy-type TargetTrackingScaling \
      --target-tracking-configuration '{
        "PredefinedMetricSpecification": {
          "PredefinedMetricType": "ASGAverageCPUUtilization"
        },
        "TargetValue": 70.0,
        "ScaleOutCooldown": 300,
        "ScaleInCooldown": 300
      }'
    
    # Step scaling
    aws autoscaling put-scaling-policy \
      --auto-scaling-group-name web-asg \
      --policy-name scale-out-policy \
      --policy-type StepScaling \
      --adjustment-type ChangeInCapacity \
      --step-adjustments '[
        {"MetricIntervalLowerBound": 0, "MetricIntervalUpperBound": 20, "ScalingAdjustment": 1},
        {"MetricIntervalLowerBound": 20, "ScalingAdjustment": 2}
      ]'
    ```
    
    ### Scheduled Scaling
    
    ```bash
    # Scale up for peak hours
    aws autoscaling put-scheduled-update-group-action \
      --auto-scaling-group-name web-asg \
      --scheduled-action-name scale-up-morning \
      --recurrence "0 8 * * MON-FRI" \
      --min-size 5 \
      --max-size 20 \
      --desired-capacity 10
    
    # Scale down at night
    aws autoscaling put-scheduled-update-group-action \
      --auto-scaling-group-name web-asg \
      --scheduled-action-name scale-down-night \
      --recurrence "0 20 * * *" \
      --min-size 2 \
      --max-size 5 \
      --desired-capacity 2
    ```
    
    ## Instance Connect and Session Manager
    
    ### EC2 Instance Connect
    
    ```bash
    # Push SSH key temporarily
    aws ec2-instance-connect send-ssh-public-key \
      --instance-id i-1234567890abcdef0 \
      --instance-os-user ec2-user \
      --ssh-public-key file://~/.ssh/id_rsa.pub
    
    # Connect via browser or CLI
    aws ec2-instance-connect ssh --instance-id i-1234567890abcdef0
    ```
    
    ### Session Manager
    
    No SSH keys or open ports required:
    
    ```bash
    # Install Session Manager plugin first
    # https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html
    
    # Start session
    aws ssm start-session --target i-1234567890abcdef0
    
    # Port forwarding
    aws ssm start-session \
      --target i-1234567890abcdef0 \
      --document-name AWS-StartPortForwardingSession \
      --parameters '{"portNumber":["3306"],"localPortNumber":["3306"]}'
    ```
    
    ### Enable Session Manager
    
    ```bash
    # Instance needs SSM agent (pre-installed on Amazon Linux 2, Windows)
    # Instance needs IAM role with AmazonSSMManagedInstanceCore policy
    
    # Verify SSM agent is running
    aws ssm describe-instance-information \
      --filters "Key=InstanceIds,Values=i-1234567890abcdef0"
    ```
    
    ## Instance Metadata Service (IMDS)
    
    ### IMDSv2 (Recommended)
    
    ```bash
    # Get token
    TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" \
      -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
    
    # Use token to get metadata
    curl -H "X-aws-ec2-metadata-token: $TOKEN" \
      http://169.254.169.254/latest/meta-data/instance-id
    
    curl -H "X-aws-ec2-metadata-token: $TOKEN" \
      http://169.254.169.254/latest/meta-data/iam/security-credentials/my-role
    ```
    
    ### Enforce IMDSv2
    
    ```bash
    # New instances
    aws ec2 run-instances \
      --metadata-options "HttpTokens=required,HttpEndpoint=enabled" \
      ...
    
    # Existing instances
    aws ec2 modify-instance-metadata-options \
      --instance-id i-1234567890abcdef0 \
      --http-tokens required \
      --http-endpoint enabled
    ```
    
    ## Placement Groups
    
    ### Cluster (Low Latency)
    
    ```bash
    aws ec2 create-placement-group \
      --group-name hpc-cluster \
      --strategy cluster
    
    aws ec2 run-instances \
      --placement "GroupName=hpc-cluster" \
      ...
    ```
    
    ### Spread (High Availability)
    
    ```bash
    aws ec2 create-placement-group \
      --group-name ha-spread \
      --strategy spread
    
    # Max 7 instances per AZ
    aws ec2 run-instances \
      --placement "GroupName=ha-spread" \
      ...
    ```
    
    ### Partition (Large Distributed)
    
    ```bash
    aws ec2 create-placement-group \
      --group-name hadoop-cluster \
      --strategy partition \
      --partition-count 7
    
    aws ec2 run-instances \
      --placement "GroupName=hadoop-cluster,PartitionNumber=1" \
      ...
    ```
    
    ## Spot Instances
    
    ### Spot Fleet
    
    ```bash
    aws ec2 request-spot-fleet \
      --spot-fleet-request-config '{
        "IamFleetRole": "arn:aws:iam::123456789012:role/spot-fleet-role",
        "TargetCapacity": 10,
        "SpotPrice": "0.10",
        "AllocationStrategy": "diversified",
        "LaunchSpecifications": [
          {
            "ImageId": "ami-0123456789abcdef0",
            "InstanceType": "c5.large",
            "SubnetId": "subnet-12345678",
            "SecurityGroups": [{"GroupId": "sg-12345678"}]
          },
          {
            "ImageId": "ami-0123456789abcdef0",
            "InstanceType": "c5.xlarge",
            "SubnetId": "subnet-12345678",
            "SecurityGroups": [{"GroupId": "sg-12345678"}]
          }
        ]
      }'
    ```
    
    ### Handle Spot Interruption
    
    ```python
    import requests
    import time
    
    def check_spot_interruption():
        """Check for spot interruption notice (2-minute warning)."""
        try:
            # IMDSv2
            token = requests.put(
                'http://169.254.169.254/latest/api/token',
                headers={'X-aws-ec2-metadata-token-ttl-seconds': '21600'},
                timeout=1
            ).text
    
            response = requests.get(
                'http://169.254.169.254/latest/meta-data/spot/termination-time',
                headers={'X-aws-ec2-metadata-token': token},
                timeout=1
            )
    
            if response.status_code == 200:
                return response.text  # Termination time
            return None
        except:
            return None
    
    # Check periodically
    while True:
        termination_time = check_spot_interruption()
        if termination_time:
            print(f"Spot interruption! Terminating at {termination_time}")
            # Graceful shutdown, save state, deregister from LB
            graceful_shutdown()
            break
        time.sleep(5)
    ```
    
    ## Instance Tags
    
    ### Bulk Tagging
    
    ```bash
    # Tag multiple resources
    aws ec2 create-tags \
      --resources i-1234567890abcdef0 vol-12345678 \
      --tags Key=Project,Value=WebApp Key=Environment,Value=production
    
    # Tag based on filter
    aws ec2 describe-instances \
      --filters "Name=instance-state-name,Values=running" \
      --query "Reservations[].Instances[].InstanceId" \
      --output text | xargs -n 1 aws ec2 create-tags --tags Key=Status,Value=active --resources
    ```
    
    ### Enforce Tagging
    
    Use Service Control Policies (SCPs) or IAM policies:
    
    ```json
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Deny",
          "Action": "ec2:RunInstances",
          "Resource": "arn:aws:ec2:*:*:instance/*",
          "Condition": {
            "Null": {
              "aws:RequestTag/Environment": "true"
            }
          }
        }
      ]
    }
    ```
    
  • SKILL.md 15.4 KB
    ---
    name: ec2
    description: >
      AWS EC2 virtual machine management — instances, security groups, key pairs, AMIs, EBS volumes,
      Auto Scaling Groups, Spot Instances, Session Manager, placement groups, and instance lifecycle automation.
    
      Trigger on ANY of these, even when EC2 isn't named explicitly:
      - Launching or provisioning: "spin up a server", "create a VM", "new instance", "run-instances", mention of instance types (t3, m5, c5, r6, g5, p4d, t4g, c7g, etc.)
      - SSH / connectivity problems: "connection refused", "connection timed out", "permission denied publickey", "can't connect to my instance", "SSH not working"
      - Instance management: resize, stop, start, terminate, reboot, change instance type
      - Cost optimization: stop dev instances overnight, save money on EC2, spot vs on-demand, reserved instances
      - Auto Scaling: ASG, launch template, mixed instances policy, scale to zero, scheduled scaling
      - Spot Instances: spot fleet, spot interruption, capacity-optimized, price-capacity-optimized
      - AMIs and backups: create image, custom AMI, EBS snapshot, DLM lifecycle policy, copy AMI
      - Monitoring: EC2 CPU utilization, CloudWatch metrics for instance, instance status checks, console output
      - Access methods: Session Manager, EC2 Instance Connect, bastion host, port forwarding
      - Security: IMDSv2, instance metadata, IAM role on instance, security group rules
      - User data and bootstrap scripts, cloud-init
    last_updated: "2026-05-12"
    doc_source: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/
    ---
    
    # AWS EC2
    
    Amazon Elastic Compute Cloud (EC2) provides resizable compute capacity in the cloud.
    
    **Advanced patterns** (Auto Scaling, Spot Fleets, Session Manager, Instance Connect, IMDS, Placement Groups, scheduled scaling): see [instance-management.md](instance-management.md).
    
    ## Table of Contents
    
    - [Core Concepts](#core-concepts)
    - [Common Patterns](#common-patterns)
    - [CLI Reference](#cli-reference)
    - [Best Practices](#best-practices)
    - [Troubleshooting](#troubleshooting)
    - [References](#references)
    
    ## Core Concepts
    
    ### Instance Types
    
    | Category | Example | Use Case |
    |----------|---------|----------|
    | General Purpose | t3, m6i, t4g (Graviton) | Web servers, dev environments |
    | Compute Optimized | c6i, c7g (Graviton) | Batch processing, gaming |
    | Memory Optimized | r6i, r7g (Graviton) | Databases, caching |
    | Storage Optimized | i3, d3 | Data warehousing |
    | Accelerated | p4d, g5 | ML, graphics |
    
    Graviton (ARM) instances (t4g, m7g, c7g, r7g) are ~20% cheaper than x86 equivalents for the same performance — worth considering for new workloads.
    
    ### Purchasing Options
    
    | Option | Description |
    |--------|-------------|
    | On-Demand | Pay by the hour/second |
    | Reserved | 1-3 year commitment, up to 72% discount |
    | Spot | Unused capacity, up to 90% discount — can be interrupted with 2-minute notice |
    | Savings Plans | Flexible commitment-based discount |
    
    ### AMI (Amazon Machine Image)
    
    Template containing OS, software, and configuration for launching instances. Use SSM Parameter Store to look up the latest official AMIs rather than hardcoding IDs:
    
    ```bash
    # Latest Amazon Linux 2 AMI
    aws ssm get-parameter \
      --name /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 \
      --query 'Parameter.Value' --output text
    
    # Latest Amazon Linux 2023
    aws ssm get-parameter \
      --name /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
      --query 'Parameter.Value' --output text
    
    # Latest Ubuntu 22.04
    aws ssm get-parameter \
      --name /aws/service/canonical/ubuntu/server/22.04/stable/current/amd64/hvm/ebs-gp2/ami-id \
      --query 'Parameter.Value' --output text
    ```
    
    ### Security Groups
    
    Virtual firewalls controlling inbound and outbound traffic. Changes take effect immediately — no restart required.
    
    ## Common Patterns
    
    ### Launch an Instance
    
    ```bash
    # Create key pair
    aws ec2 create-key-pair \
      --key-name my-key \
      --query 'KeyMaterial' \
      --output text > my-key.pem
    chmod 400 my-key.pem
    
    # Create security group
    aws ec2 create-security-group \
      --group-name web-server-sg \
      --description "Web server security group" \
      --vpc-id vpc-12345678
    
    # Allow SSH and HTTP
    aws ec2 authorize-security-group-ingress \
      --group-id sg-12345678 \
      --protocol tcp \
      --port 22 \
      --cidr 10.0.0.0/8
    
    aws ec2 authorize-security-group-ingress \
      --group-id sg-12345678 \
      --protocol tcp \
      --port 80 \
      --cidr 0.0.0.0/0
    
    # Launch instance
    aws ec2 run-instances \
      --image-id ami-0123456789abcdef0 \
      --instance-type t3.micro \
      --key-name my-key \
      --security-group-ids sg-12345678 \
      --subnet-id subnet-12345678 \
      --associate-public-ip-address \
      --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=web-server}]'
    
    # Wait until running, then get IP
    aws ec2 wait instance-running --instance-ids i-1234567890abcdef0
    aws ec2 describe-instances \
      --instance-ids i-1234567890abcdef0 \
      --query 'Reservations[].Instances[].PublicIpAddress' --output text
    ```
    
    **boto3:**
    
    ```python
    import boto3
    
    ec2 = boto3.resource('ec2')
    
    instances = ec2.create_instances(
        ImageId='ami-0123456789abcdef0',
        InstanceType='t3.micro',
        KeyName='my-key',
        SecurityGroupIds=['sg-12345678'],
        SubnetId='subnet-12345678',
        MinCount=1,
        MaxCount=1,
        TagSpecifications=[{
            'ResourceType': 'instance',
            'Tags': [{'Key': 'Name', 'Value': 'web-server'}]
        }]
    )
    
    instance = instances[0]
    instance.wait_until_running()
    instance.reload()
    print(f"Instance ID: {instance.id}")
    print(f"Public IP: {instance.public_ip_address}")
    ```
    
    ### User Data Script
    
    > **OS package manager note:**
    > - **Amazon Linux 2**: use `amazon-linux-extras install nginx1 -y` — `yum install nginx` fails because nginx is not in the default AL2 repos
    > - **Amazon Linux 2023**: use `dnf install -y nginx`
    > - **Ubuntu**: use `apt-get install -y nginx`
    > - **Amazon Linux 2 / RHEL**: `httpd` (Apache) is always available via `yum install -y httpd`
    
    ```bash
    # Amazon Linux 2 — nginx via amazon-linux-extras
    aws ec2 run-instances \
      --image-id ami-0123456789abcdef0 \
      --instance-type t3.micro \
      --key-name my-key \
      --security-group-ids sg-12345678 \
      --subnet-id subnet-12345678 \
      --user-data '#!/bin/bash
    amazon-linux-extras install nginx1 -y
    systemctl start nginx
    systemctl enable nginx
    '
    
    # Amazon Linux 2 — httpd (Apache, simpler alternative)
    # --user-data '#!/bin/bash
    # yum install -y httpd
    # systemctl start httpd
    # systemctl enable httpd
    # echo "<h1>Hello from $(hostname -f)</h1>" > /var/www/html/index.html
    # '
    ```
    
    ### Attach IAM Role
    
    ```bash
    # Create instance profile
    aws iam create-instance-profile \
      --instance-profile-name web-server-profile
    
    aws iam add-role-to-instance-profile \
      --instance-profile-name web-server-profile \
      --role-name web-server-role
    
    # Launch with profile
    aws ec2 run-instances \
      --image-id ami-0123456789abcdef0 \
      --instance-type t3.micro \
      --iam-instance-profile Name=web-server-profile \
      ...
    ```
    
    ### Create AMI from Instance
    
    ```bash
    aws ec2 create-image \
      --instance-id i-1234567890abcdef0 \
      --name "my-custom-ami-$(date +%Y%m%d)" \
      --description "Custom AMI with web server" \
      --no-reboot
    ```
    
    ### Auto Scaling Group with Spot (Modern Approach)
    
    The recommended way to use Spot Instances at scale is via Auto Scaling Groups with a mixed-instances policy — not the legacy `request-spot-instances` API. This supports instance diversification to minimize interruptions.
    
    See [instance-management.md](instance-management.md) for the full setup. Quick example:
    
    ```bash
    # 1. Create launch template with IMDSv2
    aws ec2 create-launch-template \
      --launch-template-name my-lt \
      --launch-template-data '{
        "ImageId": "ami-0123456789abcdef0",
        "SecurityGroupIds": ["sg-12345678"],
        "IamInstanceProfile": {"Name": "my-profile"},
        "MetadataOptions": {"HttpTokens": "required", "HttpEndpoint": "enabled"}
      }'
    
    # 2. Create ASG with mixed-instances (Spot + On-Demand diversification)
    aws autoscaling create-auto-scaling-group \
      --auto-scaling-group-name my-asg \
      --min-size 0 --max-size 20 --desired-capacity 2 \
      --vpc-zone-identifier "subnet-111,subnet-222" \
      --mixed-instances-policy '{
        "LaunchTemplate": {
          "LaunchTemplateSpecification": {"LaunchTemplateName": "my-lt", "Version": "$Latest"},
          "Overrides": [
            {"InstanceType": "c5.xlarge"},
            {"InstanceType": "c5.2xlarge"},
            {"InstanceType": "c5a.xlarge"}
          ]
        },
        "InstancesDistribution": {
          "OnDemandBaseCapacity": 0,
          "OnDemandPercentageAboveBaseCapacity": 0,
          "SpotAllocationStrategy": "capacity-optimized"
        }
      }'
    ```
    
    ### EBS Volume Management
    
    ```bash
    # Create volume
    aws ec2 create-volume \
      --availability-zone us-east-1a \
      --size 100 \
      --volume-type gp3 \
      --iops 3000 \
      --throughput 125 \
      --encrypted
    
    # Attach to instance
    aws ec2 attach-volume \
      --volume-id vol-12345678 \
      --instance-id i-1234567890abcdef0 \
      --device /dev/sdf
    
    # Create snapshot
    aws ec2 create-snapshot \
      --volume-id vol-12345678 \
      --description "Daily backup"
    ```
    
    ## CLI Reference
    
    ### Instance Management
    
    | Command | Description |
    |---------|-------------|
    | `aws ec2 run-instances` | Launch instances |
    | `aws ec2 describe-instances` | List instances |
    | `aws ec2 start-instances` | Start stopped instances |
    | `aws ec2 stop-instances` | Stop running instances |
    | `aws ec2 reboot-instances` | Reboot instances |
    | `aws ec2 terminate-instances` | Terminate instances |
    | `aws ec2 modify-instance-attribute` | Modify instance settings |
    
    ### Security Groups
    
    | Command | Description |
    |---------|-------------|
    | `aws ec2 create-security-group` | Create security group |
    | `aws ec2 describe-security-groups` | List security groups |
    | `aws ec2 authorize-security-group-ingress` | Add inbound rule |
    | `aws ec2 revoke-security-group-ingress` | Remove inbound rule |
    | `aws ec2 authorize-security-group-egress` | Add outbound rule |
    
    ### AMIs
    
    | Command | Description |
    |---------|-------------|
    | `aws ec2 describe-images` | List AMIs |
    | `aws ec2 create-image` | Create AMI from instance |
    | `aws ec2 copy-image` | Copy AMI to another region |
    | `aws ec2 deregister-image` | Delete AMI |
    
    ### EBS Volumes
    
    | Command | Description |
    |---------|-------------|
    | `aws ec2 create-volume` | Create EBS volume |
    | `aws ec2 attach-volume` | Attach to instance |
    | `aws ec2 detach-volume` | Detach from instance |
    | `aws ec2 create-snapshot` | Create snapshot |
    | `aws ec2 modify-volume` | Resize/modify volume |
    
    ## Best Practices
    
    ### Security
    
    - **Use IAM roles** instead of access keys on instances
    - **Restrict security groups** — principle of least privilege
    - **Use private subnets** for backend instances
    - **Enable IMDSv2** to prevent SSRF attacks
    - **Encrypt EBS volumes** at rest
    
    ```bash
    # Require IMDSv2 on existing instance
    aws ec2 modify-instance-metadata-options \
      --instance-id i-1234567890abcdef0 \
      --http-tokens required \
      --http-endpoint enabled
    ```
    
    ### Performance
    
    - **Right-size instances** — monitor and adjust
    - **Use EBS-optimized instances**
    - **Choose appropriate EBS volume type** (gp3 is the default good choice; io2 for high IOPS)
    - **Use placement groups** for low-latency networking (see instance-management.md)
    
    ### Cost Optimization
    
    - **Use Spot Instances** for fault-tolerant workloads (batch, ML training, CI)
    - **Stop/terminate unused instances**
    - **Use Reserved Instances or Savings Plans** for steady-state workloads
    - **Delete unused EBS volumes and snapshots**
    - **Consider Graviton (t4g, m7g, c7g)** — ~20% cheaper for same performance
    
    ### Reliability
    
    - **Use Auto Scaling Groups** for high availability (see instance-management.md)
    - **Deploy across multiple AZs**
    - **Use Elastic Load Balancer** for traffic distribution
    - **Implement health checks**
    
    ## Troubleshooting
    
    ### Cannot SSH to Instance
    
    **First: identify the error type — it points to different root causes:**
    
    | Error | What it means | Primary suspects |
    |-------|--------------|-----------------|
    | `Connection refused` | Network is reachable, but SSH daemon is not listening | sshd crashed, sshd not installed, OS firewall (ufw/iptables) blocking, wrong port |
    | `Connection timed out` | Packets never arrive | Security group blocks port 22, NACL blocks traffic, no public IP, wrong IP |
    | `Permission denied` | Connected, but auth failed | Wrong key file, wrong username, key not authorized |
    
    **Common username by OS:**
    
    | OS | Default SSH user |
    |----|-----------------|
    | Amazon Linux 2 / 2023 | `ec2-user` |
    | Ubuntu | `ubuntu` |
    | Debian | `admin` |
    | CentOS / RHEL | `ec2-user` or `centos` |
    | Windows | `Administrator` |
    
    **Diagnostic commands:**
    
    ```bash
    # 1. Check instance state and public IP
    aws ec2 describe-instances \
      --instance-ids i-1234567890abcdef0 \
      --query "Reservations[].Instances[].{State:State.Name,PublicIP:PublicIpAddress,StatusChecks:State.Name}"
    
    # 2. Check instance status (system + instance checks)
    aws ec2 describe-instance-status --instance-ids i-1234567890abcdef0
    
    # 3. Check security group rules for port 22
    aws ec2 describe-security-groups \
      --group-ids sg-12345678 \
      --query "SecurityGroups[].IpPermissions[?ToPort==\`22\`]"
    
    # 4. Get console output to see boot logs, sshd errors, OOM events
    aws ec2 get-console-output \
      --instance-id i-1234567890abcdef0 \
      --latest \
      --query Output --output text
    ```
    
    **If connection refused — get inside via Session Manager to fix sshd:**
    
    ```bash
    # Requires SSM agent on instance + AmazonSSMManagedInstanceCore policy
    aws ssm start-session --target i-1234567890abcdef0
    
    # Once inside, diagnose:
    systemctl status ssh        # Ubuntu
    systemctl status sshd       # Amazon Linux
    df -h                       # Check disk full
    sudo sshd -t                # Test sshd config for syntax errors
    sudo journalctl -u ssh -n 50  # Recent sshd logs
    ```
    
    **Use Session Manager instead of SSH** (no open ports, no key pair needed):
    
    ```bash
    aws ssm start-session --target i-1234567890abcdef0
    
    # Port forwarding via SSM
    aws ssm start-session \
      --target i-1234567890abcdef0 \
      --document-name AWS-StartPortForwardingSession \
      --parameters '{"portNumber":["22"],"localPortNumber":["2222"]}'
    ```
    
    ### Instance Won't Start
    
    **Causes:**
    - Reached instance limits
    - Insufficient capacity in AZ
    - EBS volume issue
    - Invalid AMI
    
    ```bash
    # Check instance state reason
    aws ec2 describe-instances \
      --instance-ids i-1234567890abcdef0 \
      --query "Reservations[].Instances[].StateReason"
    ```
    
    ### Instance Unreachable
    
    ```bash
    # Check instance status
    aws ec2 describe-instance-status \
      --instance-ids i-1234567890abcdef0
    
    # Get console output
    aws ec2 get-console-output \
      --instance-id i-1234567890abcdef0 \
      --latest
    
    # Get screenshot (for Windows/GUI issues)
    aws ec2 get-console-screenshot \
      --instance-id i-1234567890abcdef0
    ```
    
    ### High CPU/Memory
    
    ```bash
    # Enable detailed monitoring
    aws ec2 monitor-instances \
      --instance-ids i-1234567890abcdef0
    
    # Check CloudWatch metrics (cross-platform date command)
    START=$(date -u -v-1H +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u --date='1 hour ago' +%Y-%m-%dT%H:%M:%SZ)
    aws cloudwatch get-metric-statistics \
      --namespace AWS/EC2 \
      --metric-name CPUUtilization \
      --dimensions Name=InstanceId,Value=i-1234567890abcdef0 \
      --start-time "$START" \
      --end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
      --period 300 \
      --statistics Average
    ```
    
    ## References
    
    - [EC2 User Guide](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)
    - [EC2 API Reference](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/)
    - [EC2 CLI Reference](https://docs.aws.amazon.com/cli/latest/reference/ec2/)
    - [boto3 EC2](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html)
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related