Managing AWS Networking (VPC, Subnets, Route Tables) with Terraform
Learn how to create and manage AWS networking components including VPCs, subnets, route tables, and security groups using Terraform
Managing AWS Networking with Terraform
AWS networking is fundamental to building secure and scalable cloud infrastructure. This guide demonstrates how to manage AWS networking components using Terraform, including VPCs, subnets, route tables, and security groups.
Video Tutorial
Learn more about managing AWS Networking with Terraform in this comprehensive video tutorial:
Prerequisites
- AWS CLI configured with appropriate permissions
- Terraform installed (version 1.0.0 or later)
- Basic understanding of AWS networking concepts
- Understanding of CIDR notation and IP addressing
Project Structure
networking-terraform/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars
Network Architecture
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
provider "aws" {
region = var.aws_region
}
# VPC
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project_name}-vpc"
Environment = var.environment
}
}
# Availability Zones
data "aws_availability_zones" "available" {
state = "available"
}
# Public Subnets
resource "aws_subnet" "public" {
count = var.az_count
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index)
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-public-${count.index + 1}"
Environment = var.environment
Type = "Public"
}
}
# Private Subnets
resource "aws_subnet" "private" {
count = var.az_count
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index + var.az_count)
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {
Name = "${var.project_name}-private-${count.index + 1}"
Environment = var.environment
Type = "Private"
}
}
# Database Subnets
resource "aws_subnet" "database" {
count = var.az_count
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index + (var.az_count * 2))
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {
Name = "${var.project_name}-database-${count.index + 1}"
Environment = var.environment
Type = "Database"
}
}
# Internet Gateway
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project_name}-igw"
Environment = var.environment
}
}
# Elastic IPs for NAT Gateways
resource "aws_eip" "nat" {
count = var.az_count
vpc = true
tags = {
Name = "${var.project_name}-nat-eip-${count.index + 1}"
Environment = var.environment
}
}
# NAT Gateways
resource "aws_nat_gateway" "main" {
count = var.az_count
allocation_id = aws_eip.nat[count.index].id
subnet_id = aws_subnet.public[count.index].id
tags = {
Name = "${var.project_name}-nat-${count.index + 1}"
Environment = var.environment
}
depends_on = [aws_internet_gateway.main]
}
# Public 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"
Environment = var.environment
}
}
# Private Route Tables
resource "aws_route_table" "private" {
count = var.az_count
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main[count.index].id
}
tags = {
Name = "${var.project_name}-private-rt-${count.index + 1}"
Environment = var.environment
}
}
# Database Route Tables
resource "aws_route_table" "database" {
count = var.az_count
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main[count.index].id
}
tags = {
Name = "${var.project_name}-database-rt-${count.index + 1}"
Environment = var.environment
}
}
# Route Table Associations
resource "aws_route_table_association" "public" {
count = var.az_count
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "private" {
count = var.az_count
subnet_id = aws_subnet.private[count.index].id
route_table_id = aws_route_table.private[count.index].id
}
resource "aws_route_table_association" "database" {
count = var.az_count
subnet_id = aws_subnet.database[count.index].id
route_table_id = aws_route_table.database[count.index].id
}
# VPC Flow Logs
resource "aws_flow_log" "main" {
iam_role_arn = aws_iam_role.flow_log.arn
log_destination = aws_cloudwatch_log_group.flow_log.arn
traffic_type = "ALL"
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project_name}-flow-log"
Environment = var.environment
}
}
# Flow Log CloudWatch Log Group
resource "aws_cloudwatch_log_group" "flow_log" {
name = "/aws/vpc-flow-log/${var.project_name}"
retention_in_days = 30
tags = {
Name = "${var.project_name}-flow-log"
Environment = var.environment
}
}
# Flow Log IAM Role
resource "aws_iam_role" "flow_log" {
name = "${var.project_name}-flow-log-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "vpc-flow-logs.amazonaws.com"
}
}
]
})
tags = {
Name = "${var.project_name}-flow-log-role"
Environment = var.environment
}
}
# Flow Log IAM Policy
resource "aws_iam_role_policy" "flow_log" {
name = "${var.project_name}-flow-log-policy"
role = aws_iam_role.flow_log.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams"
]
Effect = "Allow"
Resource = "*"
}
]
})
}
# Default Network ACL
resource "aws_default_network_acl" "main" {
default_network_acl_id = aws_vpc.main.default_network_acl_id
ingress {
protocol = -1
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
egress {
protocol = -1
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
tags = {
Name = "${var.project_name}-default-nacl"
Environment = var.environment
}
}
# Security Group for Web Tier
resource "aws_security_group" "web" {
name = "${var.project_name}-web-sg"
description = "Security group for web tier"
vpc_id = aws_vpc.main.id
ingress {
description = "HTTP from anywhere"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "HTTPS from anywhere"
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"]
}
tags = {
Name = "${var.project_name}-web-sg"
Environment = var.environment
}
}
# Security Group for App Tier
resource "aws_security_group" "app" {
name = "${var.project_name}-app-sg"
description = "Security group for application tier"
vpc_id = aws_vpc.main.id
ingress {
description = "HTTP from web tier"
from_port = 8080
to_port = 8080
protocol = "tcp"
security_groups = [aws_security_group.web.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_name}-app-sg"
Environment = var.environment
}
}
# Security Group for Database Tier
resource "aws_security_group" "db" {
name = "${var.project_name}-db-sg"
description = "Security group for database tier"
vpc_id = aws_vpc.main.id
ingress {
description = "MySQL from app tier"
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = [aws_security_group.app.id]
}
tags = {
Name = "${var.project_name}-db-sg"
Environment = var.environment
}
}
## Variables Configuration
Create `variables.tf`:
```hcl
variable "aws_region" {
description = "AWS region"
type = string
default = "us-west-2"
}
variable "project_name" {
description = "Name of the project"
type = string
}
variable "environment" {
description = "Environment name"
type = string
default = "dev"
}
variable "vpc_cidr" {
description = "CIDR block for VPC"
type = string
default = "10.0.0.0/16"
}
variable "az_count" {
description = "Number of AZs to use"
type = number
default = 2
}
Output Configuration
Create outputs.tf:
output "vpc_id" {
description = "ID of the VPC"
value = aws_vpc.main.id
}
output "public_subnet_ids" {
description = "IDs of public subnets"
value = aws_subnet.public[*].id
}
output "private_subnet_ids" {
description = "IDs of private subnets"
value = aws_subnet.private[*].id
}
output "database_subnet_ids" {
description = "IDs of database subnets"
value = aws_subnet.database[*].id
}
output "nat_gateway_ids" {
description = "IDs of NAT Gateways"
value = aws_nat_gateway.main[*].id
}
output "web_security_group_id" {
description = "ID of web tier security group"
value = aws_security_group.web.id
}
output "app_security_group_id" {
description = "ID of app tier security group"
value = aws_security_group.app.id
}
output "db_security_group_id" {
description = "ID of database tier security group"
value = aws_security_group.db.id
}
## Network Design Patterns
1. **Three-Tier Architecture**
- Public tier (web)
- Private tier (application)
- Database tier
2. **High Availability**
- Multiple Availability Zones
- NAT Gateway per AZ
- Redundant subnets
3. **Security**
- Network ACLs
- Security Groups
- VPC Flow Logs
## Security Best Practices
1. **Network Segmentation**
- Use separate subnets for different tiers
- Implement proper routing
- Control traffic flow
2. **Access Control**
- Restrictive security groups
- Network ACLs for subnet-level control
- Principle of least privilege
3. **Monitoring**
- Enable VPC Flow Logs
- Monitor network traffic
- Set up alerts
## Deployment Steps
1. Initialize Terraform:
```bash
terraform init
- Create
terraform.tfvars:
aws_region = "us-west-2"
project_name = "my-app"
environment = "prod"
vpc_cidr = "10.0.0.0/16"
az_count = 2
- Review the plan:
terraform plan
- Apply the configuration:
terraform apply
Network Troubleshooting
-
Connectivity Issues
- Check route tables
- Verify security groups
- Validate network ACLs
-
NAT Gateway Problems
- Confirm EIP allocation
- Check route table associations
- Verify Internet Gateway
-
Subnet Issues
- Validate CIDR ranges
- Check AZ assignments
- Confirm route associations
Conclusion
You’ve learned how to set up and manage AWS Networking using Terraform. This setup provides:
- Secure network architecture
- High availability
- Proper network segmentation
- Traffic monitoring
Remember to:
- Follow security best practices
- Monitor network traffic
- Maintain proper documentation
- Regularly review and update configurations