Files

6.4 KiB

Assignment

The Nautilus DevOps team is strategizing the migration of a portion of their infrastructure to the AWS cloud. Recognizing the scale of this undertaking, they have opted to approach the migration in incremental steps rather than as a single massive transition. To achieve this, they have segmented large tasks into smaller, more manageable units.

For this task, create an EC2 instance using Terraform with the following requirements:

The EC2 instance must use the value datacenter-ec2 as its Name tag, which defines the instance name in AWS.

Use the Amazon Linux ami-0c101f26f147fa7fd to launch this instance.

The Instance type must be t2.micro.

Create a new RSA key named datacenter-kp.

Attach the default (available by default) security group.

The Terraform working directory is /home/bob/terraform. Create the main.tf file (do not create a different .tf file) to provision the instance.

Note: Right-click under the EXPLORER section in VS Code and select Open in Integrated Terminal to launch the terminal.

Solution

Terraform EC2 Instance — datacenter-ec2

Launch a t2.micro instance with a newly-created RSA key pair and the default security group.

Create main.tf (heredoc → file)

cd /home/bob/terraform

cat > main.tf <<'EOF'
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.0"
    }
    tls = {
      source  = "hashicorp/tls"
      version = "~> 4.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

# --- New RSA key pair ---
resource "tls_private_key" "datacenter_kp" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

resource "aws_key_pair" "datacenter_kp" {
  key_name   = "datacenter-kp"
  public_key = tls_private_key.datacenter_kp.public_key_openssh
}

# --- Default VPC and its default security group ---
data "aws_vpc" "default" {
  default = true
}

data "aws_security_group" "default" {
  vpc_id = data.aws_vpc.default.id
  name   = "default"
}

# --- EC2 instance ---
resource "aws_instance" "datacenter_ec2" {
  ami                    = "ami-0c101f26f147fa7fd"
  instance_type          = "t2.micro"
  key_name               = aws_key_pair.datacenter_kp.key_name
  vpc_security_group_ids = [data.aws_security_group.default.id]

  credit_specification {
    cpu_credits = "standard"
  }

  tags = {
    Name = "datacenter-ec2"
  }
}
EOF

How to run

cd /home/bob/terraform
terraform init
terraform apply -auto-approve

How it works

The heredoc

cat > main.tf <<'EOF' writes the file in one shot; the quoted 'EOF' stops the shell from expanding anything in the HCL. Everything goes in main.tf as the task requires — no separate provider or key files.

The RSA key pair (two resources)

AWS never hands back private key material, so creating a usable key pair takes two resources across two providers:

  1. tls_private_key — generates the key pair locally. algorithm = "RSA" is what makes the resulting AWS key pair type rsa; rsa_bits = 4096 sets its strength.
  2. aws_key_pair — uploads only the public half (tls_private_key.datacenter_kp.public_key_openssh) under the name datacenter-kp. AWS stores just the public key; because it's RSA, the key pair registers with KeyType: rsa.

Referencing the TLS resource's attribute creates an implicit dependency, so Terraform generates the key before trying to import it.

The task doesn't ask for the private key to be saved to disk, so no local_file resource is included. It lives in Terraform state only. If you later need to SSH in, add a local_sensitive_file writing tls_private_key.datacenter_kp.private_key_pem with 0400 permissions.

The default security group

data "aws_vpc" "default" { default = true }

data "aws_security_group" "default" {
  vpc_id = data.aws_vpc.default.id
  name   = "default"
}

Two data sources read existing infrastructure rather than creating it: the account's default VPC, then the security group named default within that VPC. Scoping by vpc_id matters — every VPC has its own group named "default", so the name alone is ambiguous.

The group is then attached via vpc_security_group_ids, which takes a list of security group IDs. (The older security_groups argument takes names and is for EC2-Classic; on modern VPC instances vpc_security_group_ids is correct.)

Using data sources keeps Terraform from taking ownership of the default SG — it only references it.

The instance

  • ami = "ami-0c101f26f147fa7fd" — hardcoded exactly as given, so no AMI lookup data source is needed.
  • instance_type = "t2.micro" — as required.
  • key_name = aws_key_pair.datacenter_kp.key_name — attaches the key pair by reference, producing an implicit dependency so the key exists before the instance launches.
  • tags = { Name = "datacenter-ec2" } — an EC2 instance's displayed name comes from the Name tag, not a native field. Omit it and the instance runs but appears unnamed — the usual way to fail this requirement. The Terraform resource label (datacenter_ec2) is unrelated; it's only an internal reference.

Why credit_specification is pinned

  credit_specification {
    cpu_credits = "standard"
  }

Burstable T-family instances run in either standard or unlimited CPU-credit mode. In unlimited, an instance can burn credits beyond its baseline and incur surcharge billing — which constrained sandbox environments actively police, sometimes by resetting the instance or suspending the session. t2.micro defaults to standard, so this is belt-and-braces, but pinning it removes any chance of the mode drifting to unlimited.

Verify

aws ec2 describe-instances \
  --filters Name=tag:Name,Values=datacenter-ec2 Name=instance-state-name,Values=pending,running \
  --query 'Reservations[0].Instances[0].{Id:InstanceId,Type:InstanceType,Key:KeyName,SG:SecurityGroups[0].GroupName,State:State.Name}'

aws ec2 describe-key-pairs --key-names datacenter-kp \
  --query 'KeyPairs[0].{Name:KeyName,Type:KeyType}'

Expected — the instance showing InstanceType: t2.micro, KeyName: datacenter-kp, security group default, and state pending then running (it takes a minute or two); and the key pair reporting KeyType: rsa.

If describe-instances returns nothing, check the Name tag — the filter matches on that tag, not the Terraform resource label.