[AWS] TransitGateway + Terrafrom Project 02

Mohamed El ErakiMohamed El Eraki
15 min read

Inception

Hello everyone, This article is part of The Terraform + AWS series, the Examples in this series is built in sequence, I use this series to publish out Projects & Knowledge.


Overview

Hello Gurus, ๐‘จ๐‘พ๐‘บ ๐‘ป๐’“๐’‚๐’๐’”๐’Š๐’•๐‘ฎ๐’‚๐’•๐’†๐’˜๐’‚๐’š acts as a ๐’„๐’†๐’๐’•๐’“๐’‚๐’ ๐‘ฏ๐’–๐’ƒ for your network connections, ๐‘บ๐’Š๐’Ž๐’‘๐’๐’Š๐’‡๐’š and ๐’”๐’„๐’‚๐’๐’Š๐’๐’ˆ your Network across ๐‘ฝ๐‘ท๐‘ช๐’”, ๐‘จ๐‘พ๐‘บ ๐‘จ๐’„๐’„๐’๐’–๐’๐’•๐’”, and ๐‘ถ๐’-๐’‘๐’“๐’†๐’Ž๐’Š๐’”๐’†๐’”, This connection simplifies your network and puts an ๐’†๐’๐’… ๐’•๐’ ๐’„๐’๐’Ž๐’‘๐’๐’†๐’™ ๐’‘๐’†๐’†๐’“๐’Š๐’๐’ˆ ๐’“๐’†๐’๐’‚๐’•๐’Š๐’๐’๐’”๐’‰๐’Š๐’‘๐’”. Transit Gateway acts as a highly scalable cloud routerโ€”each new connection is made only once.

Today's Article will configure a ๐‘ป๐’“๐’‚๐’๐’”๐’Š๐’• ๐‘ฎ๐’‚๐’•๐’†๐’˜๐’‚๐’š to route outbound internet traffic from a VPC without an internet gateway to a VPC that contains a NAT gateway and an internet gateway, Using ๐‘ป๐’†๐’“๐’“๐’‚๐’‡๐’๐’“๐’Ž.โœจ

The Architecture Design will be as the below Diagram


Terraform Resources + Code Steps

Will start out by building the main.tf file as the following steps

๐Ÿ’ก
you can separate the resources in multiple .tf files

Configure AWS Provider

Let's start out by set our AWS Provider configurations

# Configure aws provider
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# Configure aws provider
provider "aws" {
  region = "us-east-1"
  profile = "eraki"   # This is a good practice if you have 
# multiple aws accounts configured, to ensure you're deploy on this one
}
๐Ÿ˜…
Knock knock, Will share with you the full terrafrom code at the end of the Article, and you can view my GitHub Repo as well.

Setup Variables

โ„น
will use theses variables in our code later.
variable "dev_vpc1_cidr_block" {}
variable "dev_vpc2_cidr_block" {}
variable "dev_vpc3_cidr_block" {}

variable "dev_subnet1_cidr_block" {}
variable "dev_subnet2_cidr_block" {}
variable "dev_subnet3_cidr_block" {}
variable "dev_subnet4_cidr_block" {}

variable "environment" {}

Set variables values

Let's set the variables values into terraform-dev.tfvars file

๐Ÿ’ก
I prefers to use terraform-dev.tfvars file instead of the default one terraform.tfvars, This is a good practice if you run the terraform code on multiple stages/zones (e.g. Dev, UAT, and Production zone) and each one contains its stage values
dev_vpc1_cidr_block = "12.0.0.0/16"
dev_vpc2_cidr_block = "13.0.0.0/16"
dev_vpc3_cidr_block = "14.0.0.0/16"

dev_subnet1_cidr_block = "12.0.1.0/24"
dev_subnet2_cidr_block = "13.0.1.0/24"
dev_subnet3_cidr_block = "14.0.1.0/24"
dev_subnet4_cidr_block = "14.0.2.0/24"

environment = "DEV"

VPC One & VPC One contents

Now, Let's start out creating our first resource in the main.tf file.

Create a VPC-1 Resource, Private Subnet, Route table that route all traffic to the Transit gateway, And EC2 Instance with Security group that allow 22 port and key pair.

################
# Create VPC-1 #
################
resource "aws_vpc" "vpc-1" {
  cidr_block    = var.dev_vpc1_cidr_block

  tags = {
    Name = "${var.environment}-vpc-1"
  }
}

# create Subnet-1 for VPC-1
resource "aws_subnet" "subnet-1" {
  vpc_id    =   aws_vpc.vpc-1.id
  cidr_block=   var.dev_subnet1_cidr_block

  tags   = {
    Name = "${var.environment}-subnet-1"
  }
}

# Create a route table to route to transit gateway for VPC-1
resource "aws_route_table" "rt-1" {
  vpc_id    = aws_vpc.vpc-1.id

  route {  # Route all traffic through the Transit Gateway
    cidr_block = "0.0.0.0/0"
    transit_gateway_id  = aws_ec2_transit_gateway.tgw.id
  }

  tags = {
    Name = "${var.environment}-rt-1"
  }
}

# Create Route table association to Subnet-1 in VPC-1
resource "aws_route_table_association" "rt-ass-1" {
  subnet_id = aws_subnet.subnet-1.id
  route_table_id = aws_route_table.rt-1.id
}


# Create key pair for SSH access into EC2 instances
resource "aws_key_pair" "kp-1" {
  key_name  = "server_key"
  public_key = file("~/.ssh/id_rsa.pub")
}

# Create Security Group to allow SSH connection to EC2-1
resource "aws_security_group" "secgrp-1" {
  name  = "secgrp-1"
  description = "Allow SSH from anywhere."
  vpc_id = aws_vpc.vpc-1.id
}

# Create egress rules for secgrp-1 for VPC-1
resource "aws_vpc_security_group_egress_rule" "outbound-secgrp-1" {
  security_group_id = aws_security_group.secgrp-1.id
  cidr_ipv4 = "0.0.0.0/0"
  ip_protocol   = "-1"  # equivalent to all ports
}

# Create ingress rules for secgrp-1 for VPC-1
resource "aws_vpc_security_group_ingress_rule" "inbound-secgrp-1" {
  security_group_id = aws_security_group.secgrp-1.id
  cidr_ipv4 = "0.0.0.0/0"  # do Not apply such subnet in production, just for test
  ip_protocol   = "tcp"
  from_port = 22
  to_port   = 22
}


# Create EC2-1 for VPC-1
resource "aws_instance" "ec2-1" {
  ami   = "ami-0a3c3a20c09d6f377"
  instance_type = "t2.micro"
  #associate_public_ip_address   = true
  subnet_id = aws_subnet.subnet-1.id
  vpc_security_group_ids = [aws_security_group.secgrp-1.id]
  key_name = aws_key_pair.kp-1.key_name

  tags = {
    Name = "${var.environment}-EC2-1"
  }
}

VPC Two & VPC Two contents

Here will create the same resources as VPC-1.

Will Create a VPC-2 Resource, Private Subnet, Route table that route all traffic to the Transit gateway, And EC2 Instance with Security group that allow 22 port and use the key pair created in VPC-1.

################
# Create VPC-2 #
################
resource "aws_vpc" "vpc-2" {
  cidr_block    = var.dev_vpc2_cidr_block

  tags = {
    Name = "${var.environment}-vpc-2"
  }
}

# create Subnet-2 for VPC-2
resource "aws_subnet" "subnet-2" {
  vpc_id    =   aws_vpc.vpc-2.id
  cidr_block=   var.dev_subnet2_cidr_block

  tags   = {
    Name = "${var.environment}-subnet-2"
  }
}

# Create a route table to route to transit gateway for VPC-2
resource "aws_route_table" "rt-2" {
  vpc_id    = aws_vpc.vpc-2.id

  route {  # Route all traffic through the Transit Gateway
    cidr_block = "0.0.0.0/0"
    transit_gateway_id  = aws_ec2_transit_gateway.tgw.id
  }


  tags = {
    Name = "${var.environment}-rt-2"
  }
}

# Create Route table association to Subnet-2 in VPC-2
resource "aws_route_table_association" "rt-ass-2" {
  subnet_id = aws_subnet.subnet-2.id
  route_table_id = aws_route_table.rt-2.id
}

# Create Security Group to allow SSH connection to EC2-2
resource "aws_security_group" "secgrp-2" {
  name  = "secgrp-2"
  description = "Allow SSH from anywhere."
  vpc_id = aws_vpc.vpc-2.id
}

# Create egress rules for secgrp-2 for VPC-2
resource "aws_vpc_security_group_egress_rule" "outbound-secgrp-2" {
  security_group_id = aws_security_group.secgrp-2.id
  cidr_ipv4 = "0.0.0.0/0"
  ip_protocol   = "-1"  # equivalent to all ports
}

# Create ingress rules for secgrp-2 for VPC-2
resource "aws_vpc_security_group_ingress_rule" "inbound-secgrp-2" {
  security_group_id = aws_security_group.secgrp-2.id
  cidr_ipv4 = "0.0.0.0/0"  # do not apply such subnet in production, just for test
  ip_protocol   = "tcp"
  from_port = 22
  to_port   = 22
}


# Create EC2-2 for VPC-2
resource "aws_instance" "ec2-2" {
  ami   = "ami-0a3c3a20c09d6f377"
  instance_type = "t2.micro"
  #associate_public_ip_address   = true
  subnet_id = aws_subnet.subnet-2.id
  vpc_security_group_ids = [aws_security_group.secgrp-2.id]
  key_name = aws_key_pair.kp-1.key_name  # use the same key

  tags = {
    Name = "${var.environment}-EC2-2"
  }
}

VPC Three & VPC Three contents

Create a VPC-3 Resource, Public & Private Subnets, Internet gateway IGW, Nat gateway to route the traffic from private subnet to the internet, Route table to Route traffic to the internet and internal to transitgateway attachments.

################
# Create VPC-3 #
################
resource "aws_vpc" "vpc-3" {
  cidr_block = var.dev_vpc3_cidr_block

  tags = {
    Name = "${var.environment}-vpc-3"
  }
}

# Create an internet gateway
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.vpc-3.id

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

# Create subnet-4 for vpc-3 - Public subnet
resource "aws_subnet" "subnet-4" {
  vpc_id = aws_vpc.vpc-3.id
  cidr_block = var.dev_subnet4_cidr_block
  availability_zone = "us-east-1c"  # subnet 3 & 4 must be at the same zone

  tags = {
    Name = "${var.environment}-subnet-4-Pub"
  }
}

# Create route for subnet-4 to internet gateway - public subnet
resource "aws_route_table" "rtable-pub" {
  vpc_id = aws_vpc.vpc-3.id

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

  route {  # route to vpc-1
    cidr_block = var.dev_vpc1_cidr_block
    transit_gateway_id = aws_ec2_transit_gateway.tgw.id
  }

    route {  # route to vpc-2
    cidr_block = var.dev_vpc2_cidr_block
    transit_gateway_id = aws_ec2_transit_gateway.tgw.id
  }
}

# Associate the route table to public subnet
resource "aws_route_table_association" "rt-ass-Pub" {
  subnet_id      = aws_subnet.subnet-4.id
  route_table_id = aws_route_table.rtable-pub.id
}


# Create an elastic IP for the NAT Gateway
resource "aws_eip" "nat-eip" {
  #vpc   = true
  domain = "vpc"
}

# Create a NAT Gateway in the public subnet for vpc-3
resource "aws_nat_gateway" "nat-gw" {
  subnet_id = aws_subnet.subnet-4.id
  allocation_id = aws_eip.nat-eip.id
}

# Create subnet-3 for vpc-3
# The private subnet should be in the same Availability Zone as the public subnet
resource "aws_subnet" "subnet-3" {
  vpc_id = aws_vpc.vpc-3.id
  cidr_block = var.dev_subnet3_cidr_block
  availability_zone = "us-east-1c"  # subnet 3 & 4 must be at the same zone

  tags = {
    Name = "${var.environment}-subnet-3-Prv"
  }
}

# Create a route table for the private subnet
resource "aws_route_table" "private-rt" {
  vpc_id = aws_vpc.vpc-3.id
}

# Add a route to the private route table to direct all traffic to the NAT Gateway
resource "aws_route" "private-route" {
  route_table_id         = aws_route_table.private-rt.id
  destination_cidr_block = "0.0.0.0/0"
  nat_gateway_id        = aws_nat_gateway.nat-gw.id
}

# Associate the private route table with the private subnet
resource "aws_route_table_association" "private_subnet_association" {
  subnet_id      = aws_subnet.subnet-3.id
  route_table_id = aws_route_table.private-rt.id
}

Transit gateway Resources

At this Step, Will Create a Transit gateway Resource, Attachment for each VPC, and Transit gateway route table.

# Create Transitgateway
resource "aws_ec2_transit_gateway" "tgw" {
  description                     = "Development Main Transitgateway"
  default_route_table_association = "disable"
  /*default_route_table_propagation = "disable"
  dns_support                     = "enable"
  vpn_ecmp_support                = "enable"*/


  tags = {
    Name = "${var.environment}-tgw"
  }
}

# Create an attachment to subnet-1
resource "aws_ec2_transit_gateway_vpc_attachment" "tgw-att-1" {
  vpc_id = aws_vpc.vpc-1.id
  subnet_ids = [aws_subnet.subnet-1.id]
  transit_gateway_id = aws_ec2_transit_gateway.tgw.id
  #transit_gateway_default_route_table_propagation = true

    tags = {
    Name = "${var.environment}-tgw-att-1"
  }
}

# Associate the route table to attachment
resource "aws_ec2_transit_gateway_route_table_association" "tgw-rt-ass-01" {
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.tgw-att-1.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw-rtb.id
}

# Create an attachment to subnet-2
resource "aws_ec2_transit_gateway_vpc_attachment" "tgw-att-2" {
  vpc_id = aws_vpc.vpc-2.id
  subnet_ids = [aws_subnet.subnet-2.id]
  transit_gateway_id = aws_ec2_transit_gateway.tgw.id
  #transit_gateway_default_route_table_propagation = true

    tags = {
    Name = "${var.environment}-tgw-att-2"
  }
}

# Associate the route table to attachment
resource "aws_ec2_transit_gateway_route_table_association" "tgw-rt-ass-02" {
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.tgw-att-2.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw-rtb.id
}

# Create an attachment to subnet-4
resource "aws_ec2_transit_gateway_vpc_attachment" "tgw-att-4" {
  vpc_id = aws_vpc.vpc-3.id
  #subnet_ids = [aws_subnet.subnet-3.id, aws_subnet.subnet-4.id]
  subnet_ids = [aws_subnet.subnet-3.id]
  transit_gateway_id = aws_ec2_transit_gateway.tgw.id
  ##transit_gateway_default_route_table_propagation = true

    tags = {
    Name = "${var.environment}-tgw-att-4"
  }
}

# Associate the route table to attachment
resource "aws_ec2_transit_gateway_route_table_association" "tgw-rt-ass-03" {
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.tgw-att-4.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw-rtb.id
}


# Create a transit gateway route table
resource "aws_ec2_transit_gateway_route_table" "tgw-rtb" {
  transit_gateway_id = aws_ec2_transit_gateway.tgw.id

  tags = {
    Name = "${var.environment}-tgw-rtb"
  }
}

# Create transit gateway route rules for subnet 1
resource "aws_ec2_transit_gateway_route" "tgw-route-subnet-1" {
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw-rtb.id
  destination_cidr_block          = var.dev_subnet1_cidr_block # The CIDR block for the development VPC Subnet 1
  transit_gateway_attachment_id   = aws_ec2_transit_gateway_vpc_attachment.tgw-att-1.id
}

# Create transit gateway route rules for subnet 2
resource "aws_ec2_transit_gateway_route" "tgw-route-subnet-2" {
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw-rtb.id
  destination_cidr_block          = var.dev_subnet2_cidr_block # The CIDR block for the development VPC Subnet 1
  transit_gateway_attachment_id   = aws_ec2_transit_gateway_vpc_attachment.tgw-att-2.id
}

# Create transit gateway route rules for subnet 4
resource "aws_ec2_transit_gateway_route" "tgw-route-subnet-4" {
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw-rtb.id
  destination_cidr_block          = var.dev_subnet3_cidr_block
  transit_gateway_attachment_id   = aws_ec2_transit_gateway_vpc_attachment.tgw-att-4.id
}

# Create transit gateway route rules for subnet 4
resource "aws_ec2_transit_gateway_route" "tgw-route-subnet-4-all" {
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw-rtb.id
  destination_cidr_block          = "0.0.0.0/0"
  transit_gateway_attachment_id   = aws_ec2_transit_gateway_vpc_attachment.tgw-att-4.id
}
๐Ÿ’ก
Preferred to have separated route tables for each attachment in case you have Routing customization (i.e. want to isolate the VPCs, for exampleto deny vpc A to reach vpc B)

Entire Terraform Code

As mentioned above, Down below is my main.tf file content.

you can skip this section and got to the Apply Terrafrom Code section.

๐Ÿ’ก
You can view it at my GitHub Repo.
# Configure aws provider
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# Configure aws provider
provider "aws" {
  region = "us-east-1"
  profile = "eraki"
}



#######################
## Variables Section ##
#######################
variable "dev_vpc1_cidr_block" {}
variable "dev_vpc2_cidr_block" {}
variable "dev_vpc3_cidr_block" {}

variable "dev_subnet1_cidr_block" {}
variable "dev_subnet2_cidr_block" {}
variable "dev_subnet3_cidr_block" {}
variable "dev_subnet4_cidr_block" {}

variable "environment" {}


#######################
## Resources Section ##
#######################

################
# Create VPC-1 #
################
resource "aws_vpc" "vpc-1" {
  cidr_block    = var.dev_vpc1_cidr_block

  tags = {
    Name = "${var.environment}-vpc-1"
  }
}

# create Subnet-1 for VPC-1
resource "aws_subnet" "subnet-1" {
  vpc_id    =   aws_vpc.vpc-1.id
  cidr_block=   var.dev_subnet1_cidr_block

  tags   = {
    Name = "${var.environment}-subnet-1"
  }
}

# Create a route table to route to transit gateway for VPC-1
resource "aws_route_table" "rt-1" {
  vpc_id    = aws_vpc.vpc-1.id

  route {  # Route all traffic through the Transit Gateway
    cidr_block = "0.0.0.0/0"
    transit_gateway_id  = aws_ec2_transit_gateway.tgw.id
  }

  tags = {
    Name = "${var.environment}-rt-1"
  }
}

# Create Route table association to Subnet-1 in VPC-1
resource "aws_route_table_association" "rt-ass-1" {
  subnet_id = aws_subnet.subnet-1.id
  route_table_id = aws_route_table.rt-1.id
}


# Create key pair for SSH access into EC2 instances
resource "aws_key_pair" "kp-1" {
  key_name  = "server_key"
  public_key = file("~/.ssh/id_rsa.pub")
}

# Create Security Group to allow SSH connection to EC2-1
resource "aws_security_group" "secgrp-1" {
  name  = "secgrp-1"
  description = "Allow SSH from anywhere."
  vpc_id = aws_vpc.vpc-1.id
}

# Create egress rules for secgrp-1 for VPC-1
resource "aws_vpc_security_group_egress_rule" "outbound-secgrp-1" {
  security_group_id = aws_security_group.secgrp-1.id
  cidr_ipv4 = "0.0.0.0/0"
  ip_protocol   = "-1"  # equivalent to all ports
}

# Create ingress rules for secgrp-1 for VPC-1
resource "aws_vpc_security_group_ingress_rule" "inbound-secgrp-1" {
  security_group_id = aws_security_group.secgrp-1.id
  cidr_ipv4 = "0.0.0.0/0"  # do Not apply such subnet in production, just for test
  ip_protocol   = "tcp"
  from_port = 22
  to_port   = 22
}


# Create EC2-1 for VPC-1
resource "aws_instance" "ec2-1" {
  ami   = "ami-0a3c3a20c09d6f377"
  instance_type = "t2.micro"
  #associate_public_ip_address   = true
  subnet_id = aws_subnet.subnet-1.id
  vpc_security_group_ids = [aws_security_group.secgrp-1.id]
  key_name = aws_key_pair.kp-1.key_name

  tags = {
    Name = "${var.environment}-EC2-1"
  }
}



################
# Create VPC-2 #
################
resource "aws_vpc" "vpc-2" {
  cidr_block    = var.dev_vpc2_cidr_block

  tags = {
    Name = "${var.environment}-vpc-2"
  }
}

# create Subnet-2 for VPC-2
resource "aws_subnet" "subnet-2" {
  vpc_id    =   aws_vpc.vpc-2.id
  cidr_block=   var.dev_subnet2_cidr_block

  tags   = {
    Name = "${var.environment}-subnet-2"
  }
}

# Create a route table to route to transit gateway for VPC-2
resource "aws_route_table" "rt-2" {
  vpc_id    = aws_vpc.vpc-2.id

  route {  # Route all traffic through the Transit Gateway
    cidr_block = "0.0.0.0/0"
    transit_gateway_id  = aws_ec2_transit_gateway.tgw.id
  }


  tags = {
    Name = "${var.environment}-rt-2"
  }
}

# Create Route table association to Subnet-2 in VPC-2
resource "aws_route_table_association" "rt-ass-2" {
  subnet_id = aws_subnet.subnet-2.id
  route_table_id = aws_route_table.rt-2.id
}

# Create Security Group to allow SSH connection to EC2-2
resource "aws_security_group" "secgrp-2" {
  name  = "secgrp-2"
  description = "Allow SSH from anywhere."
  vpc_id = aws_vpc.vpc-2.id
}

# Create egress rules for secgrp-2 for VPC-2
resource "aws_vpc_security_group_egress_rule" "outbound-secgrp-2" {
  security_group_id = aws_security_group.secgrp-2.id
  cidr_ipv4 = "0.0.0.0/0"
  ip_protocol   = "-1"  # equivalent to all ports
}

# Create ingress rules for secgrp-2 for VPC-2
resource "aws_vpc_security_group_ingress_rule" "inbound-secgrp-2" {
  security_group_id = aws_security_group.secgrp-2.id
  cidr_ipv4 = "0.0.0.0/0"  # do not apply such subnet in production, just for test
  ip_protocol   = "tcp"
  from_port = 22
  to_port   = 22
}


# Create EC2-2 for VPC-2
resource "aws_instance" "ec2-2" {
  ami   = "ami-0a3c3a20c09d6f377"
  instance_type = "t2.micro"
  #associate_public_ip_address   = true
  subnet_id = aws_subnet.subnet-2.id
  vpc_security_group_ids = [aws_security_group.secgrp-2.id]
  key_name = aws_key_pair.kp-1.key_name  # use the same key

  tags = {
    Name = "${var.environment}-EC2-2"
  }
}


################
# Create VPC-3 #
################
resource "aws_vpc" "vpc-3" {
  cidr_block = var.dev_vpc3_cidr_block

  tags = {
    Name = "${var.environment}-vpc-3"
  }
}

# Create an internet gateway
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.vpc-3.id

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

# Create subnet-4 for vpc-3 - Public subnet
resource "aws_subnet" "subnet-4" {
  vpc_id = aws_vpc.vpc-3.id
  cidr_block = var.dev_subnet4_cidr_block
  availability_zone = "us-east-1c"  # subnet 3 & 4 must be at the same zone

  tags = {
    Name = "${var.environment}-subnet-4-Pub"
  }
}

# Create route for subnet-4 to internet gateway - public subnet
resource "aws_route_table" "rtable-pub" {
  vpc_id = aws_vpc.vpc-3.id

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

  route {  # route to vpc-1
    cidr_block = var.dev_vpc1_cidr_block
    transit_gateway_id = aws_ec2_transit_gateway.tgw.id
  }

    route {  # route to vpc-2
    cidr_block = var.dev_vpc2_cidr_block
    transit_gateway_id = aws_ec2_transit_gateway.tgw.id
  }
}

# Associate the route table to public subnet
resource "aws_route_table_association" "rt-ass-Pub" {
  subnet_id      = aws_subnet.subnet-4.id
  route_table_id = aws_route_table.rtable-pub.id
}


# Create an elastic IP for the NAT Gateway
resource "aws_eip" "nat-eip" {
  #vpc   = true
  domain = "vpc"
}

# Create a NAT Gateway in the public subnet for vpc-3
resource "aws_nat_gateway" "nat-gw" {
  subnet_id = aws_subnet.subnet-4.id
  allocation_id = aws_eip.nat-eip.id
}

# Create subnet-3 for vpc-3
# The private subnet should be in the same Availability Zone as the public subnet
resource "aws_subnet" "subnet-3" {
  vpc_id = aws_vpc.vpc-3.id
  cidr_block = var.dev_subnet3_cidr_block
  availability_zone = "us-east-1c"  # subnet 3 & 4 must be at the same zone

  tags = {
    Name = "${var.environment}-subnet-3-Prv"
  }
}

# Create a route table for the private subnet
resource "aws_route_table" "private-rt" {
  vpc_id = aws_vpc.vpc-3.id
}

# Add a route to the private route table to direct all traffic to the NAT Gateway
resource "aws_route" "private-route" {
  route_table_id         = aws_route_table.private-rt.id
  destination_cidr_block = "0.0.0.0/0"
  nat_gateway_id        = aws_nat_gateway.nat-gw.id
}

# Associate the private route table with the private subnet
resource "aws_route_table_association" "private_subnet_association" {
  subnet_id      = aws_subnet.subnet-3.id
  route_table_id = aws_route_table.private-rt.id
}


# Create Transitgateway
resource "aws_ec2_transit_gateway" "tgw" {
  description                     = "Development Main Transitgateway"
  default_route_table_association = "disable"
  /*default_route_table_propagation = "disable"
  dns_support                     = "enable"
  vpn_ecmp_support                = "enable"*/


  tags = {
    Name = "${var.environment}-tgw"
  }
}

# Create an attachment to subnet-1
resource "aws_ec2_transit_gateway_vpc_attachment" "tgw-att-1" {
  vpc_id = aws_vpc.vpc-1.id
  subnet_ids = [aws_subnet.subnet-1.id]
  transit_gateway_id = aws_ec2_transit_gateway.tgw.id
  #transit_gateway_default_route_table_propagation = true

    tags = {
    Name = "${var.environment}-tgw-att-1"
  }
}

# Associate the route table to attachment
resource "aws_ec2_transit_gateway_route_table_association" "tgw-rt-ass-01" {
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.tgw-att-1.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw-rtb.id
}

# Create an attachment to subnet-2
resource "aws_ec2_transit_gateway_vpc_attachment" "tgw-att-2" {
  vpc_id = aws_vpc.vpc-2.id
  subnet_ids = [aws_subnet.subnet-2.id]
  transit_gateway_id = aws_ec2_transit_gateway.tgw.id
  #transit_gateway_default_route_table_propagation = true

    tags = {
    Name = "${var.environment}-tgw-att-2"
  }
}

# Associate the route table to attachment
resource "aws_ec2_transit_gateway_route_table_association" "tgw-rt-ass-02" {
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.tgw-att-2.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw-rtb.id
}

# Create an attachment to subnet-4
resource "aws_ec2_transit_gateway_vpc_attachment" "tgw-att-4" {
  vpc_id = aws_vpc.vpc-3.id
  #subnet_ids = [aws_subnet.subnet-3.id, aws_subnet.subnet-4.id]
  subnet_ids = [aws_subnet.subnet-3.id]
  transit_gateway_id = aws_ec2_transit_gateway.tgw.id
  ##transit_gateway_default_route_table_propagation = true

    tags = {
    Name = "${var.environment}-tgw-att-4"
  }
}

# Associate the route table to attachment
resource "aws_ec2_transit_gateway_route_table_association" "tgw-rt-ass-03" {
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.tgw-att-4.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw-rtb.id
}


# Create a transit gateway route table
resource "aws_ec2_transit_gateway_route_table" "tgw-rtb" {
  transit_gateway_id = aws_ec2_transit_gateway.tgw.id

  tags = {
    Name = "${var.environment}-tgw-rtb"
  }
}

# Create transit gateway route rules for subnet 1
resource "aws_ec2_transit_gateway_route" "tgw-route-subnet-1" {
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw-rtb.id
  destination_cidr_block          = var.dev_subnet1_cidr_block # The CIDR block for the development VPC Subnet 1
  transit_gateway_attachment_id   = aws_ec2_transit_gateway_vpc_attachment.tgw-att-1.id
}

# Create transit gateway route rules for subnet 2
resource "aws_ec2_transit_gateway_route" "tgw-route-subnet-2" {
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw-rtb.id
  destination_cidr_block          = var.dev_subnet2_cidr_block # The CIDR block for the development VPC Subnet 1
  transit_gateway_attachment_id   = aws_ec2_transit_gateway_vpc_attachment.tgw-att-2.id
}

# Create transit gateway route rules for subnet 4
resource "aws_ec2_transit_gateway_route" "tgw-route-subnet-4" {
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw-rtb.id
  destination_cidr_block          = var.dev_subnet3_cidr_block
  transit_gateway_attachment_id   = aws_ec2_transit_gateway_vpc_attachment.tgw-att-4.id
}

# Create transit gateway route rules for subnet 4
resource "aws_ec2_transit_gateway_route" "tgw-route-subnet-4-all" {
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.tgw-rtb.id
  destination_cidr_block          = "0.0.0.0/0"
  transit_gateway_attachment_id   = aws_ec2_transit_gateway_vpc_attachment.tgw-att-4.id
}

Apply Terraform Code

After configured your Code, Time for The exciting Moment to apply the code and just view it become to Real. ๐Ÿ˜

  • First the First, Let's make our code cleaner by:
terraform fmt
๐Ÿ’ก
This Command will format your code scripts in a good way, Thanks Mina
  • Plan is always a good practice (Or even just apply ๐Ÿ˜)
terraform plan -var-file="terraform-dev.tfvars"
  • Let's apply, If there's No Errors appear and you're agree with the build resources
terraform apply -var-file="terraform-dev.tfvars" -auto-approve

Check Route Connection

After built all resources, we should have the resources as the below diagram

it's time to check the connection between ec2 instances, Follow the steps below

  • login at the AWS Console > Navigate to EC2 Then Press Connect on any ec2 machine.

  • On The EC2 instance connect > Select "Connect using EC2 Instance Connect Endpoint"

  • On Select an endpoint, Press create an endpoint

  • Create an endpoint at the vpc-1, and subnet-1 for instance connect as below

  • Press Create endpoint, and grab a cup of coffee, This step will take almost 3 minutes till get available status.

  • Now Login into the ec2 using endpoint.

  • Test route connection to internet by installing the telnet package.

sudo yum install telnet -y

  • Test route connection to the ec2 that live into the vpc-2
telnet <the other ec2 IP> 22  # Test on port 22


Destroy Resources

  • Manually Delete the created endpoint, and wait until deleted.

  • use Terrform destroy command to destroy the entire environment.

terraform destroy -var-file="terrafrom-dev.tfvars" -auto-approve

That's it, Very straightforward, very fast๐Ÿš€. Hope this article inspired you and will appreciate your feedback. Thank you.


Resources

0
Subscribe to my newsletter

Read articles from Mohamed El Eraki directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mohamed El Eraki
Mohamed El Eraki

Cloud & DevOps Engineer, Linux & Windows SysAdmin, PowerShell, Bash, Python Scriptwriter, Passionate about DevOps, Autonomous, and Self-Improvement, being DevOps Expert is my Aim.