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
VPC and Subnet Stack #
Create Template File #
# Create CloudFormation template
vi vpc-subnet-scheme.yml
CloudFormation Template #
AWSTemplateFormatVersion: '2010-09-09'
Description: VPC and Subnet scheme in us-east-1
Resources:
# VPC: us-east-1 / "10.10.0.0/16"
vpcprod:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.10.0.0/16
Tags:
- Key: Name
Value: vpcprod
EnableDnsSupport: true
EnableDnsHostnames: true
# Subnet 1: Public / us-east-1a / "10.10.0.0/24"
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref vpcprod
CidrBlock: 10.10.0.0/24
AvailabilityZone: us-east-1a
MapPublicIpOnLaunch: true # Automatically assign public IP to EC2 instances
Tags:
- Key: Name
Value: vpcprod_publicsubnet1
# Subnet 2: Private / us-east-1a / "10.10.1.0/24"
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref vpcprod
CidrBlock: 10.10.1.0/24
AvailabilityZone: us-east-1a
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: vpcprod_privatesubnet1
# Subnet 3: Private / us-east-1b / "10.10.2.0/24"
PrivateSubnet3:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref vpcprod
CidrBlock: 10.10.2.0/24
AvailabilityZone: us-east-1b
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: vpcprod_privatesubnet3
# Public Routing Table
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref vpcprod
Tags:
- Key: Name
Value: vpcprod_public-route-table
# Private Routing Table
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref vpcprod
Tags:
- Key: Name
Value: vpcprod_private-route-table
# Routing Table Association for Public Subnet
PublicRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet1
RouteTableId: !Ref PublicRouteTable
# Routing Table Association for Private Subnet 2
PrivateRouteTableAssociation2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet1
RouteTableId: !Ref PrivateRouteTable
# Routing Table Association for Private Subnet 3
PrivateRouteTableAssociation3:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet3
RouteTableId: !Ref PrivateRouteTable
# Internet Gateway
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: vpcprod_ig
# Attach Internet Gateway to VPC
AttachInternetGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref vpcprod
InternetGatewayId: !Ref InternetGateway
# Route for Public Route Table
PublicRouteToInternet:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
# Elastic IP for NAT Gateway
ElasticIPForNAT:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
Tags:
- Key: Name
Value: vpcprod_nat-gateway-eip
# NAT Gateway
NATGateway:
Type: AWS::EC2::NatGateway
Properties:
SubnetId: !Ref PublicSubnet1
AllocationId: !GetAtt ElasticIPForNAT.AllocationId
Tags:
- Key: Name
Value: nat-gateway
# Route for Private Route Table
PrivateRouteToNAT:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NATGateway
Outputs:
# VPC
VpcId:
Description: The ID of the VPC
Value: !Ref vpcprod
# Subnets
PublicSubnet1:
Description: The ID Subnet 1
Value: !Ref PublicSubnet1
PrivateSubnet1:
Description: The ID Subnet 2
Value: !Ref PrivateSubnet1
PrivateSubnet3:
Description: The ID Subnet 3
Value: !Ref PrivateSubnet3
# Routing Tables
PublicRouteTableId:
Description: The ID of the Public Route Table
Value: !Ref PublicRouteTable
PrivateRouteTableId:
Description: The ID of the Private Route Table
Value: !Ref PrivateRouteTable
# Internet Gateway
InternetGatewayId:
Description: The ID of the Internet Gateway
Value: !Ref InternetGateway
PublicRouteId:
Description: The ID of the public route to the Internet Gateway
Value: !Ref PublicRouteToInternet
# NAT Gateway
ElasticIPId:
Description: The Allocation ID of the Elastic IP for the NAT Gateway
Value: !GetAtt ElasticIPForNAT.AllocationId
NATGatewayId:
Description: The ID of the NAT Gateway
Value: !Ref NATGateway
PrivateRouteId:
Description: The ID of the private route to the NAT Gateway
Value: !Ref PrivateRouteToNAT
Validate Template #
# Validate the template
aws cloudformation validate-template --template-body file://vpc-subnet-scheme.yml
# Shell output:
{
"Parameters": [],
"Description": "VPC and Subnet scheme in us-east-1"
}
Deploy Stack #
# Deploy the stack from the template
aws cloudformation create-stack --stack-name vpc-subnet-scheme --template-body file://vpc-subnet-scheme.yml
# Shell output:
{
"StackId": "arn:aws:cloudformation:us-east-1:012345678912:stack/vpc-subnet-scheme/7e615610-bbf3-11ef-ad6d-1207e77f631b"
}
Monitor the Deployment #
# Monitor the deployment
aws cloudformation describe-stacks \
--stack-name vpc-subnet-scheme \
--no-cli-pager
# Shell output:
{
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-east-1:012345678912:stack/vpc-subnet-scheme/7e615610-bbf3-11ef-ad6d-1207e77f631b",
"StackName": "vpc-subnet-scheme",
"Description": "VPC and Subnet scheme in us-east-1",
"CreationTime": "2024-12-16T21:19:54.404000+00:00",
"RollbackConfiguration": {},
"StackStatus": "CREATE_COMPLETE", # Check the status
"DisableRollback": false,
"NotificationARNs": [],
"Outputs": [
{
"OutputKey": "InternetGatewayId",
"OutputValue": "igw-004dcdad354404aa5",
"Description": "The ID of the Internet Gateway"
},
{
"OutputKey": "NATGatewayId",
"OutputValue": "nat-0418d6ee8df9c44a2",
"Description": "The ID of the NAT Gateway"
},
{
"OutputKey": "PrivateSubnet1",
"OutputValue": "subnet-0183988c1ad6b2b2e",
"Description": "The ID Subnet 2"
},
{
"OutputKey": "PrivateSubnet3",
"OutputValue": "subnet-06f3c9f6512ee8cb4",
"Description": "The ID Subnet 3"
},
{
"OutputKey": "VpcId",
"OutputValue": "vpc-0cbb0a63524b5b9ae",
"Description": "The ID of the VPC"
},
{
"OutputKey": "PrivateRouteTableId",
"OutputValue": "rtb-08de34840f3c30a35",
"Description": "The ID of the Private Route Table"
},
{
"OutputKey": "PrivateRouteId",
"OutputValue": "rtb-08de34840f3c30a35|0.0.0.0/0",
"Description": "The ID of the private route to the NAT Gateway"
},
{
"OutputKey": "PublicRouteId",
"OutputValue": "rtb-0d1aa8970230acb54|0.0.0.0/0",
"Description": "The ID of the public route to the Internet Gateway"
},
{
"OutputKey": "ElasticIPId",
"OutputValue": "eipalloc-0b5600ad76a7e38d0",
"Description": "The Allocation ID of the Elastic IP for the NAT Gateway"
},
{
"OutputKey": "PublicSubnet1",
"OutputValue": "subnet-0e824512942cf0439",
"Description": "The ID Subnet 1"
},
{
"OutputKey": "PublicRouteTableId",
"OutputValue": "rtb-0d1aa8970230acb54",
"Description": "The ID of the Public Route Table"
}
],
"Tags": [],
"EnableTerminationProtection": false,
"DriftInformation": {
"StackDriftStatus": "NOT_CHECKED"
}
}
]
}
- Wait till the “StackStatus” changes from
CREATE_IN_PROGRESS
toCREATE_COMPLETE
# Output only the "StackStatus"
aws cloudformation describe-stacks --stack-name vpc-subnet-scheme \
--query "Stacks[0].StackStatus" --output text
# Shell output:
CREATE_COMPLETE
List the Stack Events #
Optional, for troubleshooting list the Stack events:
# List the Stack events
aws cloudformation describe-stack-events \
--stack-name vpc-subnet-scheme \
--no-cli-pager
List Stack Output Variables #
# List the Stack output variables
aws cloudformation describe-stacks --stack-name vpc-subnet-scheme \
--query "Stacks[0].Outputs" --output table
# Shell output:
-----------------------------------------------------------------------------------------------------------------------
| DescribeStacks |
+----------------------------------------------------------+----------------------+-----------------------------------+
| Description | OutputKey | OutputValue |
+----------------------------------------------------------+----------------------+-----------------------------------+
| The ID of the Internet Gateway | InternetGatewayId | igw-004dcdad354404aa5 |
| The ID of the NAT Gateway | NATGatewayId | nat-0418d6ee8df9c44a2 |
| The ID Subnet 2 | PrivateSubnet1 | subnet-0183988c1ad6b2b2e |
| The ID Subnet 3 | PrivateSubnet3 | subnet-06f3c9f6512ee8cb4 |
| The ID of the VPC | VpcId | vpc-0cbb0a63524b5b9ae |
| The ID of the Private Route Table | PrivateRouteTableId | rtb-08de34840f3c30a35 |
| The ID of the private route to the NAT Gateway | PrivateRouteId | rtb-08de34840f3c30a35|0.0.0.0/0 |
| The ID of the public route to the Internet Gateway | PublicRouteId | rtb-0d1aa8970230acb54|0.0.0.0/0 |
| The Allocation ID of the Elastic IP for the NAT Gateway | ElasticIPId | eipalloc-0b5600ad76a7e38d0 |
| The ID Subnet 1 | PublicSubnet1 | subnet-0e824512942cf0439 |
| The ID of the Public Route Table | PublicRouteTableId | rtb-0d1aa8970230acb54 |
+----------------------------------------------------------+----------------------+-----------------------------------+
Security Group and EC2 Stack #
Create Template File #
# Create CloudFormation template
vi sg-ec2.yml
CloudFormation Template #
AWSTemplateFormatVersion: '2010-09-09'
Description: Security Group and EC2 instances for testing VPC connectivity
Mappings:
SubnetMapping:
us-east-1:
PublicSubnet1: subnet-0e824512942cf0439 # Define Public Subnet 1 ID
PrivateSubnet1: subnet-0183988c1ad6b2b2e # Define Private Subnet 1 ID
PrivateSubnet2: subnet-06f3c9f6512ee8cb4 # Define Private Subnet 2 ID
VPCMapping:
us-east-1:
VPCId: vpc-0cbb0a63524b5b9ae # Define VPC ID
Resources:
# Security Group for SSH Access
SSHSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for SSH access
VpcId: !FindInMap [VPCMapping, us-east-1, VPCId]
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: vpc-us-east-1-prod-sg
# EC2 Instance in Public Subnet 1
EC2PublicSubnet1:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0e2c8caa4b6378d8c
InstanceType: t2.micro
SubnetId: !FindInMap [SubnetMapping, us-east-1, PublicSubnet1]
SecurityGroupIds:
- !Ref SSHSecurityGroup
KeyName: us-east-1-pc-le
Tags:
- Key: Name
Value: public-subnet-vm
# EC2 Instance in Private Subnet 1
EC2PrivateSubnet1:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0e2c8caa4b6378d8c
InstanceType: t2.micro
SubnetId: !FindInMap [SubnetMapping, us-east-1, PrivateSubnet1]
SecurityGroupIds:
- !Ref SSHSecurityGroup
KeyName: us-east-1-pc-le
Tags:
- Key: Name
Value: private-subnet-vm1
# EC2 Instance in Private Subnet 2
EC2PrivateSubnet2:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0e2c8caa4b6378d8c
InstanceType: t2.micro
SubnetId: !FindInMap [SubnetMapping, us-east-1, PrivateSubnet2]
SecurityGroupIds:
- !Ref SSHSecurityGroup
KeyName: us-east-1-pc-le
Tags:
- Key: Name
Value: private-subnet-vm2
Outputs:
SecurityGroupId:
Description: Security Group ID for SSH Access
Value: !Ref SSHSecurityGroup
EC2Id1:
Description: EC2 instance ID in public subnet
Value: !Ref EC2PublicSubnet1
EC2Id2:
Description: EC2 instance ID in private subnet 1
Value: !Ref EC2PrivateSubnet1
EC2Id3:
Description: EC2 instance ID in private subnet 2
Value: !Ref EC2PrivateSubnet2
Validate Template #
# Validate the template
aws cloudformation validate-template --template-body file://sg-ec2.yml
# Shell output:
{
"Parameters": [],
"Description": "Security Group and EC2 instances for testing VPC connectivity"
}
Deploy Stack #
# Deploy the stack from the template
aws cloudformation create-stack --stack-name sg-ec2 --template-body file://sg-ec2.yml
# Shell output:
{
"StackId": "arn:aws:cloudformation:us-east-1:012345678912:stack/sg-ec2/f143dc40-bbf6-11ef-b202-12fb3e550959"
}
Monitor the Deployment #
# Monitor the deployment
aws cloudformation describe-stacks \
--stack-name sg-ec2 \
--no-cli-pager
# Shell output:
{
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-east-1:012345678912:stack/sg-ec2/f143dc40-bbf6-11ef-b202-12fb3e550959",
"StackName": "sg-ec2",
"Description": "Security Group and EC2 instances for testing VPC connectivity",
"CreationTime": "2024-12-16T21:44:35.640000+00:00",
"RollbackConfiguration": {},
"StackStatus": "CREATE_COMPLETE",
"DisableRollback": false,
"NotificationARNs": [],
"Outputs": [
{
"OutputKey": "EC2Id3",
"OutputValue": "i-045acb4469ec207b7",
"Description": "EC2 instance ID in private subnet 2"
},
{
"OutputKey": "EC2Id1",
"OutputValue": "i-06b826049001de339",
"Description": "EC2 instance ID in public subnet"
},
{
"OutputKey": "EC2Id2",
"OutputValue": "i-0708a09456a6c1076",
"Description": "EC2 instance ID in private subnet 1"
},
{
"OutputKey": "SecurityGroupId",
"OutputValue": "sg-0dbe64a4f59a939a3",
"Description": "Security Group ID for SSH Access"
}
],
"Tags": [],
"EnableTerminationProtection": false,
"DriftInformation": {
"StackDriftStatus": "NOT_CHECKED"
}
}
]
}
# Output only the "StackStatus"
aws cloudformation describe-stacks --stack-name vpc-subnet-scheme \
--query "Stacks[0].StackStatus" --output text
# Shell output:
CREATE_COMPLETE
List the Stack Events #
Optional, for troubleshooting list the Stack events:
# List the Stack events
aws cloudformation describe-stack-events \
--stack-name sg-ec2 \
--no-cli-pager
List Stack Output Variables #
# List the Stack output variables
aws cloudformation describe-stacks --stack-name sg-ec2 \
--query "Stacks[0].Outputs" --output table
# Shell output:
-----------------------------------------------------------------------------------------------------
| DescribeStacks |
+-------------------------------------------------------+------------------+------------------------+
| Description | OutputKey | OutputValue |
+-------------------------------------------------------+------------------+------------------------+
| EC2 instance ID in private subnet 2 | EC2Id3 | i-045acb4469ec207b7 |
| EC2 instance ID in public subnet | EC2Id1 | i-06b826049001de339 |
| EC2 instance ID in private subnet 1 | EC2Id2 | i-0708a09456a6c1076 |
| Security Group ID for SSH Access | SecurityGroupId | sg-0dbe64a4f59a939a3 |
+-------------------------------------------------------+------------------+------------------------+
SSH into VMs #
List VM IP Addresses #
# Export variables
VM1=i-06b826049001de339
VM2=i-0708a09456a6c1076
VM3=i-045acb4469ec207b7
# Output the IP addresses of VM1
aws ec2 describe-instances \
--region us-east-1 \
--instance-ids $VM1 \
--query "Reservations[0].Instances[0].[PrivateIpAddress, PublicIpAddress]" \
--output table
# Shell output:
-------------------
|DescribeInstances|
+-----------------+
| 10.10.0.90 |
| 3.84.146.55 |
+-----------------+
# Output the IP addresses of VM2
aws ec2 describe-instances \
--region us-east-1 \
--instance-ids $VM2 \
--query "Reservations[0].Instances[0].[PrivateIpAddress, PublicIpAddress]" \
--output table
# Shell output:
-------------------
|DescribeInstances|
+-----------------+
| 10.10.1.96 |
| None |
+-----------------+
# Output the IP addresses of VM3
aws ec2 describe-instances \
--region us-east-1 \
--instance-ids $VM3 \
--query "Reservations[0].Instances[0].[PrivateIpAddress, PublicIpAddress]" \
--output table
# Shell output:
-------------------
|DescribeInstances|
+-----------------+
| 10.10.2.93 |
| None |
+-----------------+
SSH into Public Subnet VM #
- SSH into the first VM in the public subnet via it’s public IPv4 address
# Copy the private SSH key to the VM in the public subnet
scp -i /home/ubuntu/.ssh/us-east-1-pc-le.pem /home/ubuntu/.ssh/us-east-1-pc-le.pem ubuntu@3.84.146.55:~/.ssh/
# SSH into the VM in the public subnet
ssh -i /home/ubuntu/.ssh/us-east-1-pc-le.pem ubuntu@3.84.146.55
- Use the first VM to SSH it into the VMs in the private subnets
SSH into Private Subnet VM 1 #
# SSH into the VM in the private subnet 1
ssh -i /home/ubuntu/.ssh/us-east-1-pc-le.pem ubuntu@10.10.1.96
# Verify the VM can reach the internet
ping www.google.com
# Shell output:
PING www.google.com (142.251.16.99) 56(84) bytes of data.
64 bytes from bl-in-f99.1e100.net (142.251.16.99): icmp_seq=1 ttl=55 time=2.74 ms
64 bytes from bl-in-f99.1e100.net (142.251.16.99): icmp_seq=2 ttl=55 time=2.08 ms
# Exit the VM in the private subnet 1
exit
SSH into Private Subnet VM 2 #
# SSH into the VM in the private subnet 2
ssh -i /home/ubuntu/.ssh/us-east-1-pc-le.pem ubuntu@10.10.2.93
# Verify the VM can reach the internet
ping www.google.com
# Shell output:
PING www.google.com (142.251.163.104) 56(84) bytes of data.
64 bytes from wv-in-f104.1e100.net (142.251.163.104): icmp_seq=1 ttl=57 time=3.53 ms
64 bytes from wv-in-f104.1e100.net (142.251.163.104): icmp_seq=2 ttl=57 time=2.73 ms
Cleanup #
Delete Security Group and EC2 Stack #
# Delete the Security Group and EC2 Stack
aws cloudformation delete-stack --stack-name sg-ec2
# Verify the Stack is deleted
aws cloudformation describe-stacks --stack-name sg-ec2
# Shell output: (Wait till the stack does not exist)
{
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-east-1:012345678912:stack/sg-ec2/f143dc40-bbf6-11ef-b202-12fb3e550959",
"StackName": "sg-ec2",
"Description": "Security Group and EC2 instances for testing VPC connectivity",
"CreationTime": "2024-12-16T21:44:35.640000+00:00",
"DeletionTime": "2024-12-16T21:55:18.147000+00:00",
"RollbackConfiguration": {},
"StackStatus": "DELETE_IN_PROGRESS",
"DisableRollback": false,
"NotificationARNs": [],
"Outputs": [
{
"OutputKey": "EC2Id3",
"OutputValue": "i-045acb4469ec207b7",
"Description": "Instance ID of the EC2 instance in private subnet 2"
},
{
"OutputKey": "EC2Id1",
"OutputValue": "i-06b826049001de339",
"Description": "Instance ID of the EC2 instance in the public subnet"
},
{
"OutputKey": "EC2Id2",
"OutputValue": "i-0708a09456a6c1076",
"Description": "Instance ID of the EC2 instance in private subnet 1"
},
{
"OutputKey": "SecurityGroupId",
"OutputValue": "sg-0dbe64a4f59a939a3",
"Description": "Security Group ID for SSH Access"
}
],
"Tags": [],
"EnableTerminationProtection": false,
"DriftInformation": {
"StackDriftStatus": "NOT_CHECKED"
}
}
]
}
# Shell output:
An error occurred (ValidationError) when calling the DescribeStacks operation: Stack with id sg-ec2 does not exist
Delete VPC and Subnet Stack #
# Delete the VPC and Subnet Stack
aws cloudformation delete-stack --stack-name vpc-subnet-scheme