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
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
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