Creating Your First AWS EC2 Instance with Terraform

A step-by-step guide to launching and managing EC2 instances using Infrastructure as Code with Terraform

Creating Your First AWS EC2 Instance with Terraform

Amazon EC2 (Elastic Compute Cloud) is one of the most fundamental services in AWS. In this guide, we’ll walk through creating and managing EC2 instances using Terraform, making your infrastructure deployment reproducible and version-controlled.

Video Tutorial

Learn more about managing AWS EC2 with Terraform in this comprehensive video tutorial:

Prerequisites

  • AWS CLI installed and configured with appropriate credentials
  • Terraform installed (version 1.0.0 or later)
  • Basic understanding of AWS EC2 concepts
  • Text editor of your choice

Project Structure

ec2-terraform/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars

Setting Up the Provider

Create main.tf:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
}

# VPC for our EC2 instance
resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name = "${var.project_name}-vpc"
  }
}

# Public subnet
resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = var.subnet_cidr
  availability_zone       = "${var.aws_region}a"
  map_public_ip_on_launch = true

  tags = {
    Name = "${var.project_name}-public-subnet"
  }
}

# Internet Gateway
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "${var.project_name}-igw"
  }
}

# Route table
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }

  tags = {
    Name = "${var.project_name}-public-rt"
  }
}

# Route table association
resource "aws_route_table_association" "public" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}

# Security group
resource "aws_security_group" "ec2" {
  name        = "${var.project_name}-ec2-sg"
  description = "Security group for EC2 instance"
  vpc_id      = aws_vpc.main.id

  ingress {
    description = "SSH from anywhere"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "HTTP from anywhere"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "${var.project_name}-ec2-sg"
  }
}

# EC2 instance
resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = var.instance_type
  subnet_id     = aws_subnet.public.id

  vpc_security_group_ids = [aws_security_group.ec2.id]
  key_name              = var.key_name

  user_data = <<-EOF
              #!/bin/bash
              yum update -y
              yum install -y httpd
              systemctl start httpd
              systemctl enable httpd
              echo "<h1>Hello from Terraform-managed EC2!</h1>" > /var/www/html/index.html
              EOF

  tags = {
    Name = "${var.project_name}-web"
  }
}

Variables Configuration

Create variables.tf:

variable "aws_region" {
  description = "AWS region"
  type        = string
  default     = "us-west-2"
}

variable "project_name" {
  description = "Name of the project"
  type        = string
  default     = "terraform-ec2-demo"
}

variable "vpc_cidr" {
  description = "CIDR block for VPC"
  type        = string
  default     = "10.0.0.0/16"
}

variable "subnet_cidr" {
  description = "CIDR block for subnet"
  type        = string
  default     = "10.0.1.0/24"
}

variable "instance_type" {
  description = "EC2 instance type"
  type        = string
  default     = "t2.micro"
}

variable "ami_id" {
  description = "AMI ID for EC2 instance"
  type        = string
  # Amazon Linux 2 AMI ID for us-west-2
  default     = "ami-0735c191cf914754d"
}

variable "key_name" {
  description = "Name of the SSH key pair"
  type        = string
}

Output Configuration

Create outputs.tf:

output "instance_public_ip" {
  description = "Public IP of the EC2 instance"
  value       = aws_instance.web.public_ip
}

output "instance_public_dns" {
  description = "Public DNS of the EC2 instance"
  value       = aws_instance.web.public_dns
}

Deployment Steps

  1. Initialize Terraform:
terraform init
  1. Create a key pair in AWS and note its name.

  2. Create terraform.tfvars:

aws_region   = "us-west-2"
project_name = "my-ec2-demo"
key_name     = "your-key-pair-name"
  1. Review the plan:
terraform plan
  1. Apply the configuration:
terraform apply

Accessing Your EC2 Instance

After deployment, you can:

  1. SSH into your instance:
ssh -i path/to/your-key.pem ec2-user@$(terraform output -raw instance_public_ip)
  1. View the web page by visiting the public IP in your browser:
echo "http://$(terraform output -raw instance_public_ip)"

Best Practices

  1. Security:

    • Restrict security group rules to specific IP ranges
    • Use SSH key pairs instead of passwords
    • Regularly update AMIs for security patches
  2. Cost Management:

    • Use appropriate instance types for your needs
    • Implement auto-shutdown for non-production instances
    • Tag resources for cost allocation
  3. Maintenance:

    • Use data sources for AMI IDs to get the latest versions
    • Implement backup strategies
    • Monitor instance metrics

Cleanup

To avoid ongoing charges, destroy the resources when not needed:

terraform destroy

Common Issues and Solutions

  1. Connection Timeout:

    • Check security group rules
    • Verify subnet routing
    • Confirm key pair permissions
  2. Instance Launch Failure:

    • Verify AMI ID is valid for the region
    • Check instance type availability
    • Confirm sufficient AWS limits
  3. User Data Script Issues:

    • Check instance logs in AWS Console
    • Verify script syntax
    • Ensure proper permissions

Next Steps

Consider exploring:

  • Auto Scaling Groups
  • Load Balancers
  • Custom AMIs
  • Instance Profiles
  • CloudWatch monitoring

Conclusion

You’ve successfully created an EC2 instance using Terraform! This Infrastructure as Code approach makes it easy to version control your infrastructure and maintain consistency across deployments.

Remember to:

  • Follow security best practices
  • Monitor costs
  • Keep your Terraform configurations up to date
  • Document any customizations