*Configuring an AWS Site-to-Site VPN
Summary
In this lab I set up an AWS Site-to-Site VPN between an AWS VPC and an on-premises data center. I didn't have a physical data center, so I simulated one with a second VPC in a different region. On the AWS side, I created a VPC with a private subnet and an instance, EC2-A, plus a Virtual Private Gateway. On the data center side, I created a VPC with a public subnet holding a VPN-Server instance and a private subnet holding an application server, EC2-B. I created a Customer Gateway pointing to VPN-Server's public IP and a Site-to-Site VPN connection using static routing. Then I installed LibreSwan on VPN-Server, configured it from the AWS VPN configuration file, and started the IPsec tunnel. After updating the route tables on both sides and turning off VPN-Server's source/destination check so it could act as a router, EC2-B could ping EC2-A across the VPN using only private IP addresses.
Architecture
| AWS side | Simulated data center | |
|---|---|---|
| Region | Canada (Central) ca-central-1 | US East (N. Virginia) us-east-1 |
| VPC | AWS-VPC, 10.0.0.0/16 | DC-VPC, 192.168.0.0/16 |
| Subnets | Private subnet 10.0.0.0/24, ca-central-1a | Public subnet 192.168.0.0/24, private subnet 192.168.100.0/24 |
| Instances | EC2-A (private, no public IP) | VPN-Server (public), EC2-B (private, no public IP) |
| Internet access | None | Internet Gateway for the public subnet |
| VPN endpoint | Virtual Private Gateway | VPN-Server running LibreSwan |
| VPN resource | Details |
|---|---|
| Virtual Private Gateway (VGW) | Attached to AWS-VPC |
| Customer Gateway (CGW) | IP address = VPN-Server's public IP |
| Site-to-Site VPN connection | VGW ↔ CGW, static routing, static prefix 192.168.0.0/16 |
Traffic path from EC2-B to EC2-A:
EC2-B (192.168.100.x)
→ DC private route table: 10.0.0.0/16 → VPN-Server
→ VPN-Server (LibreSwan encrypts the packet)
→ IPsec tunnel over the internet
→ Virtual Private Gateway
→ EC2-A (10.0.0.x)
Part 1: Building the AWS Network
1. Created AWS-VPC
I switched the console to Canada (Central), opened the VPC console, and created a VPC named AWS-VPC with the CIDR block 10.0.0.0/16. This VPC represented the cloud side of the connection.
2. Created the private subnet
I created a subnet in AWS-VPC with the CIDR block 10.0.0.0/24 in ca-central-1a. It represented internal AWS resources, so it didn't need an Internet Gateway or any public access.
3. Created a route table for the subnet
I created a route table in AWS-VPC, then opened its Subnet associations tab, clicked Edit subnet associations, selected the private subnet, and saved. I used a dedicated route table so I could add a route to the VPN later without touching the VPC's main route table.
4. Launched EC2-A
I launched an instance named EC2-A with these settings:
| Setting | Value |
|---|---|
| AMI | Amazon Linux |
| VPC / Subnet | AWS-VPC / the private subnet |
| Auto-assign public IP | Disable |
Security group inbound rules:
| Type | Protocol | Port | Source | Reason |
|---|---|---|---|---|
| All ICMP - IPv4 | ICMP | All | 192.168.0.0/16 | Lets EC2-A answer pings from anywhere in DC-VPC (VPN-Server and EC2-B). Traffic through the VPN keeps its original private address, so the source is DC-VPC's private range. |
That was the only traffic EC2-A would accept. It didn't need an SSH rule, because it had no public IP and AWS-VPC had no Internet Gateway, so nothing on the internet could reach it anyway. I noted its private IPv4 address for testing later.
Part 2: Building the Simulated Data Center
1. Created DC-VPC
I switched the console to US East (N. Virginia) and created a VPC named DC-VPC with the CIDR block 192.168.0.0/16. This VPC played the role of the company's on-premises network. Its range didn't overlap with AWS-VPC's 10.0.0.0/16, which was required so each side could route to the other.
2. Created and attached an Internet Gateway
I created an Internet Gateway and attached it to DC-VPC. The VPN server needed internet access, because the IPsec tunnel to AWS travels over the public internet.
3. Created two subnets
| Subnet | CIDR | Purpose |
|---|---|---|
| Public subnet | 192.168.0.0/24 | Holds VPN-Server, the data center's VPN router |
| Private subnet | 192.168.100.0/24 | Holds EC2-B, an internal application server |
4. Created route tables
| Route table | Associated with | Routes |
|---|---|---|
| Public route table | Public subnet | 192.168.0.0/16 → local, 0.0.0.0/0 → Internet Gateway |
| Private route table | Private subnet | 192.168.0.0/16 → local |
5. Launched VPN-Server
| Setting | Value |
|---|---|
| AMI | Amazon Linux |
| VPC / Subnet | DC-VPC / public subnet |
| Auto-assign public IP | Enable |
Security group inbound rules:
| Type | Protocol | Port | Source | Reason |
|---|---|---|---|---|
| SSH | TCP | 22 | 0.0.0.0/0 | Lets me SSH in from my computer's public IP to install and configure LibreSwan. |
| All ICMP - IPv4 | ICMP | All | 0.0.0.0/0 | Lets me ping VPN-Server to check it's reachable, and lets EC2-B's pings to AWS-VPC pass through it. As a router, VPN-Server's security group checks all traffic passing through it, not just traffic meant for it. |
No rules were needed for the IPsec tunnel (UDP 500 and 4500), because VPN-Server starts the tunnel itself and the security group automatically allows AWS's replies.
I noted its public IP, since AWS would need it to know where the data center's end of the VPN was.
6. Launched EC2-B
| Setting | Value |
|---|---|
| AMI | Amazon Linux |
| VPC / Subnet | DC-VPC / private subnet |
| Auto-assign public IP | Disable |
Security group inbound rules:
| Type | Protocol | Port | Source | Reason |
|---|---|---|---|---|
| SSH | TCP | 22 | 192.168.0.0/16 | EC2-B has no public IP, so I SSH in from VPN-Server. That connection comes from VPN-Server's private IP inside DC-VPC, not from my computer. |
| All ICMP - IPv4 | ICMP | All | 10.0.0.0/16 | Lets EC2-A ping EC2-B across the VPN. Not needed for EC2-B pinging EC2-A, since replies to connections EC2-B starts are allowed automatically. |
Part 3: Configuring the AWS VPN Components
1. Created the Virtual Private Gateway
Back in Canada (Central), I went to Virtual private gateways in the VPC console, created one, and then clicked Actions → Attach to VPC and selected AWS-VPC.
A Virtual Private Gateway (VGW) is the AWS end of a VPN connection. It's attached to a VPC much like an Internet Gateway, but it only carries VPN traffic.
2. Created the Customer Gateway
Under Customer gateways, I created one and set its IP address to VPN-Server's public IP.
A Customer Gateway (CGW) is AWS's record of the device on the customer's side of the VPN. In a real company, it would be a physical router or firewall. Here, it was VPN-Server.
3. Created the Site-to-Site VPN connection
Under Site-to-Site VPN connections, I clicked Create VPN connection and set:
| Setting | Value |
|---|---|
| Target gateway type | Virtual private gateway (the one attached to AWS-VPC) |
| Customer gateway | Existing (the one pointing to VPN-Server) |
| Routing options | Static |
| Static IP prefixes | 192.168.0.0/16 |
Static routing meant I told AWS directly which address range was on the data center side. The alternative, dynamic routing, uses the BGP protocol to exchange routes automatically, and needs a router that supports BGP.
AWS created the connection with two tunnels, each with its own AWS public IP, so the VPN can stay up if one tunnel fails.
4. Downloaded the configuration file
Once the VPN connection's state was Available, I clicked Download configuration and chose the Openswan format. LibreSwan is based on Openswan, so it uses the same configuration style. The file contained the tunnel IP addresses, the pre-shared keys, and step-by-step settings for the VPN server.
Part 4: Configuring LibreSwan on VPN-Server
1. Installed LibreSwan
I connected to VPN-Server over SSH and installed LibreSwan, the software that creates the IPsec tunnel:
sudo dnf update -y
sudo dnf install libreswan -y2. Turned on packet forwarding
I opened the system settings file:
sudo nano /etc/sysctl.confand added these lines at the end:
net.ipv4.ip_forward = 1
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.default.accept_source_route = 0
ip_forward = 1let the server pass packets between networks, so it could act as a router instead of only handling its own traffic.rp_filter = 0turned off a check that would drop packets arriving on an unexpected interface, which can happen with tunneled traffic.accept_source_route = 0kept a safety setting that rejects packets trying to choose their own route.
I applied the changes without restarting:
sudo sysctl -pThe command printed the three settings back, confirming they were active.
3. Enabled LibreSwan's extra configuration folder
I opened /etc/ipsec.conf and made sure this line near the bottom wasn't commented out:
include /etc/ipsec.d/*.conf
This told LibreSwan to load any connection files placed in /etc/ipsec.d/, which is where I put the AWS settings.
4. Created the tunnel configuration
I created the connection file:
sudo nano /etc/ipsec.d/aws.confand pasted the Tunnel 1 section from the downloaded configuration file, with a few changes:
conn Tunnel1
authby=secret
auto=start
left=%defaultroute
leftid=VPN_SERVER_PUBLIC_IP
right=AWS_TUNNEL1_PUBLIC_IP
type=tunnel
ikelifetime=8h
keylife=1h
phase2alg=aes128-sha256
ike=aes128-sha256;modp2048
keyingtries=%forever
keyexchange=ike
leftsubnet=192.168.0.0/16
rightsubnet=10.0.0.0/16
dpddelay=10
dpdtimeout=30
dpdaction=restart_by_peer
| Setting | What it means |
|---|---|
left / leftid | The data center side: VPN-Server and its public IP |
right | The AWS side: the public IP of Tunnel 1 |
leftsubnet | The data center's network, DC-VPC's CIDR |
rightsubnet | The AWS network, AWS-VPC's CIDR |
phase2alg / ike | Updated to the stronger encryption settings AWS recommends |
auto=start | Bring the tunnel up automatically when IPsec starts |
dpd... settings | Dead peer detection: restart the tunnel if AWS stops responding |
I also removed the auth=esp line from the downloaded file, since newer LibreSwan versions don't accept it.
5. Added the pre-shared key
I created the secrets file:
sudo nano /etc/ipsec.d/aws.secretsand pasted the line from step 5 of the configuration file, in this format:
VPN_SERVER_PUBLIC_IP AWS_TUNNEL1_PUBLIC_IP: PSK "PRE_SHARED_KEY"
The pre-shared key is a password both ends of the tunnel use to prove who they are.
6. Started IPsec
sudo systemctl start ipsec.service
sudo systemctl status ipsec.serviceThe status showed active (running). A running service doesn't guarantee the tunnel works, though, so I tested connectivity next.
Part 5: Routing Traffic Through the VPN
1. First ping test
From VPN-Server, I pinged EC2-A's private IP:
ping EC2_A_PRIVATE_IPIt got no replies. The tunnel could carry traffic to AWS, but EC2-A's route table had no route back to 192.168.0.0/16, so its replies had nowhere to go.
2. Added a route on the AWS side
In Canada (Central), I opened the private subnet's route table in AWS-VPC, clicked Edit routes, and added:
| Destination | Target |
|---|---|
192.168.0.0/16 | Virtual Private Gateway |
I ran the ping again, and this time EC2-A replied. In the VPN connection's Tunnel details tab, Tunnel 1 now showed Up. Tunnel 2 stayed Down, because I had only configured Tunnel 1 on VPN-Server.
3. Turned off Source/Destination Check on VPN-Server
At this point, only VPN-Server itself could reach EC2-A. For EC2-B to use the VPN, VPN-Server had to forward other instances' traffic.
By default, EC2 drops any traffic passing through an instance that isn't addressed to or from that instance. That's the Source/Destination Check. In US East (N. Virginia), I selected VPN-Server, clicked Actions → Networking → Change source/destination check, checked Stop, and saved.
4. Added a route on the data center side
I opened DC-VPC's private route table, clicked Edit routes, and added:
| Destination | Target |
|---|---|
10.0.0.0/16 | Instance → VPN-Server |
Now any traffic from the private subnet headed for AWS-VPC would go to VPN-Server, which would send it through the tunnel.
Part 6: Verifying End-to-End Connectivity
1. Connected to EC2-B through VPN-Server
EC2-B had no public IP, so I reached it through VPN-Server. On VPN-Server, I created a key file:
nano key.pempasted in the contents of EC2-B's private key, saved it, and locked down its permissions:
chmod 400 key.pemThen I connected to EC2-B using its private IP:
ssh -i key.pem ec2-user@EC2_B_PRIVATE_IP2. Pinged EC2-A from EC2-B
From EC2-B, I pinged EC2-A's private IP:
ping EC2_A_PRIVATE_IPEC2-A replied. Traffic from a private server in the simulated data center was traveling through VPN-Server, across the encrypted IPsec tunnel, through the Virtual Private Gateway, and into a private subnet in a different region, without either private instance having a public IP.
Why the Lab Was Set Up This Way
Why VPN-Server needed a public IP
The VPN tunnel runs over the public internet, so both ends needed an address the other end could reach. AWS's tunnel endpoints already had public IPs. To build the tunnel, VPN-Server and AWS exchanged packets across the internet, first to agree on encryption keys (IKE) and then to carry the encrypted traffic (IPsec). The customer gateway told AWS where to send its side of that conversation, and the only thing it stored was VPN-Server's public IP. Without one, the tunnel could never have formed.
Only VPN-Server needed a public IP. EC2-A and EC2-B stayed private, because their traffic traveled inside the encrypted tunnel between the two public endpoints. That's why VPN-Server was in the public subnet with a route to the Internet Gateway, while EC2-B stayed in the private subnet.
The public IP from Auto-assign public IP changes whenever an instance is stopped and started. If that had happened, the customer gateway would have kept pointing to the old address and the tunnel would have broken. In a real setup, VPN-Server would get an Elastic IP so its address never changes, the same way a physical VPN router in a data center gets a static public IP from its internet provider.
What VPN-Server did
VPN-Server was the data center's gateway for all traffic to and from AWS, in both directions.
| Direction | What happened |
|---|---|
| AWS → data center | The packet arrived encrypted through the tunnel. LibreSwan decrypted it, saw it was for EC2-B (192.168.100.x), and VPN-Server forwarded it over DC-VPC's local network. |
| Data center → AWS | EC2-B sent a packet to 10.0.0.x. The private route table's 10.0.0.0/16 → VPN-Server route sent it to VPN-Server, which encrypted it and sent it through the tunnel. |
That gave it two jobs at once:
- VPN endpoint: encrypting and decrypting traffic at the data center's end of the tunnel.
- Router: passing traffic between the tunnel and the servers behind it.
Both jobs depended on IP forwarding being turned on in Linux and the Source/Destination Check being turned off in EC2. Replies took the same path in reverse, which is why the private route table's route back to VPN-Server was just as important as the route to AWS. On the AWS side, the Virtual Private Gateway played the matching role.
Where the AWS VPN resources lived
The customer gateway was created in Canada (Central), even though VPN-Server was in N. Virginia. A customer gateway isn't a device that runs anywhere. It's a record on the AWS side that describes the other end of the VPN. Since the VPN connection links the virtual private gateway to the customer gateway, and AWS only links resources in the same region, all three AWS VPN resources had to be in Canada (Central) with AWS-VPC:
- Virtual private gateway
- Customer gateway
- Site-to-Site VPN connection
Nothing VPN-related was created in N. Virginia. That side was only DC-VPC, its subnets, and VPN-Server running LibreSwan, standing in for a real data center that AWS wouldn't otherwise know about.
Which IPs went where
I never set an IP for the virtual private gateway. AWS assigned the public IPs to the VPN connection's tunnels, shown as Outside IP address on the connection's Tunnel details tab and in the downloaded configuration file.
| IP | Belongs to | Used in |
|---|---|---|
| Tunnel 1 outside IP | AWS's end of the VPN | right= in aws.conf, and aws.secrets |
VPN-Server's public IP | The data center's end | The customer gateway, leftid= in aws.conf, and aws.secrets |
Inside tunnel IPs (169.254.x.x) | The inside of the tunnel | Only needed for dynamic (BGP) routing, so not used here |
Each tunnel has its own pre-shared key, so the key in aws.secrets had to come from the same tunnel as the IP.
Why VPN-Server's rules allowed SSH and ICMP from anywhere
VPN-Server had two inbound rules, each covering a different kind of traffic reaching it:
Rule on VPN-Server | Allows | Needed for |
|---|---|---|
SSH from 0.0.0.0/0 | Logins from the internet | Me setting up the VPN from my computer |
All ICMP - IPv4 from 0.0.0.0/0 | Pings from anywhere | Testing VPN-Server, and letting EC2-B's pings pass through it |
The SSH rule. I set up the whole VPN by SSHing into VPN-Server from my computer: installing LibreSwan, editing sysctl.conf and aws.conf, and starting IPsec. That connection came from my home public IP, so the rule had to allow a source on the internet. 0.0.0.0/0 worked but was wider than necessary. Setting the source type to My IP would have allowed only my computer.
The ICMP rule. This rule did two jobs:
- Pinging
VPN-Serveritself. I could ping its public IP from my computer to check it was reachable. - Letting
EC2-B's pings pass through. WhenEC2-BpingedEC2-A, the packet went toVPN-Serverfirst, before it was encrypted. It arrived atVPN-Server's network interface as an ordinary ICMP packet from192.168.100.x, and the security group checked it like any other inbound traffic. Without this rule, the ping would have been dropped atVPN-Serverbefore it ever reached the tunnel.
Because VPN-Server acted as a router, its security group applied to all traffic passing through it, not just traffic meant for it. In a real setup, if EC2-B also needed HTTPS or SSH to reach EC2-A, VPN-Server's security group would need inbound rules for those protocols from DC-VPC too.
Why no rules for the tunnel itself. IPsec uses UDP ports 500 and 4500, but VPN-Server didn't need inbound rules for them. It started the tunnel itself (auto=start), so AWS's replies were allowed automatically by the stateful security group. Those rules would only be needed if AWS had to start the connection.
A tighter version of VPN-Server's rules:
| Type | Port | Source | Why |
|---|---|---|---|
| SSH | 22 | My IP | Me managing the server |
| All ICMP - IPv4 | All | 192.168.0.0/16 | DC-VPC servers' traffic passing through to AWS |
| All ICMP - IPv4 | All | 10.0.0.0/16 | Pings from AWS-VPC arriving at VPN-Server itself |
Why EC2-A only allowed ICMP from 192.168.0.0/16
EC2-A's single inbound rule, All ICMP - IPv4 from 192.168.0.0/16, let it answer pings from anywhere in DC-VPC, covering both VPN-Server (192.168.0.x) and EC2-B (192.168.100.x), and nothing else.
The source had to be a private range because traffic through a VPN keeps its original address. When EC2-B pinged EC2-A, the packet was encrypted, sent through the tunnel, and decrypted at the virtual private gateway, but its source stayed EC2-B's private IP the whole way. Unlike an Internet Gateway or NAT Gateway, nothing translated it.
The lab only needed ICMP because ping was the simplest way to prove the VPN worked. In a real setup, data center servers would need to reach EC2-A for actual work, so I'd add one rule for each kind of traffic it should accept, as narrowly as possible:
| Need | Type | Port | Source |
|---|---|---|---|
Web app or API on EC2-A | HTTPS | 443 | 192.168.100.0/24, the app servers' subnet |
Admins managing EC2-A | SSH | 22 | A specific admin machine, e.g. 192.168.100.50/32 |
EC2-A as a MySQL database | MYSQL/Aurora | 3306 | Only the app servers that use it |
| Connectivity checks | All ICMP - IPv4 | All | 192.168.0.0/16 |
The VPN itself doesn't restrict any traffic. Once the tunnel and routes are in place, the security groups (and any custom network ACLs) on each side decide exactly what's allowed across it. If EC2-A also needed to start connections to the data center, EC2-B's security group would need matching inbound rules too.
Why EC2-B's rules allowed SSH from inside DC-VPC
EC2-B had two inbound rules, each letting in one kind of traffic from one specific place:
Rule on EC2-B | Allows | Needed for |
|---|---|---|
SSH from 192.168.0.0/16 | Logins from inside DC-VPC | Me reaching EC2-B through VPN-Server |
All ICMP - IPv4 from 10.0.0.0/16 | Pings from AWS-VPC | EC2-A pinging EC2-B across the VPN |
The SSH rule. EC2-B had no public IP, so I couldn't SSH into it from my computer. VPN-Server was the only machine in DC-VPC reachable from the internet, so I logged in to it first and connected to EC2-B from there. That second connection came from VPN-Server's private IP (192.168.0.x), not from my computer's public IP, so the SSH rule had to allow a source inside DC-VPC. For that hop, VPN-Server was acting as a bastion host as well as the VPN router.
192.168.0.0/16 was wider than necessary, since it allowed SSH from anywhere in DC-VPC. A tighter rule would allow only the public subnet (192.168.0.0/24), or only VPN-Server's private IP with /32.
The ICMP rule. 10.0.0.0/16 is AWS-VPC's range, so this rule let EC2-A ping EC2-B. Like EC2-A's rule, its source was a private range because traffic through the VPN keeps its original address.
This rule wasn't what made my test work, though. In the lab, EC2-B pinged EC2-A. Since EC2-B started that connection, EC2-A needed an inbound rule to accept it, but the replies coming back to EC2-B were allowed automatically because security groups are stateful. EC2-B's ICMP rule only matters when the ping goes the other way, from EC2-A to EC2-B, so it makes the test work in both directions.
Why I tested from EC2-B instead of VPN-Server
SSHing into EC2-B was how I got a terminal on it so I could run the real test from there.
Pinging EC2-A from VPN-Server only proved the tunnel worked, since VPN-Server was the end of the tunnel itself. The point of a Site-to-Site VPN is for ordinary servers in the data center to reach AWS, and EC2-B played that role. When its ping succeeded, it proved the whole chain worked:
EC2-B's route table sent AWS-bound traffic toVPN-Server.VPN-Serverforwarded it, which depended on IP forwarding being on and the Source/Destination Check being off.- The tunnel carried it to AWS.
- The replies found their way back along the same path.
If any of those steps had been missing, VPN-Server's own ping would still have worked, but EC2-B's would have failed.
Copying the key onto VPN-Server wasn't the only way to reach EC2-B. An SSH jump from my computer would have avoided putting the key on the server:
ssh -i KEY_NAME.pem -J ec2-user@VPN_SERVER_PUBLIC_IP ec2-user@EC2_B_PRIVATE_IPAn EC2 Instance Connect Endpoint in DC-VPC would also have worked, like the one I used to reach the database server in the private subnet lab.
Why a VPN instead of VPC peering
The simulated data center was really just a second VPC, so in practice the two would be connected with VPC peering: it's simpler, needs no VPN server, stays on AWS's network, and has no hourly charge. The lab used a Site-to-Site VPN because it was standing in for a network outside AWS, like an office or data center, which can't be peered with.
What I Learned
- An AWS Site-to-Site VPN connects a VPC to an on-premises network through an encrypted IPsec tunnel over the public internet.
- The AWS end is a Virtual Private Gateway, attached to the VPC. The customer end is described to AWS as a Customer Gateway, which points to the public IP of the customer's VPN device.
- Every VPN connection has two tunnels to different AWS endpoints for redundancy. In production, both would be configured so the connection survives if one goes down.
- Static routing means listing the remote network's CIDR by hand. Dynamic routing uses BGP to share routes automatically.
- The two networks need non-overlapping CIDR blocks (
10.0.0.0/16and192.168.0.0/16), or routing between them wouldn't work. - A working tunnel isn't enough. Both sides need routes: AWS-side traffic to
192.168.0.0/16must go to the VGW, and data-center-side traffic to10.0.0.0/16must go to the VPN device. - An EC2 instance acting as a router needs IP forwarding turned on in Linux and its Source/Destination Check turned off in EC2. Otherwise it drops traffic that isn't addressed to itself.
- A route table entry can target an EC2 instance, which is how the private subnet's traffic was sent to
VPN-Server. - Compared with VPC peering, a Site-to-Site VPN is for connecting to networks outside AWS, like an office or data center. Peering connects two VPCs over AWS's own network without encryption overhead.
Notes
VPN-Serverstarted the tunnel itself (auto=start), so its security group didn't need inbound rules for IPsec. If AWS had to start the connection, the security group would need to allow UDP 500 and UDP 4500 (IKE and IPsec NAT traversal) from the tunnel IPs.- Copying
EC2-B's private key ontoVPN-Serverwas fine for a lab, but it's risky in practice. SSH's jump host feature (ssh -J) would avoid putting the key on the server. - The downloaded configuration file and
aws.secretscontain the pre-shared keys. They shouldn't be shared, screenshotted into documents, or committed to a repository.
Cleanup
This lab created resources in two regions, and several of them are billed by the hour: the Site-to-Site VPN connection, all three EC2 instances, and VPN-Server's public IPv4 address. I deleted everything in this order:
1. Deleted the VPN connection (Canada Central)
Under Site-to-Site VPN connections, I selected the connection, clicked Actions → Delete VPN connection, and confirmed. This is the most important step, since the VPN connection is billed every hour it exists, whether or not traffic is flowing.
2. Deleted the Customer Gateway (Canada Central)
Once the VPN connection showed Deleted, I went to Customer gateways, selected the gateway, and clicked Actions → Delete customer gateway.
3. Detached and deleted the Virtual Private Gateway (Canada Central)
Under Virtual private gateways, I selected the gateway, clicked Actions → Detach from VPC, waited for it to show Detached, then clicked Actions → Delete virtual private gateway.
4. Terminated the instances
In Canada (Central), I terminated EC2-A. In US East (N. Virginia), I terminated VPN-Server and EC2-B. I waited until all three showed Terminated.
5. Deleted the security groups
In each region, I deleted the security groups created for the instances.
6. Deleted the data center network (N. Virginia)
- Detached the Internet Gateway from
DC-VPCand deleted it. - Deleted the public and private subnets.
- Deleted the two custom route tables.
- Deleted
DC-VPC.
7. Deleted the AWS network (Canada Central)
I deleted the private subnet, its custom route table, and AWS-VPC.
8. Confirmed nothing was left
In both regions, I checked that Site-to-Site VPN connections, Virtual private gateways, Customer gateways, Instances, and Your VPCs didn't show anything from this lab. I made sure not to delete the default VPC in either region.