Community VPC Module
The community EC2 instance is nice, but the VPC community module really helps us in an obvious way - it simplifies our own configuration.
First, the community VPC module creates an IGW and NAT Gateway for us, so we can go ahead and delete the gateways.tf
file completely. This module intelligently creates route tables for public vs private subnets as well.
Second, we can use our "sub-module" pattern to still create security groups in our own vpc
module while simplifying the rest (by using the community module).
File variables.tf
We first update (rewrite) the module/vpc/variables.tf
file to add a variable for the availability zones we need, and to simplify the variables for subnets.
1variable "infra_env" { 2 type = string 3 description = "infrastructure environment" 4} 5 6variable "vpc_cidr" { 7 type = string 8 description = "The IP range to use for the VPC" 9 default = "10.0.0.0/16"10}11 12# New variable13variable "azs" {14 type = list(string)15 description = "AZs to create subnets into"16}17 18# Simplified Variable19variable "public_subnets" {20 type = list(string)21 description = "subnets to create for public network traffic, one per AZ"22}23 24# Simplified Variable25variable "private_subnets" {26 type = list(string)27 description = "subnets to create for private network traffic, one per AZ"28}
File main.tf
We'll next update (rewrite) file modules/vpc/main.tf
to use the VPC community module.
1module "vpc" { 2 source = "terraform-aws-modules/vpc/aws" 3 version = "2.77.0" 4 5 name = "cloudcasts-${var.infra_env}-vpc" 6 cidr = var.vpc_cidr 7 8 azs = var.azs 9 10 # Single NAT Gateway, see docs linked above11 enable_nat_gateway = true12 single_nat_gateway = true13 one_nat_gateway_per_az = false14 15 private_subnets = var.private_subnets16 public_subnets = var.public_subnets17 18 tags = {19 Name = "cloudcasts-${var.infra_env}-vpc"20 Project = "cloudcasts.io"21 Environment = var.infra_env22 ManagedBy = "terraform"23 }24}
This setup follows the same conventions we used, including creating a single NAT gateway (one option of a few strategies the module provides).
File outputs.tf
We'll next update (rewrite) file modules/vpc/outputs.tf
to use outputs from the new module.
1output "vpc_id" { 2 value = module.vpc.vpc_id 3} 4 5output "vpc_cidr" { 6 value = module.vpc.vpc_cidr_block 7} 8 9output "vpc_public_subnets" {10 value = module.vpc.public_subnets11}12 13output "vpc_private_subnets" {14 value = module.vpc.private_subnets15}16 17output "security_group_public" {18 value = aws_security_group.public.id19}20 21output "security_group_private" {22 value = aws_security_group.private.id23}
Note te use of the "module.vpc" syntax and how we simplified the output a bit.
File security.tf
We update file modules/vpc/security.tf
as needed to use the module output to get some needed values.
1 ### 2 # Public Security Group 3 ## 4 5 resource "aws_security_group" "public" { 6 name = "cloudcasts-${var.infra_env}-public-sg" 7 description = "Public internet access" 8- vpc_id = aws_vpc.vpc.id 9+ vpc_id = module.vpc.vpc_id 10 11 tags = {12 Name = "cloudcasts-${var.infra_env}-public-sg"13 Role = "public"14 Project = "cloudcasts.io"15 Environment = var.infra_env16 ManagedBy = "terraform"17 }18 }19 20 resource "aws_security_group_rule" "public_out" {
21 type = "egress"22 from_port = 023 to_port = 024 protocol = "-1"25 cidr_blocks = ["0.0.0.0/0"]26 27 security_group_id = aws_security_group.public.id28 }29 30 resource "aws_security_group_rule" "public_in_ssh" {31 type = "ingress"32 from_port = 2233 to_port = 2234 protocol = "tcp"35 cidr_blocks = ["0.0.0.0/0"]36 security_group_id = aws_security_group.public.id37 }38 39 resource "aws_security_group_rule" "public_in_http" {40 type = "ingress"41 from_port = 8042 to_port = 8043 protocol = "tcp"44 cidr_blocks = ["0.0.0.0/0"]45 security_group_id = aws_security_group.public.id46 }47 48 resource "aws_security_group_rule" "public_in_https" {49 type = "ingress"50 from_port = 44351 to_port = 44352 protocol = "tcp"53 cidr_blocks = ["0.0.0.0/0"]54 security_group_id = aws_security_group.public.id55 } 56 57 ###58 # Private Security Group59 ##60 61 resource "aws_security_group" "private" {62 name = "cloudcasts-${var.infra_env}-private-sg"63 description = "Private internet access"64- vpc_id = aws_vpc.vpc.id 65+ vpc_id = module.vpc.vpc_id 66 67 tags = {68 Name = "cloudcasts-${var.infra_env}-private-sg"69 Role = "private"70 Project = "cloudcasts.io"71 Environment = var.infra_env72 ManagedBy = "terraform"73 }74 }75 76 resource "aws_security_group_rule" "private_out" {
77 type = "egress"78 from_port = 079 to_port = 080 protocol = "-1"81 cidr_blocks = ["0.0.0.0/0"]82 83 security_group_id = aws_security_group.private.id84 } 85 86 resource "aws_security_group_rule" "private_in" {87 type = "ingress"88 from_port = 089 to_port = 6553590 protocol = "-1"91- cidr_blocks = [aws_vpc.vpc.cidr_block] 92+ cidr_blocks = [module.vpc.vpc_cidr_block] 93 94 security_group_id = aws_security_group.private.id95 }
File cloudcasts.tf
Finally we can update our root module as needed to use our updated VPC module.
1 module "ec2_app" { 2 source = "./modules/ec2" 3 4 infra_env = var.infra_env 5 infra_role = "web" 6 instance_size = "t3.small" 7 instance_ami = data.aws_ami.app.id 8 # instance_root_device_size = 12 9- subnets = keys(module.vpc.vpc_public_subnets) 10+ subnets = module.vpc.vpc_public_subnets 11 security_groups = [module.vpc.security_group_public]12 tags = {13 Name = "cloudcasts-${var.infra_env}-web"14 }15 create_eip = true16 }17 18 module "ec2_worker" {19 source = "./modules/ec2"20 21 infra_env = var.infra_env22 infra_role = "worker"23 instance_size = "t3.large"24 instance_ami = data.aws_ami.app.id25 instance_root_device_size = 2026- subnets = keys(module.vpc.vpc_private_subnets) 27+ subnets = module.vpc.vpc_private_subnets 28 security_groups = [module.vpc.security_group_private]29 tags = {30 Name = "cloudcasts-${var.infra_env}-worker"31 }32 create_eip = false33 }34 35 module "vpc" {36 source = "./modules/vpc"37 38 infra_env = var.infra_env39 vpc_cidr = "10.0.0.0/17"40 41+ azs = ["us-east-2a", "us-east-2b", "us-east-2c"] 42+ public_subnets = slice(cidrsubnets("10.0.0.0/17", 4, 4, 4, 4, 4, 4), 0, 3)43+ private_subnets = slice(cidrsubnets("10.0.0.0/17", 4, 4, 4, 4, 4, 4), 3, 6) 44 }
This uses the cidrsubnets and the slice functions to generate subnets for us.
To use the new module, we need to init it first:
1terraform init2terraform plan -var-file=variables.tfvars3terraform apply -var-file=variables.tfvars