Terraform AWS Resource Types
Introduction
Terraform allows you to manage AWS infrastructure programmatically using Infrastructure as Code (IaC). One of the most powerful aspects of Terraform is its ability to work with a wide variety of AWS resource types. In this guide, we'll explore the common AWS resources you can manage with Terraform, how to define them, and how they work together to create complete cloud environments.
AWS resources in Terraform are defined as blocks of code within your configuration files, each representing a specific component of your infrastructure like virtual machines, storage systems, networking components, and more.
Core AWS Resource Types
EC2 Instances
Amazon EC2 (Elastic Compute Cloud) instances are virtual servers in the AWS cloud. Here's how to define an EC2 instance in Terraform:
resource "aws_instance" "web_server" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = "WebServer"
Environment = "Development"
}
}
Key parameters:
ami
: The Amazon Machine Image ID that determines the operating systeminstance_type
: The size and capabilities of the instancetags
: Metadata to help organize and manage your instances
When applied, Terraform will create an EC2 instance and output its attributes:
aws_instance.web_server:
id = "i-0123456789abcdef0"
ami = "ami-0c55b159cbfafe1f0"
availability_zone = "us-west-2a"
instance_type = "t2.micro"
private_ip = "172.31.16.139"
public_ip = "34.215.187.52"
...
S3 Buckets
Amazon S3 (Simple Storage Service) provides object storage through a web interface. Creating an S3 bucket with Terraform is straightforward:
resource "aws_s3_bucket" "data_bucket" {
bucket = "my-unique-bucket-name"
tags = {
Name = "Data Bucket"
Environment = "Production"
}
}
resource "aws_s3_bucket_acl" "data_bucket_acl" {
bucket = aws_s3_bucket.data_bucket.id
acl = "private"
}
Key concepts:
- The bucket name must be globally unique across all AWS accounts
- ACLs (Access Control Lists) define who can access the bucket
RDS Databases
Amazon RDS (Relational Database Service) makes it easy to set up and manage databases in the cloud:
resource "aws_db_instance" "default" {
allocated_storage = 10
engine = "mysql"
engine_version = "5.7"
instance_class = "db.t3.micro"
db_name = "mydb"
username = "admin"
password = var.database_password
parameter_group_name = "default.mysql5.7"
skip_final_snapshot = true
}
Important parameters:
engine
: Database type (mysql, postgres, oracle, etc.)instance_class
: Determines computing and memory capacityallocated_storage
: Amount of storage allocated in GB
Networking Resources
VPC (Virtual Private Cloud)
A VPC is your own isolated section of the AWS cloud where you can launch resources:
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "Main VPC"
}
}
Subnets
Subnets allow you to segment your VPC into smaller networks:
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "us-west-2a"
tags = {
Name = "Public Subnet"
}
}
resource "aws_subnet" "private" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.2.0/24"
availability_zone = "us-west-2b"
tags = {
Name = "Private Subnet"
}
}
Security Groups
Security groups act as virtual firewalls for your instances:
resource "aws_security_group" "web" {
name = "web_server_sg"
description = "Allow web traffic"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
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"]
}
}
Identity and Access Management (IAM)
IAM Roles
IAM roles define permissions for what AWS services can do:
resource "aws_iam_role" "lambda_role" {
name = "lambda_execution_role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
},
]
})
}
IAM Policies
Policies define the permissions that are allowed or denied:
resource "aws_iam_policy" "s3_access" {
name = "s3_read_write_policy"
description = "Policy that allows read and write to specific S3 bucket"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"s3:GetObject",
"s3:PutObject",
]
Effect = "Allow"
Resource = [
"${aws_s3_bucket.data_bucket.arn}/*",
]
},
]
})
}
resource "aws_iam_role_policy_attachment" "s3_access_attachment" {
role = aws_iam_role.lambda_role.name
policy_arn = aws_iam_policy.s3_access.arn
}
Serverless Resources
Lambda Functions
AWS Lambda lets you run code without provisioning servers:
resource "aws_lambda_function" "example" {
function_name = "example_lambda"
role = aws_iam_role.lambda_role.arn
handler = "index.handler"
runtime = "nodejs14.x"
filename = "lambda_function.zip"
environment {
variables = {
BUCKET_NAME = aws_s3_bucket.data_bucket.bucket
}
}
}
Real-world Example: Web Application Infrastructure
Let's put these resources together to create a complete web application infrastructure:
Here's the Terraform code for this architecture:
# Create a VPC
resource "aws_vpc" "app_vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "app-vpc"
}
}
# Create an Internet Gateway
resource "aws_internet_gateway" "app_igw" {
vpc_id = aws_vpc.app_vpc.id
tags = {
Name = "app-igw"
}
}
# Create a public subnet
resource "aws_subnet" "public" {
vpc_id = aws_vpc.app_vpc.id
cidr_block = "10.0.1.0/24"
availability_zone = "us-west-2a"
map_public_ip_on_launch = true
tags = {
Name = "public-subnet"
}
}
# Create a private subnet
resource "aws_subnet" "private" {
vpc_id = aws_vpc.app_vpc.id
cidr_block = "10.0.2.0/24"
availability_zone = "us-west-2b"
tags = {
Name = "private-subnet"
}
}
# Create a route table
resource "aws_route_table" "public" {
vpc_id = aws_vpc.app_vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.app_igw.id
}
tags = {
Name = "public-route-table"
}
}
# Associate route table with public subnet
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
# Create security group for web servers
resource "aws_security_group" "web" {
name = "web-sg"
description = "Security group for web servers"
vpc_id = aws_vpc.app_vpc.id
ingress {
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"]
}
}
# Create security group for database
resource "aws_security_group" "db" {
name = "db-sg"
description = "Security group for database"
vpc_id = aws_vpc.app_vpc.id
ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = [aws_security_group.web.id]
}
}
# Create EC2 instance
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
subnet_id = aws_subnet.public.id
security_groups = [aws_security_group.web.id]
user_data = <<-EOF
#!/bin/bash
echo "Hello, World" > index.html
nohup python -m SimpleHTTPServer 80 &
EOF
tags = {
Name = "web-server"
}
}
# Create RDS instance
resource "aws_db_instance" "app_db" {
allocated_storage = 10
engine = "mysql"
engine_version = "5.7"
instance_class = "db.t3.micro"
db_name = "appdb"
username = "admin"
password = var.db_password
parameter_group_name = "default.mysql5.7"
skip_final_snapshot = true
vpc_security_group_ids = [aws_security_group.db.id]
db_subnet_group_name = aws_db_subnet_group.app.name
}
# Create DB subnet group
resource "aws_db_subnet_group" "app" {
name = "app-db-subnet-group"
subnet_ids = [aws_subnet.private.id, aws_subnet.public.id]
tags = {
Name = "App DB subnet group"
}
}
# Create S3 bucket
resource "aws_s3_bucket" "assets" {
bucket = "app-assets-${random_id.bucket_suffix.hex}"
tags = {
Name = "App Assets"
}
}
resource "random_id" "bucket_suffix" {
byte_length = 8
}
Resource Dependencies
Terraform automatically creates a dependency graph based on your resource definitions. For example:
- The EC2 instance depends on the subnet and security group
- The DB subnet group depends on both subnets
- The RDS instance depends on the DB subnet group and security group
Terraform uses these dependencies to determine the correct order for creating, updating, and destroying resources.
Data Sources
Data sources allow you to fetch information about existing AWS resources:
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = "t2.micro"
# ...
}
This data source fetches the latest Amazon Linux AMI and uses it for the EC2 instance.
Best Practices for AWS Resources in Terraform
-
Use variables for reusable configurations:
hclvariable "instance_type" {
description = "EC2 instance type"
default = "t2.micro"
}
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = var.instance_type
# ...
} -
Use modules for repeatable infrastructure patterns:
hclmodule "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "3.14.0"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["us-west-2a", "us-west-2b", "us-west-2c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = true
} -
Use remote state for team collaboration:
hclterraform {
backend "s3" {
bucket = "terraform-state-bucket"
key = "terraform.tfstate"
region = "us-west-2"
encrypt = true
dynamodb_table = "terraform-locks"
}
} -
Tag all resources consistently:
hcllocals {
common_tags = {
Project = "MyApp"
Environment = "Production"
Owner = "DevOps Team"
}
}
resource "aws_instance" "web" {
# ...
tags = merge(
local.common_tags,
{
Name
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)