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

# Register an AWS EC2 Instance

> Launch and connect an AWS EC2 instance to Omni using a custom Talos AMI.

export const release = 'v1.13.0';

This guide walks through creating a Talos AMI for AWS, importing it into your AWS account, and launching EC2 instances that can be registered with Omni.

## Step 1: Set your AWS region

Define the AWS region where you want to create your Omni machines.

```bash theme={null}
REGION=<your-aws-region> # e.g. us-east-1
```

## Step 2: Identify the VPC

Your EC2 instances must run inside a VPC. Most AWS accounts have a default VPC available.

First list the VPCs in your region:

```bash theme={null}
aws ec2 describe-vpcs --region $REGION
```

Then capture the default VPC:

```bash theme={null}
VPC_ID=$(aws ec2 describe-vpcs \
  --region $REGION \
  --query "Vpcs[?IsDefault].VpcId" \
  --output text)

echo $VPC_ID
```

This command stores the VPC ID in the `VPC_ID` variable.

## Step 3: Create a subnet

Next, create a subnet within the VPC.

The subnet CIDR must fall within the VPC CIDR range. For example, if the VPC uses `172.31.0.0/16`, you can create a subnet such as `172.31.128.0/20`.

```bash theme={null}
SUBNET_ID=$(aws ec2 create-subnet \
  --region $REGION \
  --vpc-id $VPC_ID \
  --cidr-block 172.31.128.0/20 \
  --query "Subnet.SubnetId" \
  --output text)

echo $SUBNET_ID
```

The subnet ID will be used later when launching EC2 instances.

## Step 4: Create a security group

First, create a security group that will be attached to the EC2 instances.

```bash theme={null}
SECURITY_GROUP=$(aws ec2 create-security-group \
  --region $REGION \
  --group-name omni-aws-sg \
  --description "Security group for Omni EC2 instances" \
  --query "GroupId" \
  --output text)

echo $SECURITY_GROUP
```

Next, update the security group to allow all internal traffic within the same group. This allows Kubernetes applications running on different machines to communicate with each other:

```bash theme={null}
aws ec2 authorize-security-group-ingress \
  --region $REGION \
  --group-id $SECURITY_GROUP \
  --protocol all \
  --port -1 \
  --source-group $SECURITY_GROUP
```

## Step 5: Register machines to your Omni account

Talos provides an official AWS AMI that you can use directly. However, if you need to customize the AMI, for example by adding custom labels or system extensions, you must create a custom Talos AMI and bake those customizations into the image.

If you plan to create a custom Talos AMI, skip this step and continue to **Step 6**.

<Note>The official Talos AWS AMI includes the `ecr-credential-provider` system extension by default.</Note>

To register AWS EC2 instances to your Omni account using the official Talos AMI, you must add a [join config](./join-machines-to-omni) to the EC2 instance user data and boot the instance with the Talos Omni AMI. This ensures the machine automatically joins your Omni account when it starts.

To register your AWS EC2 instances:

1. Define environment variables that specify the machines you want to register.

These are example values. Adjust them to match your machine specifications.

<CodeBlock lang="sh">
  {`
    AWS_REGION=$(aws configure get region)\nTALOS_VERSION=${release}\nARCH=amd64\nINSTANCE_TYPE=t3.small\nCOUNT=1
    `}
</CodeBlock>

2. Retrieve the official Talos AWS AMI:

```bash theme={null}
AMI=$(curl -sL https://github.com/siderolabs/talos/releases/download/${TALOS_VERSION}/cloud-images.json \
  | jq -r '.[] | select(.region == "'"$AWS_REGION"'") | select(.arch == "'"$ARCH"'") | .id')

echo "Using AMI: $AMI"
```

3. Generate the join configuration. This configuration allows the Talos machines to register with Omni when they boot:

```bash theme={null}
USER_DATA=$(omnictl jointoken machine-config)
```

4. Launch the EC2 instances:

```bash theme={null}
aws ec2 run-instances \
  --region $AWS_REGION \
  --image-id $AMI \
  --instance-type $INSTANCE_TYPE \
  --count $CONTROL_PLANE_NO  \
  --iam-instance-profile Name=$AUTOSCALER_INSTANCE_PROFILE_NAME \
  --user-data "$USER_DATA" \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=role,Value=autoscaler-controlplane-machine}]'
```

## Step 6: Create a custom Talos AWS AMI

If you need to customize the Talos AMI (for example by adding custom labels or system extensions), you must build a custom image with those extensions included.

In this step, you will:

1. Download the Talos AWS disk image from Omni
2. Upload it to Amazon S3
3. Import it into EC2 as a snapshot
4. Register the snapshot as an AMI

### 6.1: Download the Talos AWS image

You can download the Talos AWS image from the CLI or the UI:

<Tabs>
  <Tab title="CLI">
    Run the following command to download the Talos AWS image:

    ```bash theme={null}
    omnictl download aws
    ```

    To see available options for adding custom labels or system extensions:

    ```bash theme={null}
    omnictl download aws --help
    ```
  </Tab>

  <Tab title="UI">
    To download the Talos AWS image from the UI:

    1. Log into your Omni account.
    2. Go to the **Overview** page.
    3. Click **Download Installation Media**.
    4. Select **AWS AMI (amd64)** or **AWS AMI (arm64)** depending on your instance architecture.
    5. Add any required system extensions or labels.
    6. Click **Download**.
  </Tab>
</Tabs>

The downloaded Talos AWS image will contain a compressed archive similar to:

```bash theme={null}
aws-amd64.tar.gz
```

Extract the archive:

```bash theme={null}
tar -xzf aws-amd64.tar.gz
```

This command will produce a disk image similar to `disk.raw`:

### 6.2: Create S3 bucket

The disk image must be uploaded to an S3 bucket before it can be imported into EC2.

Bucket names must be globally unique. The following command creates a bucket with a globally unique name using a timestamp.

```bash theme={null}
BUCKET="omni-aws-$(date +%s)"

aws s3api create-bucket \
  --bucket $BUCKET \
  --region $REGION \
  --create-bucket-configuration LocationConstraint=$REGION
```

### 6.3: Upload the disk image to S3

Upload the extracted disk image to the S3 bucket:

```
aws s3 cp disk.raw s3://$BUCKET/omni-aws.raw
```

### 6.4: Import the disk image as an EC2 snapshot

Next, import the uploaded disk image into EC2 as a snapshot.

```bash theme={null}
IMPORT_TASK=$(aws ec2 import-snapshot \
  --region $REGION \
  --description "Omni AWS" \
  --disk-container "Format=raw,UserBucket={S3Bucket=$BUCKET,S3Key=omni-aws.raw}" \
  --query "ImportTaskId" \
  --output text)

echo $IMPORT_TASK
```

### 6.5: Monitor the snapshot import

You can check the status of the import task using:

```bash theme={null}
aws ec2 describe-import-snapshot-tasks \
  --region $REGION \
  --import-task-ids $IMPORT_TASK
```

Once the import completes, retrieve the snapshot ID:

```bash theme={null}
SNAPSHOT=$(aws ec2 describe-import-snapshot-tasks \
  --region $REGION \
  --import-task-ids $IMPORT_TASK \
  --query "ImportSnapshotTasks[0].SnapshotTaskDetail.SnapshotId" \
  --output text)

echo $SNAPSHOT
```

You will use this snapshot in the next step to register the AMI.

## Step 7: Register the AMI

Create an AMI from the snapshot.

```bash theme={null}
AMI_ID=$(aws ec2 register-image \
  --region $REGION \
  --block-device-mappings "DeviceName=/dev/xvda,Ebs={DeleteOnTermination=true,SnapshotId=$SNAPSHOT,VolumeSize=14,VolumeType=gp2}" \
  --root-device-name /dev/xvda \
  --virtualization-type hvm \
  --architecture x86_64 \
  --ena-support \
  --name omni-aws-ami \
  --query "ImageId" \
  --output text)

echo $AMI_ID
```

## Step 8: Launch EC2 instances

Finally, create EC2 instances using the AMI.

```bash theme={null}
aws ec2 run-instances \
  --region $REGION \
  --image-id $AMI_ID \
  --count 1 \
  --instance-type t3.small \
  --subnet-id $SUBNET_ID \
  --security-group-ids $SECURITY_GROUP \
  --associate-public-ip-address \
  --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=omni-aws-ami}]"
```

## Cleanup

If you no longer need the resources created in this guide, remove them to avoid unnecessary AWS charges.

### Terminate the EC2 instance

First, terminate the EC2 instance created from the AMI.

You can find the instance ID with:

```bash theme={null}
INSTANCE_ID=$(aws ec2 describe-instances \
  --region $REGION \
  --filters "Name=image-id,Values=$AMI_ID" \
  --query "Reservations[*].Instances[*].InstanceId" \
  --output text)

echo $INSTANCE_ID
```

Terminate the instance:

```bash theme={null}
aws ec2 terminate-instances \
  --region $REGION \
  --instance-ids $INSTANCE_ID
```

### Deregister the AMI

Next, deregister the AMI created earlier:

```bash theme={null}
aws ec2 deregister-image \
  --region $REGION \
  --image-id $AMI_ID
```

### Delete the snapshot

After deregistering the AMI, delete the snapshot used to create it:

```bash theme={null}
aws ec2 delete-snapshot \
  --region $REGION \
  --snapshot-id $SNAPSHOT
```

### Remove the disk image from S3

Delete the uploaded disk image:

```bash theme={null}
aws s3 rm s3://$BUCKET/omni-aws.raw
```

### Delete the S3 bucket

After removing the object, delete the bucket:

```bash theme={null}
aws s3 rb s3://$BUCKET
```

### Delete the security group

Remove the security group created for the EC2 instances:

```bash theme={null}
aws ec2 delete-security-group \
  --region $REGION \
  --group-id $SECURITY_GROUP
```

### Delete the subnet

Finally, delete the subnet created earlier:

```bash theme={null}
aws ec2 delete-subnet \
  --region $REGION \
  --subnet-id $SUBNET_ID
```
