
Establish a Site-to-Site VPN between AWS and your on-premise network using a Windows Server as your router - no third-party networking equipment required. Use Terraform to create AWS VPN resources, then configure Windows Server with Routing and Remote Access (RRAS) for IKEv2 VPN connectivity.
Introduction
Do you rely solely on Windows servers in your environment and lack access to third-party networking equipment? Are you looking to establish a site-to-site VPN connection to your AWS environment but unsure how to do so with your existing setup? If so, fear not, because it's possible to achieve this with just a Windows Server acting as a router. In this blog post, we'll guide you through the steps needed to establish a secure connection between your LAN and AWS, enabling bidirectional communication between your servers and the cloud platform.
Prerequisites
- Windows Server to be used as a router
- I am using Server Operating System 2019
- An AWS account
- An AWS IAM object with adequate rights
- An AWS VPC with an EC2 instance for testing connectivity
- I am using Terraform to create all AWS objects
The Windows Server must have a public IP address accessible from the internet. Ensure your firewall allows UDP ports 500 and 4500 for IKE negotiation, as well as ESP protocol (IP protocol 50) for encrypted traffic.
AWS Tasks
Create VPC
resource "aws_vpc" "vpc" {
cidr_block = "192.168.0.0/16"
}
Create Subnets
#Private Subnet in AZ-A
resource "aws_subnet" "prisub1" {
cidr_block = 192.168.0.0/24
vpc_id = aws_vpc.vpc.id
availability_zone = data.aws_availability_zones.available.names[0]
}
#Private Subnet in AZ-B
resource "aws_subnet" "prisub2" {
cidr_block = 192.168.1.0/24
vpc_id = aws_vpc.vpc.id
availability_zone = data.aws_availability_zones.available.names[1]
}
Create and Attach Route Table
resource "aws_route_table" "routetableprivate" {
vpc_id = aws_vpc.vpc.id
}
# Associate both subnets to this RT, then enable VGW route propagation
resource "aws_vpn_gateway_route_propagation" "routepropagation" {
vpn_gateway_id = aws_vpn_gateway.vgw.id
route_table_id = aws_route_table.routetableprivate.id
}
The route propagation block is the part that matters, it pushes the on-prem CIDR into this route table once the tunnel comes up. Without it your subnets won't know how to reach the LAN even when the VPN says it's connected.
Create and Attach Virtual Private Gateway
resource "aws_vpn_gateway" "vgw" {
vpc_id = aws_vpc.vpc.id
}
resource "aws_vpn_gateway_attachment" "vgw_attachment" {
vpc_id = aws_vpc.vpc.id
vpn_gateway_id = aws_vpn_gateway.vgw.id
}
Create Customer Gateway
resource "aws_customer_gateway" "cgw" {
bgp_asn = 65000
ip_address = "20.85.247.126" #this is your public IP address you will obtain from your Windows Server
type = "ipsec.1"
}
Create Site to Site VPN
resource "aws_vpn_connection" "vpn" {
vpn_gateway_id = aws_vpn_gateway.vgw.id
customer_gateway_id = aws_customer_gateway.cgw.id
type = "ipsec.1"
static_routes_only = true
tunnel1_preshared_key = "abc123xyz987" # use this same PSK on the Windows side
}
# Add a static route for the on-prem LAN CIDR, then output tunnel1_address
# so you know which AWS endpoint to point Windows RRAS at.
Use static_routes_only = true because Windows RRAS doesn't speak BGP cleanly. The tunnel1_address output is the AWS-side IP you'll feed into Add-VpnS2SInterface on the Windows server.
Use static routes for Windows Server RRAS since BGP configuration on Windows is more complex. The static_routes_only = true setting simplifies the setup significantly.
Create Test EC2
I also stood up a small Linux EC2 in one of the private subnets just to have a ping target. The security group accepts all traffic from the on-prem CIDR (10.0.0.0/24) so I can verify the tunnel actually carries traffic both directions. Output the instance's private IP so you can ping it from your LAN once RRAS finishes.
Windows Server Tasks
These tasks can be done via PowerShell.
Install Routing Services
Install-WindowsFeature -Name Routing -IncludeManagementTools
Install-RemoteAccess -VpnType VpnS2S
$tunnel1IP = <input the tunnel IP address terraform outputs>
$psk = 'abc123xyz987'
Add-VpnS2SInterface -Name AWS1 -Destination $tunnel1IP -Protocol IKEv2 -AuthenticationMethod PSKOnly -SharedSecret $psk -IPv4Subnet 192.168.0.0/16:100
restart-service RemoteAccess; start-sleep 15; Connect-VpnS2SInterface -Name AWS1 -PassThru
The IPv4Subnet parameter (192.168.0.0/16:100) specifies the AWS VPC CIDR with a metric of 100. This creates the necessary route for traffic destined to AWS to use the VPN tunnel.
Install and Configure Remote Access with IPSec
(Same PowerShell as above, this section was a duplicate in the original post.)
Verification
As AWS never initiates a connection, you will have to initiate the connection over the Site to Site VPN. An easy way to achieve this is by pinging the test AWS instance we created via Terraform. The private IP address for the EC2 instance has been outputted when running Terraform.
ping a.b.c.d -t #continuous ping; replace a.b.c.d with the private IP address of the test EC2 instance
AWS VPN tunnels go DOWN after 10 seconds of inactivity. Configure a persistent ping or monitoring tool to keep the tunnel alive, or implement Dead Peer Detection (DPD) on your Windows Server.
Confirm Inbound and Outbound connection
Get-NetIPsecQuickModeSA
You should see both, Inbound and Outbound connections from your server to AWS.
To take this one step further, you can set the default gateway for other machines in your LAN to the IP Address of your Windows Server acting as a router to AWS so that you can communicate from our LAN servers to AWS, and vice versa.
Conclusion
With Windows Server Operating Systems, establishing a Site to Site IPSec VPN tunnel to AWS is easy and allows you to efficiently direct traffic through a Windows Server functioning as a router. This solution is ideal for creating a fast and straightforward lab environment that allows you to test Site to Site VPN connectivity and dependencies that rely on a Site to Site VPN connection.
Troubleshooting
| Issue | Possible Cause | Solution |
|---|---|---|
| VPN connection fails to establish | Firewall blocking IPSec ports | Ensure UDP 500 (IKE), UDP 4500 (NAT-T), and IP protocol 50 (ESP) are allowed through your firewall. Check Windows Firewall rules. |
| Tunnel shows connected but no traffic passes | Route not added or incorrect subnet | Verify the IPv4Subnet in Add-VpnS2SInterface matches the AWS VPC CIDR. Check route table with Get-NetRoute. |
| "The remote server did not respond" error | AWS tunnel IP incorrect or not reachable | Verify the tunnel1_address output from Terraform. Test connectivity with Test-NetConnection $tunnel1IP -Port 500. |
| Connection drops after short period | AWS DPD timeout - no traffic keeping tunnel alive | Configure a persistent ping script to the AWS EC2 or use network monitoring. AWS tunnels timeout after 10 seconds of inactivity. |
| Pre-shared key mismatch error | PSK in Windows doesn't match AWS VPN | Verify tunnel1_preshared_key in Terraform matches the -SharedSecret parameter in PowerShell. Keys are case-sensitive. |
Want Help With This?
If you're working on something similar and want a second set of eyes, or you'd like to talk through how this applies to your environment, reach out via the contact form. Happy to help.