Quick Answer
resource "aws_instance" "web" {
ami = "ami-0c7217cdde317cfec" # Ubuntu 22.04
instance_type = "t3.micro"
tags = { Name = "my-server" }
}
Run terraform init && terraform apply to launch it. For production, add a VPC, security group, and key pair as shown below.
Prerequisites
- AWS account with IAM credentials configured
- Terraform installed (
terraform version) - AWS CLI configured (
aws configure)
Step 1: Provider Configuration
# main.tf
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.region
}
variable "region" {
default = "us-east-1"
}
Step 2: Dynamic AMI Lookup
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
Step 3: Networking (VPC + Subnet)
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = { Name = "main-vpc" }
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
map_public_ip_on_launch = true
availability_zone = "${var.region}a"
tags = { Name = "public-subnet" }
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
}
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
}
}
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
Step 4: Security Group
resource "aws_security_group" "web" {
name = "web-server"
vpc_id = aws_vpc.main.id
ingress {
description = "SSH"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [var.my_ip] # Restrict to your IP
}
ingress {
description = "HTTP"
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"]
}
}
variable "my_ip" {
description = "Your IP for SSH access (e.g., 1.2.3.4/32)"
type = string
}
Step 5: Key Pair
resource "aws_key_pair" "deploy" {
key_name = "deploy-key"
public_key = file("~/.ssh/id_rsa.pub")
}
Step 6: EC2 Instance
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.web.id]
key_name = aws_key_pair.deploy.key_name
user_data = <<-EOF
#!/bin/bash
apt-get update -y
apt-get install -y nginx
systemctl enable nginx
systemctl start nginx
EOF
root_block_device {
volume_size = 20
volume_type = "gp3"
encrypted = true
}
tags = {
Name = "web-server"
Environment = "dev"
}
}
Step 7: Outputs
output "public_ip" {
value = aws_instance.web.public_ip
}
output "public_dns" {
value = aws_instance.web.public_dns
}
output "ssh_command" {
value = "ssh ubuntu@${aws_instance.web.public_ip}"
}
Deploy
terraform init
terraform plan -var="my_ip=$(curl -s ifconfig.me)/32"
terraform apply -var="my_ip=$(curl -s ifconfig.me)/32"
# SSH into the instance
ssh ubuntu@$(terraform output -raw public_ip)
# Clean up
terraform destroy
Common Instance Types
| Type | vCPU | RAM | Use Case | Cost/month |
|---|---|---|---|---|
| t3.micro | 2 | 1 GB | Dev/test | ~$7.50 |
| t3.small | 2 | 2 GB | Small apps | ~$15 |
| t3.medium | 2 | 4 GB | Web servers | ~$30 |
| m6i.large | 2 | 8 GB | Production | ~$70 |
Related Articles
Conclusion
Deploy EC2 instances with a VPC, security group, and key pair for production readiness. Use aws_ami data source instead of hardcoded AMI IDs, encrypt EBS volumes, restrict SSH to your IP, and always run terraform plan before apply.




