> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fjall.io/llms.txt
> Use this file to discover all available pages before exploring further.

# EC2 Instance

> Provision an AWS EC2 instance with an Auto Scaling Group, security groups, and optional SSH access using Fjall.

## Overview

The EC2 Instance resource deploys a managed EC2 instance backed by an Auto Scaling Group, with security groups and optional SSH access. Use it for workloads that need direct server access or custom configuration.

IMDSv2 is required by default, the root EBS volume is encrypted by default, and `AZRebalance` is suspended to keep instances pinned to their subnet.

## Resource Class

```typescript theme={null}
import { Ec2Instance } from "@fjall/components-infrastructure/lib/resources/aws/compute/ec2";
```

## Basic Usage

```typescript theme={null}
const instance = new Ec2Instance(this, "MyInstance", {
  serviceName: "my-service",
  instanceType: "t4g.medium",
  vpc: myVpc,
  minCapacity: 1,
  maxCapacity: 3,
});
```

## Configuration Options

### Core Properties

| Property       | Type     | Description        | Default         |
| -------------- | -------- | ------------------ | --------------- |
| `serviceName`  | `string` | Service identifier | Required        |
| `instanceType` | `string` | EC2 instance type  | Required        |
| `vpc`          | `IVpc`   | VPC for deployment | Auto-created    |
| `accountId`    | `string` | AWS account ID     | Current account |

### Capacity Configuration

| Property                 | Type     | Description                                    | Default       |
| ------------------------ | -------- | ---------------------------------------------- | ------------- |
| `minCapacity`            | `number` | Minimum instances (passed to the CDK ASG)      | CDK default 1 |
| `maxCapacity`            | `number` | Maximum instances (passed to the CDK ASG)      | CDK default 1 |
| `desiredCapacity`        | `number` | Initial instance count (passed to the CDK ASG) | CDK default   |
| `spotCapacityPercentage` | `number` | Percentage of spot instances                   | `0`           |

`minCapacity` and `maxCapacity` forward straight to the CDK Auto Scaling Group without a Fjall-supplied fallback. When both are omitted, CDK's default of `1` applies to each.

### Instance Configuration

| Property       | Type            | Description              | Default                  |
| -------------- | --------------- | ------------------------ | ------------------------ |
| `machineImage` | `IMachineImage` | AMI to use               | Latest Amazon Linux 2023 |
| `userData`     | `UserData`      | User data script         | -                        |
| `role`         | `Role`          | IAM role for instance    | -                        |
| `blockDevices` | `BlockDevice[]` | EBS volume configuration | -                        |
| `enableSSH`    | `boolean`       | Enable SSH access        | `false`                  |

### Network Configuration

| Property              | Type                    | Description                 | Default                 |
| --------------------- | ----------------------- | --------------------------- | ----------------------- |
| `subnetConfiguration` | `SubnetConfiguration[]` | Custom subnet configuration | Standard public/private |

## Machine Images

When `machineImage` is omitted, the construct uses `MachineImage.latestAmazonLinux2023()`.

### Amazon Linux 2023 (default)

```typescript theme={null}
const instance = new Ec2Instance(this, "AL2023Instance", {
  serviceName: "al2023-service",
  instanceType: "t4g.micro",
  machineImage: MachineImage.latestAmazonLinux2023(),
});
```

### Ubuntu

```typescript theme={null}
const instance = new Ec2Instance(this, "UbuntuInstance", {
  serviceName: "ubuntu-service",
  instanceType: "t4g.small",
  machineImage: MachineImage.fromSsmParameter(
    "/aws/service/canonical/ubuntu/server/20.04/stable/current/amd64/hvm/ebs-gp2/ami-id",
  ),
});
```

### Custom AMI

```typescript theme={null}
const instance = new Ec2Instance(this, "CustomInstance", {
  serviceName: "custom-service",
  instanceType: "m5.large",
  machineImage: MachineImage.lookup({
    name: "my-custom-ami-*",
    owners: ["self"],
  }),
});
```

## User Data Configuration

### Basic Script

```typescript theme={null}
const userData = UserData.forLinux();
userData.addCommands(
  "yum update -y",
  "yum install -y httpd",
  "systemctl start httpd",
  "systemctl enable httpd",
);

const instance = new Ec2Instance(this, "WebServer", {
  serviceName: "web",
  instanceType: "t4g.micro",
  userData: userData,
});
```

### Complex Setup

```typescript theme={null}
const userData = UserData.forLinux();
userData.addCommands(
  // Install dependencies
  "yum update -y",
  "yum install -y docker",

  // Start Docker
  "systemctl start docker",
  "usermod -a -G docker ec2-user",

  // Pull and run container
  "docker pull nginx:latest",
  "docker run -d -p 80:80 nginx",
);
```

## Storage Configuration

### Additional EBS Volumes

```typescript theme={null}
const instance = new Ec2Instance(this, "StorageInstance", {
  serviceName: "storage",
  instanceType: "m5.large",
  blockDevices: [
    {
      deviceName: "/dev/sdf",
      volume: BlockDeviceVolume.ebs(100, {
        volumeType: EbsDeviceVolumeType.GP3,
        encrypted: true,
        deleteOnTermination: false,
      }),
    },
  ],
});
```

### RAID Configuration

```typescript theme={null}
const userData = UserData.forLinux();
userData.addCommands(
  // Create RAID 0 array
  "mdadm --create /dev/md0 --level=0 --raid-devices=2 /dev/xvdf /dev/xvdg",
  "mkfs.ext4 /dev/md0",
  "mkdir /data",
  "mount /dev/md0 /data",
);
```

## Security Configuration

### SSH Access

```typescript theme={null}
const instance = new Ec2Instance(this, "SSHInstance", {
  serviceName: "ssh-enabled",
  instanceType: "t4g.micro",
  enableSSH: true, // Creates key pair and opens port 22
});
```

### Custom Security Rules

```typescript theme={null}
const instance = new Ec2Instance(this, "SecureInstance", {
  serviceName: "secure",
  instanceType: "t4g.small",
});

// Add custom ingress rules
instance.asgSecurityGroup.addIngressRule(
  Peer.ipv4("10.0.0.0/8"),
  Port.tcp(443),
  "Allow HTTPS from internal",
);

// Connect to other resources
instance.connections.allowTo(database, Port.tcp(5432), "Allow database access");
```

## IAM Role Configuration

The `role` prop accepts a standard CDK IAM `Role` from `aws-cdk-lib/aws-iam`.

```typescript theme={null}
const role = new Role(this, "InstanceRole", {
  assumedBy: new ServicePrincipal("ec2.amazonaws.com"),
  managedPolicies: [
    ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMManagedInstanceCore"),
  ],
  inlinePolicies: {
    S3Access: new PolicyDocument({
      statements: [
        new PolicyStatement({
          actions: ["s3:GetObject", "s3:PutObject"],
          resources: ["arn:aws:s3:::my-bucket/*"],
        }),
      ],
    }),
  },
});

const instance = new Ec2Instance(this, "RoleInstance", {
  serviceName: "role-service",
  instanceType: "t4g.medium",
  role: role,
});
```

## Auto Scaling Configuration

### Scaling Across AZs

```typescript theme={null}
const instance = new Ec2Instance(this, "ScaledInstance", {
  serviceName: "scaled",
  instanceType: "t4g.large",
  minCapacity: 2,
  maxCapacity: 10,
  vpc: vpc, // Ensure VPC has subnets in multiple AZs
});
```

### Spot Instances

```typescript theme={null}
const instance = new Ec2Instance(this, "SpotInstance", {
  serviceName: "spot",
  instanceType: "c5.large",
  minCapacity: 3,
  maxCapacity: 10,
  spotCapacityPercentage: 70, // 70% spot, 30% on-demand
});
```

## Methods

### Get Security Group

```typescript theme={null}
const securityGroup = instance.asgSecurityGroup;
// Returns: SecurityGroup
```

### Get Connections

```typescript theme={null}
const connections = instance.connections;
// Use for network access control
connections.allowTo(targetResource, Port.tcp(443));
```

### Get VPC

```typescript theme={null}
const vpc = instance.vpc;
// Returns: IVpc
```

### Get Auto Scaling Group

The underlying ASG is private. Read it through `getAutoScalingGroup()`, for example to register it as a load balancer target.

```typescript theme={null}
const asg = instance.getAutoScalingGroup();
// Returns: AutoScalingGroup
```

## Advanced Patterns

### Web Server Fleet

```typescript theme={null}
const webFleet = new Ec2Instance(this, "WebFleet", {
  serviceName: "web-fleet",
  instanceType: "t4g.small",
  minCapacity: 2,
  maxCapacity: 10,
  userData: UserData.forLinux({
    shebang: "#!/bin/bash -xe",
  }),
});

// Add load balancer
const alb = new ApplicationLoadBalancer(this, "WebALB", {
  vpc: webFleet.vpc,
  internetFacing: true,
});

// Configure target group
const targetGroup = new ApplicationTargetGroup(this, "WebTargets", {
  vpc: webFleet.vpc,
  port: 80,
  targets: [webFleet.getAutoScalingGroup()],
});
```

### Bastion Host

```typescript theme={null}
const bastion = new Ec2Instance(this, "Bastion", {
  serviceName: "bastion",
  instanceType: "t4g.nano",
  enableSSH: true,
  minCapacity: 1,
  maxCapacity: 1,
});

// Restrict SSH access
bastion.asgSecurityGroup.addIngressRule(
  Peer.ipv4("203.0.113.0/24"), // Your office IP
  Port.tcp(22),
  "SSH from office only",
);
```

### GPU Instance

```typescript theme={null}
const gpuInstance = new Ec2Instance(this, "GPUInstance", {
  serviceName: "ml-gpu",
  instanceType: "p3.2xlarge",
  machineImage: MachineImage.fromSsmParameter(
    "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2",
  ),
  blockDevices: [
    {
      deviceName: "/dev/sda1",
      volume: BlockDeviceVolume.ebs(100, {
        volumeType: EbsDeviceVolumeType.GP3,
        iops: 3000,
      }),
    },
  ],
});
```

## Complete Example

```typescript theme={null}
import { Ec2Instance } from "@fjall/components-infrastructure/lib/resources/aws/compute/ec2";
import { UserData, MachineImage, BlockDeviceVolume } from "aws-cdk-lib/aws-ec2";
import {
  Role,
  ServicePrincipal,
  PolicyStatement,
  PolicyDocument,
} from "aws-cdk-lib/aws-iam";

// Create IAM role
const instanceRole = new Role(this, "AppRole", {
  assumedBy: new ServicePrincipal("ec2.amazonaws.com"),
  inlinePolicies: {
    AppPolicy: new PolicyDocument({
      statements: [
        new PolicyStatement({
          actions: ["s3:GetObject"],
          resources: ["arn:aws:s3:::config-bucket/*"],
        }),
      ],
    }),
  },
});

// Prepare user data
const userData = UserData.forLinux();
userData.addCommands(
  // Update system
  "yum update -y",

  // Install application dependencies
  "yum install -y nodejs npm",

  // Download application
  "aws s3 cp s3://config-bucket/app.tar.gz /opt/",
  "cd /opt && tar -xzf app.tar.gz",

  // Start application
  "npm install --production",
  "npm start",
);

// Create EC2 instance
const appInstance = new Ec2Instance(this, "AppInstance", {
  serviceName: "production-app",
  instanceType: "t4g.medium",
  minCapacity: 2,
  maxCapacity: 6,
  machineImage: MachineImage.latestAmazonLinux2023(),
  userData: userData,
  role: instanceRole,
  blockDevices: [
    {
      deviceName: "/dev/xvda",
      volume: BlockDeviceVolume.ebs(30, {
        encrypted: true,
        deleteOnTermination: true,
      }),
    },
  ],
  enableSSH: false, // Production, no direct SSH
});

// Allow database access
appInstance.connections.allowTo(
  database,
  Port.tcp(5432),
  "Application to database",
);
```

## Best Practices

1. **Use Systems Manager** instead of SSH for production
2. **Enable IMDSv2** (enabled by default in this construct)
3. **Encrypt EBS volumes** for sensitive data
4. **Use Auto Scaling** even for single instances
5. **Apply least privilege** IAM policies
6. **Monitor with CloudWatch** and set alarms
7. **Use user data** for repeatable configuration

## Cost Optimisation

* Use **Spot instances** for fault-tolerant workloads
* **Right-size instances** based on CloudWatch metrics
* Enable **detailed monitoring** only when needed
* Use **GP3 volumes** instead of GP2 for better price/performance
* Consider **Savings Plans** for predictable workloads

## Next Steps

<CardGroup cols={2}>
  <Card title="Compute Factory" icon="industry" href="/patterns/compute-factory">
    Build EC2 and ECS compute through the Fjall compute factory pattern.
  </Card>

  <Card title="Security Group" icon="shield-halved" href="/resources/networking/security-group">
    Control inbound and outbound traffic for your instances.
  </Card>

  <Card title="VPC" icon="network-wired" href="/resources/networking/vpc">
    Configure the network your EC2 instances run in.
  </Card>

  <Card title="IAM Role" icon="key" href="/resources/security/iam-role">
    Grant least-privilege AWS permissions to your instances.
  </Card>
</CardGroup>
