Skip to main content

AWS Multi Region Multi Virtual Private Cloud (VPC) Networking via Transit Gateway Peering Connection: Terraform Configuration, Mermaid Flowchart

4564 words·
AWS AWS CLI Terraform Multi Region Cloud Network Virtual Private Cloud (VPC) Transit Gateway Transit Gateway Peering Internet Gateway NAT Gateway Routing Tables EC2 Mermaid Flowchart
Table of Contents

Network Overview
#

Network Flowchart
#

graph LR %% Region 1 subgraph Region1["AWS Region 1: us-east-1 N. Virginia"] %% VPC 1 subgraph VPC1["VPC 1: 10.10.0.0/16"] VPC1-IGW["Internet Gateway"] VPC1-NAT["NAT Gateway
Public Subnet
us-east-1a"] %% Routing Tables subgraph VPC1-RoutingTables["Routing Tables"] VPC1-PublicRT["Public Routing Table"] VPC1-PrivateRT["Private Routing Table"] end %% Subnets subgraph VPC1-Subnets["Subnets"] VPC1-Subnet1["Public Subnet 10.10.0.0/24
us-east-1a"] VPC1-Subnet2["Private Subnet 10.10.1.0/24
us-east-1b"] VPC1-Subnet3["Private Subnet 10.10.2.0/24
us-east-1c"] end VPC1-PublicRT -->|Route: 0.0.0.0/0 to IGW| VPC1-IGW VPC1-PrivateRT -->|Route: 0.0.0.0/0 to NAT| VPC1-NAT VPC1-Subnet1 -->|Associated| VPC1-PublicRT VPC1-Subnet2 -->|Associated| VPC1-PrivateRT VPC1-Subnet3 -->|Associated| VPC1-PrivateRT VPC1-NAT -->|Outbound Internet Traffic| VPC1-IGW end %% Transit Gateway TransitGatewayR1["Transit Gateway 1"] %% Transit Gateway Attachments VPC1 -->|Attachment VPC1:
Subnet 10.10.1.0/24
Subnet 10.10.2.0/24| TransitGatewayR1 end %% Region 2 subgraph Region2["AWS Region 2: us-west-2 Oregon"] %% VPC 2 subgraph VPC2["VPC 2: 10.20.0.0/16"] VPC2-IGW["Internet Gateway"] VPC2-NAT["NAT Gateway
Public Subnet
us-east-1a"] %% Routing Tables subgraph VPC2-RoutingTables["Routing Tables"] VPC2-PublicRT["Public Routing Table"] VPC2-PrivateRT["Private Routing Table"] end %% Subnets subgraph VPC2-Subnets["Subnets"] VPC2-Subnet1["Public Subnet 10.20.0.0/24
us-west-a"] VPC2-Subnet2["Private Subnet 10.20.1.0/24
us-west-b"] VPC2-Subnet3["Private Subnet 10.20.2.0/24
us-west-c"] end VPC2-PublicRT -->|Route: 0.0.0.0/0 to IGW| VPC2-IGW VPC2-PrivateRT -->|Route: 0.0.0.0/0 to NAT| VPC2-NAT VPC2-Subnet1 -->|Associated| VPC2-PublicRT VPC2-Subnet2 -->|Associated| VPC2-PrivateRT VPC2-Subnet3 -->|Associated| VPC2-PrivateRT VPC2-NAT -->|Outbound Internet Traffic| VPC2-IGW end %% Transit Gateway TransitGatewayR2["Transit Gateway 2"] %% Transit Gateway Attachments VPC2 -->|Attachment VPC2:
Subnet 10.20.1.0/24
Subnet 10.20.2.0/24| TransitGatewayR2 end %% Transit Gateway Peering Attachment TransitGatewayR1 <-->|Peering Connection | TransitGatewayR2 %% Transit Gateway Routes VPC1-PublicRT -->|Route: 10.20.0.0/16 to VPC2| VPC2 VPC1-PrivateRT -->|Route: 10.20.0.0/16 to VPC2| VPC2 VPC2-PublicRT -->|Route: 10.10.0.0/16 to VPC1| VPC1 VPC2-PrivateRT -->|Route: 10.10.0.0/16 to VPC1| VPC1 %% Dashed Border VPC1 classDef BoarderDash1 stroke-dasharray:5 5; class VPC1-Subnets,VPC1-RoutingTables,VPC1,VPC2 BoarderDash1 %% Dashed Border VPC2 classDef BoarderDash2 stroke-dasharray:5 5; class VPC2-Subnets,VPC2-RoutingTables,VPC2,VPC2 BoarderDash2 %% Font Size classDef Fontsize1 stroke-dasharray:5 5, font-size:20px; class TransitGatewayR1,TransitGatewayR2 Fontsize1 %% Node Colors VPC1 style VPC1-Subnet1 fill:#d1f7d6,stroke:#333,stroke-width:2px style VPC1-Subnet2 fill:#f7d6d6,stroke:#333,stroke-width:2px style VPC1-Subnet3 fill:#f7d6d6,stroke:#333,stroke-width:2px style VPC1-PublicRT fill:#d1f7d6,stroke:#333,stroke-width:2px style VPC1-PrivateRT fill:#f7d6d6,stroke:#333,stroke-width:2px style VPC1-NAT fill:#d6f7f7,stroke:#333,stroke-width:2px %% Node Colors VPC2 style VPC2-Subnet1 fill:#d1f7d6,stroke:#333,stroke-width:2px style VPC2-Subnet2 fill:#f7d6d6,stroke:#333,stroke-width:2px style VPC2-Subnet3 fill:#f7d6d6,stroke:#333,stroke-width:2px style VPC2-PublicRT fill:#d1f7d6,stroke:#333,stroke-width:2px style VPC2-PrivateRT fill:#f7d6d6,stroke:#333,stroke-width:2px style VPC2-NAT fill:#d6f7f7,stroke:#333,stroke-width:2px

Transit Gateway Flowchart
#

graph TD %% Region 1 subgraph Region1["AWS Region 1: us-east-1"] %% VPC 1 subgraph VPC1["VPC 1: 10.10.0.0/16"] end %% Transit Gateway TransitGatewayR1("Transit Gateway 1") %% Transit Gateway Attachments VPC1 -->|Attachment VPC1:
Subnet 10.10.1.0/24
Subnet 10.10.2.0/24| TransitGatewayR1 PeeringAattachment["PeeringAttachment"] --> TransitGatewayR1 %% Routing Tables subgraph VPC1-RoutingTables["Routing Tables"] TransitGateway1RT["Transit Gateway 1 RT"] end TransitGateway1RT -.->|10.10.0.0/16| VPC1 TransitGateway1RT -.->|10.20.0.0/16| PeeringAattachment end %% Region 2 subgraph Region2["AWS Region 2: us-west-2"] %% VPC 2 subgraph VPC2["VPC 2: 10.20.0.0/16"] end %% Transit Gateway TransitGatewayR2("Transit Gateway 2") %% Transit Gateway Attachments VPC2 -->|Attachment VPC2:
Subnet 10.20.1.0/24
Subnet 10.20.2.0/24| TransitGatewayR2 PeeringAattachmentAccepter["PeeringAttachment Accepter"] --> TransitGatewayR2 %% Routing Tables subgraph VPC2-RoutingTables["Routing Tables"] TransitGateway2RT["Transit Gateway 2 RT"] end TransitGateway2RT -.->|10.20.0.0/16| VPC2 TransitGateway2RT -.->|10.10.0.0/16| PeeringAattachmentAccepter end %% Transit Gateway Peering Attachment PeeringAattachment <-.->|Peering Connection | PeeringAattachmentAccepter %% Dashed Border VPC1 classDef BoarderDash1 stroke-dasharray:5 5; class VPC1,VPC2,PeeringAattachment,PeeringAattachmentAccepter,VPC1-RoutingTables,VPC2-RoutingTables BoarderDash1 %% Font Size classDef Fontsize1 font-size:20px; class TransitGatewayR1,TransitGatewayR2 Fontsize1



Prerequisites
#

Install AWS CLI
#

# Update packages
sudo apt update

# Unstall zip tool
sudo apt install unzip -y

# Download AWS CLI zip file
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"

# Unzip
unzip awscliv2.zip

# Install
sudo ./aws/install
# Verify installation / check version
/usr/local/bin/aws --version

Configure AWS CLI
#

# Start AWS CLI configuration
aws configure

Install Terraform
#

# Install the HashiCorp GPG key
wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg > /dev/null

# Verify the GPG key fingerprint
gpg --no-default-keyring --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg --fingerprint

# Add the official HashiCorp repository 
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list

# Install Terraform
sudo apt update && sudo apt-get install terraform
# Verify installation / check version
terraform version

List AWS Availability Zones
#

List the available availability zones of a specific AWS region:

# List availability zones in the region "us-east-1"
aws ec2 describe-availability-zones --region us-east-1 --filters Name=state,Values=available --query "AvailabilityZones[*].ZoneName" --output text

# Shell output:
us-east-1a      us-east-1b      us-east-1c      us-east-1d      us-east-1e      us-east-1f
# List availability zones in the region "us-east-1"
aws ec2 describe-availability-zones --region us-west-2 --filters Name=state,Values=available --query "AvailabilityZones[*].ZoneName" --output text

# Shell output:
us-west-2a      us-west-2b      us-west-2c      us-west-2d



Terraform Network Stack
#

File and Folder Structure
#

The file and folder structure of the Terraform project looks like this:

aws-multi-region-network-stack
├── terraform.tf
├── transitgateway_outputs.tf
├── transitgateway.tf
├── variables.tf
├── vpc1_gateways.tf
├── vpc1_outputs.tf
├── vpc1_routetable.tf
├── vpc1.tf
├── vpc2_gateways.tf
├── vpc2_outputs.tf
├── vpc2_routetable.tf
└── vpc2.tf

Terraform Configuration Files
#

Project Folder & Terraform Provider
#

# Create Terraform project folder
TF_PROJECT_NAME=aws-multi-region-network-stack
mkdir $TF_PROJECT_NAME && cd $TF_PROJECT_NAME

  • terraform.tf
# Terraform Provider
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# Provider Region 1
provider "aws" {
  alias  = "aws_region_1"
  region = var.aws_region_1
}

# Provider Region 2
provider "aws" {
  alias  = "aws_region_2"
  region = var.aws_region_2
}

Variables
#

  • variables.tf
## VPC 1: AWS Region & Availability Zones
variable "aws_region_1" {
  description = "AWS Region"
  type        = string
  default     = "us-east-1"
}

variable "region_1_availability_zone_1" {
  description = "The availability zone for the subnet"
  type        = string
  default     = "us-east-1a"
}

variable "region_1_availability_zone_2" {
  description = "The availability zone for the subnet"
  type        = string
  default     = "us-east-1b"
}

variable "region_1_availability_zone_3" {
  description = "The availability zone for the subnet"
  type        = string
  default     = "us-east-1c"
}


## VPC 2: AWS Region & Availability Zones
variable "aws_region_2" {
  description = "AWS Region"
  type        = string
  default     = "us-west-2"
}

variable "region_2_availability_zone_1" {
  description = "The availability zone for the subnet"
  type        = string
  default     = "us-west-2a"
}

variable "region_2_availability_zone_2" {
  description = "The availability zone for the subnet"
  type        = string
  default     = "us-west-2b"
}

variable "region_2_availability_zone_3" {
  description = "The availability zone for the subnet"
  type        = string
  default     = "us-west-2c"
}


## VPC 1: CIDR Blocks
variable "vpc1_cidr" {
  description = "VPC CIDR block"
  type        = string
  default     = "10.10.0.0/16"
}

variable "vpc1_subnet_cidr_1" {
  description = "Subnet CIDR block"
  type        = string
  default     = "10.10.0.0/24"
}

variable "vpc1_subnet_cidr_2" {
  description = "Subnet CIDR block"
  type        = string
  default     = "10.10.1.0/24"
}

variable "vpc1_subnet_cidr_3" {
  description = "Subnet CIDR block"
  type        = string
  default     = "10.10.2.0/24"
}


## VPC 2: CIDR Blocks
variable "vpc2_cidr" {
  description = "VPC CIDR block"
  type        = string
  default     = "10.20.0.0/16"
}

variable "vpc2_subnet_cidr_1" {
  description = "Subnet CIDR block"
  type        = string
  default     = "10.20.0.0/24"
}

variable "vpc2_subnet_cidr_2" {
  description = "Subnet CIDR block"
  type        = string
  default     = "10.20.1.0/24"
}

variable "vpc2_subnet_cidr_3" {
  description = "Subnet CIDR block"
  type        = string
  default     = "10.20.2.0/24"
}

VPCs and Subnets
#

  • vpc1.tf
# VPC 1
resource "aws_vpc" "vpc1" {
  provider = aws.aws_region_1
  cidr_block           = var.vpc1_cidr
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name       = "VPC1"
    Env        = "Production"
  }
}


# Public Subnet "10.10.0.0/24"
resource "aws_subnet" "vpc1_subnet_public1" {
  provider = aws.aws_region_1
  vpc_id                  = aws_vpc.vpc1.id
  cidr_block              = var.vpc1_subnet_cidr_1
  availability_zone       = var.region_1_availability_zone_1
  map_public_ip_on_launch = true

  tags = {
    Name      = "VPC1 Subnet-Public-1"
    Env       = "Production"
  }
}

# Private Subnet "10.10.1.0/24"
resource "aws_subnet" "vpc1_subnet_private1" {
  provider = aws.aws_region_1
  vpc_id            = aws_vpc.vpc1.id
  cidr_block        = var.vpc1_subnet_cidr_2
  availability_zone = var.region_1_availability_zone_1

  tags = {
    Name      = "VPC1 Subnet-Private-1"
    Env       = "Production"
  }
}

# Private Subnet "10.10.2.0/24"
resource "aws_subnet" "vpc1_subnet_private2" {
  provider = aws.aws_region_1
  vpc_id            = aws_vpc.vpc1.id
  cidr_block        = var.vpc1_subnet_cidr_3
  availability_zone = var.region_1_availability_zone_2

  tags = {
    Name      = "VPC1 Subnet-Private-2"
    Env       = "Production"
  }
}

  • vpc2.tf
# VPC 2
resource "aws_vpc" "vpc2" {
  provider = aws.aws_region_2
  cidr_block           = var.vpc2_cidr
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name       = "vpc2"
    Env        = "Production"
  }
}


# Public Subnet "10.20.0.0/24"
resource "aws_subnet" "vpc2_subnet_public1" {
  provider = aws.aws_region_2
  vpc_id                  = aws_vpc.vpc2.id
  cidr_block              = var.vpc2_subnet_cidr_1
  availability_zone       = var.region_2_availability_zone_1
  map_public_ip_on_launch = true

  tags = {
    Name      = "VPC2 Subnet-Public-1"
    Env       = "Production"
  }
}

# Private Subnet "10.20.1.0/24"
resource "aws_subnet" "vpc2_subnet_private1" {
  provider = aws.aws_region_2
  vpc_id            = aws_vpc.vpc2.id
  cidr_block        = var.vpc2_subnet_cidr_2
  availability_zone = var.region_2_availability_zone_1

  tags = {
    Name      = "VPC2 Subnet-Private-1"
    Env       = "Production"
  }
}

# Private Subnet "10.20.2.0/24"
resource "aws_subnet" "vpc2_subnet_private2" {
  provider = aws.aws_region_2
  vpc_id            = aws_vpc.vpc2.id
  cidr_block        = var.vpc2_subnet_cidr_3
  availability_zone = var.region_2_availability_zone_2

  tags = {
    Name      = "VPC2 Subnet-Private-2"
    Env       = "Production"
  }
}

Internet & NAT Gateways
#

  • vpc1_gateways.tf
# Elastic IP (EIP) for NAT Gateway
resource "aws_eip" "vpc1_nat_gw_eip" {
  provider = aws.aws_region_1
  domain = "vpc"

  tags = {
    Name        = "VPC1 NAT-GW EIP"
    Env         = "Production"
  }
}

# NAT Gateway
resource "aws_nat_gateway" "vpc1_nat_gw" {
  provider = aws.aws_region_1
  allocation_id = aws_eip.vpc1_nat_gw_eip.id
  subnet_id     = aws_subnet.vpc1_subnet_public1.id

  tags = {
    Name        = "VPC1 NAT-GW"
    Env         = "Production"
  }

  depends_on = [aws_internet_gateway.vpc1_igw]
}

# Internet Gateway
resource "aws_internet_gateway" "vpc1_igw" {
  provider = aws.aws_region_1
  vpc_id = aws_vpc.vpc1.id

  tags = {
    Name        = "VPC1 IGW"
    Env         = "Production"
  }
}

vpc2_gateways.tf

# Elastic IP (EIP) for NAT Gateway
resource "aws_eip" "vpc2_nat_gw_eip" {
  provider = aws.aws_region_2
  domain = "vpc"

  tags = {
    Name        = "VPC2 NAT-GW EIP"
    Env         = "Production"
  }
}

# NAT Gateway
resource "aws_nat_gateway" "vpc2_nat_gw" {
  provider = aws.aws_region_2
  allocation_id = aws_eip.vpc2_nat_gw_eip.id
  subnet_id     = aws_subnet.vpc2_subnet_public1.id

  tags = {
    Name        = "VPC2 NAT-GW"
    Env         = "Production"
  }

  depends_on = [aws_internet_gateway.vpc2_igw]
}

# Internet Gateway
resource "aws_internet_gateway" "vpc2_igw" {
  provider = aws.aws_region_2
  vpc_id = aws_vpc.vpc2.id

  tags = {
    Name        = "VPC2 IGW"
    Env         = "Production"
  }
}

Route Tables
#

  • vpc1_routetable.tf
# Private Routing Table
resource "aws_route_table" "vpc1_private_routetable" {
  provider = aws.aws_region_1
  vpc_id = aws_vpc.vpc1.id

  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.vpc1_nat_gw.id
  }

  tags = {
    Name        = "VPC1 Private Route Table"
    Env         = "Production"
  }
}

# Public Routing Table
resource "aws_route_table" "vpc1_public_routetable" {
  provider = aws.aws_region_1
  vpc_id = aws_vpc.vpc1.id

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

  tags = {
    Name        = "VPC1 Public Route Table"
    Env         = "Production"
  }
}


# Associate Routes with Subnets
resource "aws_route_table_association" "vpc1_subnet_public1_ra" {
  provider = aws.aws_region_1
  subnet_id      = aws_subnet.vpc1_subnet_public1.id
  route_table_id = aws_route_table.vpc1_public_routetable.id
}

resource "aws_route_table_association" "vpc1_subnet_private1_ra" {
  provider = aws.aws_region_1
  subnet_id      = aws_subnet.vpc1_subnet_private1.id
  route_table_id = aws_route_table.vpc1_private_routetable.id
}

resource "aws_route_table_association" "vpc1_subnet_private2_ra" {
  provider = aws.aws_region_1
  subnet_id      = aws_subnet.vpc1_subnet_private2.id
  route_table_id = aws_route_table.vpc1_private_routetable.id
}

  • vpc2_routetable.tf
# Private Routing Table
resource "aws_route_table" "vpc2_private_routetable" {
  provider = aws.aws_region_2
  vpc_id = aws_vpc.vpc2.id

  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.vpc2_nat_gw.id
  }

  tags = {
    Name        = "VPC2 Private Route Table"
    Env         = "Production"
  }
}

# Public Routing Table
resource "aws_route_table" "vpc2_public_routetable" {
  provider = aws.aws_region_2
  vpc_id = aws_vpc.vpc2.id

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

  tags = {
    Name        = "VPC2 Public Route Table"
    Env         = "Production"
  }
}


# Associate Routes with Subnets
resource "aws_route_table_association" "vpc2_subnet_public1_ra" {
  provider = aws.aws_region_2
  subnet_id      = aws_subnet.vpc2_subnet_public1.id
  route_table_id = aws_route_table.vpc2_public_routetable.id
}

resource "aws_route_table_association" "vpc2_subnet_private1_ra" {
  provider = aws.aws_region_2
  subnet_id      = aws_subnet.vpc2_subnet_private1.id
  route_table_id = aws_route_table.vpc2_private_routetable.id
}

resource "aws_route_table_association" "vpc2_subnet_private2_ra" {
  provider = aws.aws_region_2
  subnet_id      = aws_subnet.vpc2_subnet_private2.id
  route_table_id = aws_route_table.vpc2_private_routetable.id
}

Outputs
#

  • vpc1_outputs.tf
# VPC1 ID
output "VPC1_ID" {
  value = aws_vpc.vpc1.id
}

# Public Subnet ID
output "VPC1_Public_Subnet_IDs" {
  value = [
    aws_subnet.vpc1_subnet_public1.id
  ]
}

# Private Subnet IDs
output "VPC1_Private_Subnet_IDs" {
  value = [
    aws_subnet.vpc1_subnet_private1.id,
    aws_subnet.vpc1_subnet_private2.id
  ]
}


# Public Routing Table ID
output "VPC1_Public_Route_Table_ID" {
  value = aws_route_table.vpc1_public_routetable.id
}

# Private Routing Table ID
output "VPC1_Private_Route_Table_ID" {
  value = aws_route_table.vpc1_private_routetable.id
}

# Internet Gateway ID
output "VPC1_Internet_Gateway_ID" {
  value = aws_internet_gateway.vpc1_igw.id
}

# NAT Gateway ID
output "VPC1_NAT_Gateway_ID" {
  value = aws_nat_gateway.vpc1_nat_gw.id
}

  • vpc2_outputs.tf
# VPC2 ID
output "VPC2_ID" {
  value = aws_vpc.vpc2.id
}

# Public Subnet ID
output "VPC2_Public_Subnet_IDs" {
  value = [
    aws_subnet.vpc2_subnet_public1.id
  ]
}

# Private Subnet IDs
output "VPC2_Private_Subnet_IDs" {
  value = [
    aws_subnet.vpc2_subnet_private1.id,
    aws_subnet.vpc2_subnet_private2.id
  ]
}


# Public Routing Table ID
output "VPC2_Public_Route_Table_ID" {
  value = aws_route_table.vpc2_public_routetable.id
}

# Private Routing Table ID
output "VPC2_Private_Route_Table_ID" {
  value = aws_route_table.vpc2_private_routetable.id
}

# Internet Gateway ID
output "VPC2_Internet_Gateway_ID" {
  value = aws_internet_gateway.vpc2_igw.id
}

# NAT Gateway ID
output "VPC2_NAT_Gateway_ID" {
  value = aws_nat_gateway.vpc2_nat_gw.id
}

Transit Gateway
#

  • transitgateway.tf
## Transit Gateways

# Transit Gateway 1
resource "aws_ec2_transit_gateway" "transit_gateway_1" {
  provider = aws.aws_region_1
  description = "Transit Gateway for inter-VPC communication"
  default_route_table_association = "enable"
  default_route_table_propagation = "enable"
  dns_support                     = "enable"
  vpn_ecmp_support                = "enable"

  tags = {
    Name = "Transit Gateway"
    Env  = "Production"
  }
}

# Transit Gateway 2
resource "aws_ec2_transit_gateway" "transit_gateway_2" {
  provider = aws.aws_region_2
  description = "Transit Gateway for inter-VPC communication"
  default_route_table_association = "enable"
  default_route_table_propagation = "enable"
  dns_support                     = "enable"
  vpn_ecmp_support                = "enable"

  tags = {
    Name = "Transit Gateway"
    Env  = "Production"
  }
}


## Transit Gateway Attachments

# Transit Gateway Attachment VPC1
resource "aws_ec2_transit_gateway_vpc_attachment" "vpc1_attachment" {
  provider = aws.aws_region_1
  transit_gateway_id = aws_ec2_transit_gateway.transit_gateway_1.id
  vpc_id             = aws_vpc.vpc1.id
  subnet_ids         = [
    aws_subnet.vpc1_subnet_private1.id,
    aws_subnet.vpc1_subnet_private2.id
  ]

  depends_on = [
    aws_subnet.vpc1_subnet_private1,
    aws_subnet.vpc1_subnet_private2,
    aws_ec2_transit_gateway.transit_gateway_1
  ]

  tags = {
    Name = "VPC1-TGW-Attachment"
    Env  = "Production"
  }
}

# Transit Gateway Attachment VPC2
resource "aws_ec2_transit_gateway_vpc_attachment" "vpc2_attachment" {
  provider = aws.aws_region_2
  transit_gateway_id = aws_ec2_transit_gateway.transit_gateway_2.id
  vpc_id             = aws_vpc.vpc2.id
  subnet_ids         = [
    aws_subnet.vpc2_subnet_private1.id,
    aws_subnet.vpc2_subnet_private2.id
  ]

  depends_on = [
    aws_subnet.vpc2_subnet_private1,
    aws_subnet.vpc2_subnet_private2,
    aws_ec2_transit_gateway.transit_gateway_2
  ]

  tags = {
    Name = "VPC2-TGW-Attachment"
    Env  = "Production"
  }
}


## Transit Gateway Peering Connection Attachments

data "aws_caller_identity" "current" {}

# Transit Gateway Peering Connection
resource "aws_ec2_transit_gateway_peering_attachment" "tgw_peering" {
  provider = aws.aws_region_1
  transit_gateway_id        = aws_ec2_transit_gateway.transit_gateway_1.id
  peer_transit_gateway_id   = aws_ec2_transit_gateway.transit_gateway_2.id
  peer_account_id           = data.aws_caller_identity.current.account_id
  peer_region               = "us-west-2"

  tags = {
    Name = "TGW-Peering"
    Env  = "Production"
  }
}

# Accept the Peering Request in VPC2
resource "aws_ec2_transit_gateway_peering_attachment_accepter" "tgw_peering_accepter" {
  provider = aws.aws_region_2
  transit_gateway_attachment_id = aws_ec2_transit_gateway_peering_attachment.tgw_peering.id

  tags = {
    Name = "TGW-Peering-Accepter"
    Env  = "Production"
  }
}


### Routes for inter-VPC networking

# Route in VPC1 private route table to reach VPC2
resource "aws_route" "vpc1_to_vpc2" {
  provider = aws.aws_region_1
  route_table_id         = aws_route_table.vpc1_private_routetable.id
  destination_cidr_block = aws_vpc.vpc2.cidr_block
  transit_gateway_id     = aws_ec2_transit_gateway.transit_gateway_1.id

  depends_on = [aws_ec2_transit_gateway.transit_gateway_1]

}

# Route in VPC1 public route table to reach VPC2 (if needed)
resource "aws_route" "vpc1_public_to_vpc2" {
  provider = aws.aws_region_1
  route_table_id         = aws_route_table.vpc1_public_routetable.id
  destination_cidr_block = aws_vpc.vpc2.cidr_block
  transit_gateway_id     = aws_ec2_transit_gateway.transit_gateway_1.id

  depends_on = [aws_ec2_transit_gateway.transit_gateway_1]
}


# Route in VPC2 private route table to reach VPC1
resource "aws_route" "vpc2_to_vpc1" {
  provider = aws.aws_region_2
  route_table_id         = aws_route_table.vpc2_private_routetable.id
  destination_cidr_block = aws_vpc.vpc1.cidr_block
  transit_gateway_id     = aws_ec2_transit_gateway.transit_gateway_2.id

  depends_on = [aws_ec2_transit_gateway.transit_gateway_2]
}

# Route in VPC2 public route table to reach VPC1 (if needed)
resource "aws_route" "vpc2_public_to_vpc1" {
  provider = aws.aws_region_2
  route_table_id         = aws_route_table.vpc2_public_routetable.id
  destination_cidr_block = aws_vpc.vpc1.cidr_block
  transit_gateway_id     = aws_ec2_transit_gateway.transit_gateway_2.id

  depends_on = [aws_ec2_transit_gateway.transit_gateway_2]
}

  • transitgateway_outputs.tf
output "transit_gateway_1_id" {
  description = "ID of Transit Gateway 1"
  value       = aws_ec2_transit_gateway.transit_gateway_1.id
}

output "transit_gateway_2_id" {
  description = "ID of Transit Gateway 2"
  value       = aws_ec2_transit_gateway.transit_gateway_2.id
}

output "vpc1_attachment_id" {
  description = "ID of the Transit Gateway Attachment for VPC1"
  value       = aws_ec2_transit_gateway_vpc_attachment.vpc1_attachment.id
}

output "vpc2_attachment_id" {
  description = "ID of the Transit Gateway Attachment for VPC2"
  value       = aws_ec2_transit_gateway_vpc_attachment.vpc2_attachment.id
}

output "tgw_peering_attachment_id" {
  description = "ID of the Transit Gateway Peering Attachment"
  value       = aws_ec2_transit_gateway_peering_attachment.tgw_peering.id
}

output "tgw_peering_accepter_id" {
  description = "ID of the Transit Gateway Peering Attachment Accepter"
  value       = aws_ec2_transit_gateway_peering_attachment_accepter.tgw_peering_accepter.id
}



Configuration Deployment
#

Initialize Terraform Project
#

This will download and install the AWS Terraform provider defined in the terraform.tf file with “hashicorp/aws”, as well as setting up the configuration files in the project directory.

# Initialize the Terraform project
terraform init

Validate Configuration Files
#

# Validates the syntax and structure of Terraform configuration files
terraform validate

# Shell output:
Success! The configuration is valid.

Plan the Deployment
#

# Dry run / preview changes before applying them
terraform plan

Apply the Configuration
#

# Create network stack
terraform apply -auto-approve

# Shell output:
Apply complete! Resources: 34 added, 0 changed, 0 destroyed.

Outputs:

VPC1_ID = "vpc-00e406cab44674995"
VPC1_Internet_Gateway_ID = "igw-0d89defa54685f7b7"
VPC1_NAT_Gateway_ID = "nat-0cdd11d3e38e8042d"
VPC1_Private_Route_Table_ID = "rtb-0d30405f67e2ad45d"
VPC1_Private_Subnet_IDs = [
  "subnet-0abd17ff3f892fa66",
  "subnet-02985d6516b5ae45c",
]
VPC1_Public_Route_Table_ID = "rtb-053caea5baca40a60"
VPC1_Public_Subnet_IDs = [
  "subnet-037256ad0b13bd1af",
]
VPC2_ID = "vpc-0b764c6e01ec601e7"
VPC2_Internet_Gateway_ID = "igw-097c5f0a37012465a"
VPC2_NAT_Gateway_ID = "nat-0d30923de0f38afc3"
VPC2_Private_Route_Table_ID = "rtb-0839c983f4e82003d"
VPC2_Private_Subnet_IDs = [
  "subnet-0e89090c585fb508d",
  "subnet-04b4a7529f6a2099c",
]
VPC2_Public_Route_Table_ID = "rtb-0ee9dd4153ad0f73c"
VPC2_Public_Subnet_IDs = [
  "subnet-0bbb63e77e130ccf8",
]
tgw_peering_accepter_id = "tgw-attach-019cd3158f7582b91"
tgw_peering_attachment_id = "tgw-attach-019cd3158f7582b91"
transit_gateway_1_id = "tgw-060055296fecefcba"
transit_gateway_2_id = "tgw-0f69d4fc48ef4632d"
vpc1_attachment_id = "tgw-attach-0e274e426c0edae9e"
vpc2_attachment_id = "tgw-attach-0c1a08496e1033abc"

Verify Deployment State
#

# Lists all resources tracked in the Terraform state file
terraform state list

# Shell output:
data.aws_caller_identity.current
aws_ec2_transit_gateway.transit_gateway_1
aws_ec2_transit_gateway.transit_gateway_2
aws_ec2_transit_gateway_peering_attachment.tgw_peering
aws_ec2_transit_gateway_peering_attachment_accepter.tgw_peering_accepter
aws_ec2_transit_gateway_vpc_attachment.vpc1_attachment
aws_ec2_transit_gateway_vpc_attachment.vpc2_attachment
aws_eip.vpc1_nat_gw_eip
aws_eip.vpc2_nat_gw_eip
aws_internet_gateway.vpc1_igw
aws_internet_gateway.vpc2_igw
aws_nat_gateway.vpc1_nat_gw
aws_nat_gateway.vpc2_nat_gw
aws_route.vpc1_public_to_vpc2
aws_route.vpc1_to_vpc2
aws_route.vpc2_public_to_vpc1
aws_route.vpc2_to_vpc1
aws_route_table.vpc1_private_routetable
aws_route_table.vpc1_public_routetable
aws_route_table.vpc2_private_routetable
aws_route_table.vpc2_public_routetable
aws_route_table_association.vpc1_subnet_private1_ra
aws_route_table_association.vpc1_subnet_private2_ra
aws_route_table_association.vpc1_subnet_public1_ra
aws_route_table_association.vpc2_subnet_private1_ra
aws_route_table_association.vpc2_subnet_private2_ra
aws_route_table_association.vpc2_subnet_public1_ra
aws_subnet.vpc1_subnet_private1
aws_subnet.vpc1_subnet_private2
aws_subnet.vpc1_subnet_public1
aws_subnet.vpc2_subnet_private1
aws_subnet.vpc2_subnet_private2
aws_subnet.vpc2_subnet_public1
aws_vpc.vpc1
aws_vpc.vpc2



Add Transit Gateway Peering Routes
#

List Transit Gateway Default Route Table
#

# List Transitgateway 1 default route table ID
aws ec2 describe-transit-gateway-route-tables \
    --filters Name=transit-gateway-id,Values=tgw-060055296fecefcba \
    --query 'TransitGatewayRouteTables[?DefaultAssociationRouteTable==`true` || DefaultPropagationRouteTable==`true`].TransitGatewayRouteTableId' \
    --output text \
    --region us-east-1

# Shell output:
tgw-rtb-09dd6e94ac11f9ee6
# List Transitgateway 2 default route table ID
aws ec2 describe-transit-gateway-route-tables \
    --filters Name=transit-gateway-id,Values=tgw-0f69d4fc48ef4632d \
    --query 'TransitGatewayRouteTables[?DefaultAssociationRouteTable==`true` || DefaultPropagationRouteTable==`true`].TransitGatewayRouteTableId' \
    --output text \
    --region us-west-2

# Shell output:
tgw-rtb-0eb37956f977077ef

Add Transit Gateway 1 Peering Route
#

# Create route
aws ec2 create-transit-gateway-route \
  --transit-gateway-route-table-id tgw-rtb-09dd6e94ac11f9ee6 \
  --destination-cidr-block 10.20.0.0/16 \
  --transit-gateway-attachment-id tgw-attach-019cd3158f7582b91 \
  --region us-east-1

# Shell output:
{
    "Route": {
        "DestinationCidrBlock": "10.20.0.0/16",
        "TransitGatewayAttachments": [
            {
                "ResourceId": "tgw-0f69d4fc48ef4632d",
                "TransitGatewayAttachmentId": "tgw-attach-019cd3158f7582b91",
                "ResourceType": "peering"
            }
        ],
        "Type": "static",
        "State": "active"
    }
}

Add Transit Gateway 2 Peering Route
#

# Create route
aws ec2 create-transit-gateway-route \
  --transit-gateway-route-table-id tgw-rtb-0eb37956f977077ef \
  --destination-cidr-block 10.10.0.0/16 \
  --transit-gateway-attachment-id tgw-attach-019cd3158f7582b91 \
  --region us-west-2

# Shell output:
{
    "Route": {
        "DestinationCidrBlock": "10.10.0.0/16",
        "TransitGatewayAttachments": [
            {
                "ResourceId": "tgw-060055296fecefcba",
                "TransitGatewayAttachmentId": "tgw-attach-019cd3158f7582b91",
                "ResourceType": "peering"
            }
        ],
        "Type": "static",
        "State": "active"
    }
}



Terraform ECS2 & Security Group Stack
#

File and Folder Structure
#

The file and folder structure of the Terraform project looks like this:

aws-multi-region-compute-stack
├── main.tf
├── outputs.tf
├── security-groups.tf
├── terraform.tf
└── variables.tf

Terraform Configuration Files
#

Project Folder & Terraform Provider
#

# Create Terraform project folder
TF_PROJECT_NAME=aws-multi-region-compute-stack
mkdir $TF_PROJECT_NAME && cd $TF_PROJECT_NAME

  • terraform.tf
# Terraform Provider
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# Provider Region 1
provider "aws" {
  alias  = "aws_region_1"
  region = var.aws_region_1
}

# Provider Region 2
provider "aws" {
  alias  = "aws_region_2"
  region = var.aws_region_2
}

Variables
#

  • variables.tf
# Input Variables for VPC, Subnets, SSH key pair and EC2 image IDs

## AWS Region

variable "aws_region_1" {
  description = "AWS Region"
  type        = string
  default     = "us-east-1"
}

variable "aws_region_2" {
  description = "AWS Region"
  type        = string
  default     = "us-west-2"
}


## VPC1
variable "vpc1_id" {
  default = "vpc-00e406cab44674995" # Define VPC ID
}

# Public Subnet
variable "vpc1_public_subnet_id" {
  default = "subnet-037256ad0b13bd1af" # Define public subnet ID
}

# Private Subnet 1
variable "vpc1_private_subnet1_id" {
  default = "subnet-0abd17ff3f892fa66" # Define private subnet 1 ID
}

# Private Subnet 2
variable "vpc1_private_subnet2_id" {
  default = "subnet-02985d6516b5ae45c" # Define private subnet 2 ID
}


## VPC2
variable "vpc2_id" {
  default = "vpc-0b764c6e01ec601e7" # Define VPC ID
}

# Public Subnet
variable "vpc2_public_subnet_id" {
  default = "subnet-0bbb63e77e130ccf8" # Define public subnet ID
}

# Private Subnet 1
variable "vpc2_private_subnet1_id" {
  default = "subnet-0e89090c585fb508d" # Define private subnet 1 ID
}

# Private Subnet 2
variable "vpc2_private_subnet2_id" {
  default = "subnet-04b4a7529f6a2099c" # Define private subnet 2 ID
}


## VPC 1: SSH Key and EC2 Image

# SSH key pair name
variable "region_1_key_name" {
  default = "us-east-1-pc-le" # Define key pair name
}

# EC2 Image ID
variable "region_1_ami_id" {
  default = "ami-0e2c8caa4b6378d8c" # Define EC2 AMI ID
}

## VPC 1: SSH Key and EC2 Image

# SSH key pair name
variable "region_2_key_name" {
  default = "us-west-2-pc-le" # Define key pair name
}

# EC2 Image ID
variable "region_2_ami_id" {
  default = "ami-05d38da78ce859165" # Define EC2 AMI ID
}

Main Configuration
#

  • main.tf
# VPC1: EC2 Instance in Public Subnet
resource "aws_instance" "ec2_vpc1_public_subnet" {
  provider = aws.aws_region_1
  ami                    = var.region_1_ami_id
  instance_type          = "t2.micro"
  subnet_id              = var.vpc1_public_subnet_id
  key_name               = var.region_1_key_name
  vpc_security_group_ids = [aws_security_group.vpc1_sg.id]

  tags = {
    Name = "public-subnet-vm"
    Env  = "Production"
  }
}

# VPC1: EC2 Instance in Private Subnet 1
resource "aws_instance" "ec2_vpc1_private_subnet1" {
  provider = aws.aws_region_1
  ami                    = var.region_1_ami_id
  instance_type          = "t2.micro"
  subnet_id              = var.vpc1_private_subnet1_id
  key_name               = var.region_1_key_name
  vpc_security_group_ids = [aws_security_group.vpc1_sg.id]

  tags = {
    Name = "private-subnet-vm1"
    Env  = "Production"
  }
}

# VPC1: EC2 Instance in Private Subnet 2
resource "aws_instance" "ec2_vpc1_private_subnet2" {
  provider = aws.aws_region_1
  ami                    = var.region_1_ami_id
  instance_type          = "t2.micro"
  subnet_id              = var.vpc1_private_subnet2_id
  key_name               = var.region_1_key_name
  vpc_security_group_ids = [aws_security_group.vpc1_sg.id]

  tags = {
    Name = "private-subnet-vm2"
    Env  = "Production"
  }
}



# VPC2: EC2 Instance in Public Subnet
resource "aws_instance" "ec2_vpc2_public_subnet" {
  provider = aws.aws_region_2
  ami                    = var.region_2_ami_id
  instance_type          = "t2.micro"
  subnet_id              = var.vpc2_public_subnet_id
  key_name               = var.region_2_key_name
  vpc_security_group_ids = [aws_security_group.vpc2_sg.id]

  tags = {
    Name = "public-subnet-vm"
    Env  = "Production"
  }
}

# VPC2: EC2 Instance in Private Subnet 1
resource "aws_instance" "ec2_vpc2_private_subnet1" {
  provider = aws.aws_region_2
  ami                    = var.region_2_ami_id
  instance_type          = "t2.micro"
  subnet_id              = var.vpc2_private_subnet1_id
  key_name               = var.region_2_key_name
  vpc_security_group_ids = [aws_security_group.vpc2_sg.id]

  tags = {
    Name = "private-subnet-vm1"
    Env  = "Production"
  }
}

# VPC2: EC2 Instance in Private Subnet 2
resource "aws_instance" "ec2_vpc2_private_subnet2" {
  provider = aws.aws_region_2
  ami                    = var.region_2_ami_id
  instance_type          = "t2.micro"
  subnet_id              = var.vpc2_private_subnet2_id
  key_name               = var.region_2_key_name
  vpc_security_group_ids = [aws_security_group.vpc2_sg.id]

  tags = {
    Name = "private-subnet-vm2"
    Env  = "Production"
  }
}

Security Groups
#

  • security-groups.tf
# VPC1: Security Group for SSH Access and Ping
resource "aws_security_group" "vpc1_sg" {
  provider = aws.aws_region_1
  name        = "VPC1-SG"
  description = "Security group for SSH access and ping"
  vpc_id      = var.vpc1_id

  ingress {
    description = "Allow SSH from anywhere"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "Allow ping"
    from_port   = 8
    to_port     = -1
    protocol    = "icmp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    description = "Allow all outbound traffic"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "VPC1-SG"
    Env  = "Production"
  }
}


# VPC1: Security Group for SSH Access and Ping
resource "aws_security_group" "vpc2_sg" {
  provider = aws.aws_region_2
  name        = "VPC2-SG"
  description = "Security group for SSH access and ping"
  vpc_id      = var.vpc2_id

  ingress {
    description = "Allow SSH from anywhere"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "Allow ping"
    from_port   = 8
    to_port     = -1
    protocol    = "icmp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    description = "Allow all outbound traffic"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "VPC2-SG"
    Env  = "Production"
  }
}

Outputs
#

  • outputs.tf
# VPC1: Security Group ID
output "VPC1_SecurityGroup_ID" {
  description = "Security Group ID for SSH Access"
  value       = aws_security_group.vpc1_sg.id
}

# VPC2: Security Group ID
output "VPC2_SecurityGroup_ID" {
  description = "Security Group ID for SSH Access"
  value       = aws_security_group.vpc2_sg.id
}


#### EC2 Instance IDs

# VPC1: EC2 Instance ID: VM in Public Subnet
output "VPC1_Public_Instance_ID" {
  description = "EC2 instance ID in public subnet"
  value       = aws_instance.ec2_vpc1_public_subnet.id
}

# VPC1: EC2 Instance ID: VM in Private Subnet 1
output "VPC1_Private_Instance1_ID" {
  description = "EC2 instance ID in private subnet 1"
  value       = aws_instance.ec2_vpc1_private_subnet1.id
}

# VPC1: EC2 Instance ID: VM in Private Subnet 2
output "VPC1_Private_Instance2_ID" {
  description = "EC2 instance ID in private subnet 2"
  value       = aws_instance.ec2_vpc1_private_subnet2.id
}


# VPC2: EC2 Instance ID: VM in Public Subnet
output "VPC2_Public_Instance_ID" {
  description = "EC2 instance ID in public subnet"
  value       = aws_instance.ec2_vpc2_public_subnet.id
}

# VPC2: EC2 Instance ID: VM in Private Subnet 1
output "VPC2_Private_Instance1_ID" {
  description = "EC2 instance ID in private subnet 1"
  value       = aws_instance.ec2_vpc2_private_subnet1.id
}

# VPC2: EC2 Instance ID: VM in Private Subnet 2
output "VPC2_Private_Instance2_ID" {
  description = "EC2 instance ID in private subnet 2"
  value       = aws_instance.ec2_vpc2_private_subnet2.id
}


#### EC2 IPs

# VPC1: EC2 Instance public IP (VM in Public Subnet)
output "VPC1_Public_Instance_Public_IP" {
  description = "Public IP address of the EC2 instance in the public subnet"
  value       = aws_instance.ec2_vpc1_public_subnet.public_ip
}

# VPC1: EC2 Instance private IP (VM in Public Subnet)
output "VPC1_Public_Instance_Private_IP" {
  description = "Private IP address of the EC2 instance in the public subnet"
  value       = aws_instance.ec2_vpc1_public_subnet.private_ip
}

# VPC1: EC2 Instance private IP (VM1 in Private Subnet)
output "VPC1_Private_Instance1_Private_IP" {
  description = "Private IP address of the EC2 instance in private subnet 1"
  value       = aws_instance.ec2_vpc1_private_subnet1.private_ip
}

# VPC1: EC2 Instance private IP (VM2 in Private Subnet)
output "VPC1_Private_Instance2_Private_IP" {
  description = "Private IP address of the EC2 instance in private subnet 2"
  value       = aws_instance.ec2_vpc1_private_subnet2.private_ip
}


# VPC2: EC2 Instance public IP (VM in Public Subnet)
output "VPC2_Public_Instance_Public_IP" {
  description = "Public IP address of the EC2 instance in the public subnet"
  value       = aws_instance.ec2_vpc2_public_subnet.public_ip
}

# VPC2: EC2 Instance private IP (VM in Public Subnet)
output "VPC2_Public_Instance_Private_IP" {
  description = "Private IP address of the EC2 instance in the public subnet"
  value       = aws_instance.ec2_vpc2_public_subnet.private_ip
}

# VPC2: EC2 Instance private IP (VM1 in Private Subnet)
output "VPC2_Private_Instance1_Private_IP" {
  description = "Private IP address of the EC2 instance in private subnet 1"
  value       = aws_instance.ec2_vpc2_private_subnet1.private_ip
}

# VPC2: EC2 Instance private IP (VM2 in Private Subnet)
output "VPC2_Private_Instance2_Private_IP" {
  description = "Private IP address of the EC2 instance in private subnet 2"
  value       = aws_instance.ec2_vpc2_private_subnet2.private_ip
}



Configuration Deployment
#

Initialize Terraform Project
#

This will download and install the AWS Terraform provider defined in the terraform.tf file with “hashicorp/aws”, as well as setting up the configuration files in the project directory.

# Initialize the Terraform project
terraform init

Validate Configuration Files
#

# Validates the syntax and structure of Terraform configuration files
terraform validate

# Shell output:
Success! The configuration is valid.

Plan the Deployment
#

# Dry run / preview changes before applying them
terraform plan

Apply the Configuration
#

# Create network stack
terraform apply -auto-approve

# Shell output:
Apply complete! Resources: 8 added, 0 changed, 0 destroyed.

Outputs:

VPC1_Private_Instance1_ID = "i-0c69c3913fdd49368"
VPC1_Private_Instance1_Private_IP = "10.10.1.137"
VPC1_Private_Instance2_ID = "i-01b686db3bdd5c4a3"
VPC1_Private_Instance2_Private_IP = "10.10.2.55"
VPC1_Public_Instance_ID = "i-0fd948528820ecbf3"
VPC1_Public_Instance_Private_IP = "10.10.0.95"
VPC1_Public_Instance_Public_IP = "54.89.134.192"
VPC1_SecurityGroup_ID = "sg-0de76a63edc00ffdc"

VPC2_Private_Instance1_ID = "i-0058cd35b909c6d04"
VPC2_Private_Instance1_Private_IP = "10.20.1.173"
VPC2_Private_Instance2_ID = "i-0608d729dec251f74"
VPC2_Private_Instance2_Private_IP = "10.20.2.120"
VPC2_Public_Instance_ID = "i-0e82b6cc55c3d19ce"
VPC2_Public_Instance_Private_IP = "10.20.0.177"
VPC2_Public_Instance_Public_IP = "34.220.211.239"
VPC2_SecurityGroup_ID = "sg-0d992943a4f5ef83d"



Verfiy Network Connectivity
#

VPC1 to VPC2
#

# SSH into the VM in the public subnet
ssh -i /home/ubuntu/.ssh/us-east-1-pc-le.pem ubuntu@54.89.134.192
# Ping the VM in the private subnet 1
ping 10.10.1.137

# Shell output:
PING 10.10.1.137 (10.10.1.137) 56(84) bytes of data.
64 bytes from 10.10.1.137: icmp_seq=1 ttl=64 time=1.51 ms
64 bytes from 10.10.1.137: icmp_seq=2 ttl=64 time=0.952 ms


# Ping the VM in the private subnet 2
ping 10.10.2.55

# Shell output:
PING 10.10.2.55 (10.10.2.55) 56(84) bytes of data.
64 bytes from 10.10.2.55: icmp_seq=1 ttl=64 time=2.96 ms
64 bytes from 10.10.2.55: icmp_seq=2 ttl=64 time=1.29 ms
# Ping the VM in the private subnet 1 in VPC2
ping 10.20.1.173

# Shell output:
PING 10.20.1.173 (10.20.1.173) 56(84) bytes of data.
64 bytes from 10.20.1.173: icmp_seq=1 ttl=61 time=74.0 ms
64 bytes from 10.20.1.173: icmp_seq=2 ttl=61 time=65.6 ms


# Ping the VM in the private subnet 2 in VPC2
ping 10.20.2.120

# Shell output:
PING 10.20.2.120 (10.20.2.120) 56(84) bytes of data.
64 bytes from 10.20.2.120: icmp_seq=1 ttl=61 time=84.2 ms
64 bytes from 10.20.2.120: icmp_seq=2 ttl=61 time=66.0 ms

VPC2 to VPC1
#

# SSH into the VM in the public subnet
ssh -i /home/ubuntu/.ssh/us-west-2-pc-le.pem ubuntu@34.220.211.239
# Ping the VM in the private subnet 1
ping 10.20.1.173

# Shell output:
PING 10.20.1.173 (10.20.1.173) 56(84) bytes of data.
64 bytes from 10.20.1.173: icmp_seq=1 ttl=64 time=1.90 ms
64 bytes from 10.20.1.173: icmp_seq=2 ttl=64 time=0.477 ms


# Ping the VM in the private subnet 2
ping 10.20.2.120

# Shell output:
PING 10.20.2.120 (10.20.2.120) 56(84) bytes of data.
64 bytes from 10.20.2.120: icmp_seq=1 ttl=64 time=2.17 ms
64 bytes from 10.20.2.120: icmp_seq=2 ttl=64 time=1.78 ms
# Ping the VM in the private subnet 1 in VPC2
ping 10.10.1.137

# Shell output:
PING 10.10.1.137 (10.10.1.137) 56(84) bytes of data.
64 bytes from 10.10.1.137: icmp_seq=1 ttl=61 time=69.2 ms
64 bytes from 10.10.1.137: icmp_seq=2 ttl=61 time=61.7 ms


# Ping the VM in the private subnet 2 in VPC2
ping 10.10.2.55

# Shell output:
PING 10.10.2.55 (10.10.2.55) 56(84) bytes of data.
64 bytes from 10.10.2.55: icmp_seq=1 ttl=61 time=70.5 ms
64 bytes from 10.10.2.55: icmp_seq=2 ttl=61 time=61.4 ms



Links #

# AWS Transit Gateway Documentation
https://docs.aws.amazon.com/vpc/latest/tgw/tgw-vpc-attachments.html