This check flags a Virtual Private Gateway (VGW) that exists in your AWS account but is not attached to any VPC. Detached VGWs serve no purpose, clutter your inventory, and can cost money if attached to active VPN connections. Delete the gateway if it is genuinely unused, or re-attach it to the VPC it was meant to serve.
Virtual Private Gateways are one of those resources that quietly accumulate. Someone spins one up for a site-to-site VPN proof of concept, the project gets shelved, and the VGW sits there detached for months. Lensix surfaces these so you can clean them up before they become a billing surprise or an audit finding.
What this check detects
The vpc_unusedvpg check looks at every Virtual Private Gateway in your AWS account and flags any that have no VPC attachment. In AWS terms, a VGW with an empty VpcAttachments list, or one whose attachment state is detached, will trigger this finding.
A Virtual Private Gateway is the AWS-side endpoint for a site-to-site VPN connection or, historically, the anchor for a VPN that terminates into a VPC. It needs to be attached to exactly one VPC to do anything useful. When it sits detached, it is doing nothing but occupying a slot in your account.
Note: A VGW can only be attached to one VPC at a time. If you see several detached gateways, they are often leftovers from VPCs that were deleted or from VPN architectures that were replaced by AWS Transit Gateway.
Why it matters
An unused VGW is not a critical security hole on its own, but it carries real operational and cost risk.
Cost creep
A detached VGW with no VPN connection attached is free. The trap is the gateway that still has a site-to-site VPN connection hanging off it. AWS charges per VPN connection-hour regardless of whether traffic flows, and regardless of whether the VGW is attached to a live VPC. A forgotten VPN connection on an orphaned gateway can quietly bill you every hour, every month, for nothing.
Warning: Site-to-site VPN connections are billed per hour they exist, not per byte transferred. An idle VPN attached to an unused VGW still costs money. Always check for attached VPN connections before assuming a detached gateway is harmless.
Inventory drift and audit noise
Auditors and compliance frameworks expect your cloud inventory to map to a purpose. A detached VGW is an asset with no owner and no function. During a SOC 2 or ISO 27001 review, unexplained networking resources lead to questions you do not want to spend time answering. They also inflate the surface area your team has to reason about during incident response.
Quota exhaustion
AWS limits you to 5 Virtual Private Gateways per region by default. If your team genuinely needs more VGWs and several slots are wasted on detached leftovers, you hit the quota and waste a support ticket raising the limit when deleting dead resources would have solved it.
Accidental reconnection
A detached VGW retains its configuration. If someone re-attaches it to the wrong VPC, you can inadvertently re-establish routing into a network segment that was supposed to be isolated. Stale infrastructure that still works is more dangerous than infrastructure that is clearly gone.
How to fix it
First, decide whether the gateway is genuinely abandoned or simply waiting to be attached. Investigate before you delete.
Step 1: Identify the detached gateways
aws ec2 describe-vpn-gateways \
--filters "Name=attachment.state,Values=detached" \
--query 'VpnGateways[].{ID:VpnGatewayId,State:State,Type:Type,Tags:Tags}' \
--output table
Gateways in the available state with no attachment, or those reporting a detached attachment state, are your candidates.
Step 2: Check for attached VPN connections
Before deleting, make sure no VPN connection is still pointing at the gateway. This is the part that catches people out.
aws ec2 describe-vpn-connections \
--filters "Name=vpn-gateway-id,Values=vgw-0abc123456789def0" \
--query 'VpnConnections[].{ID:VpnConnectionId,State:State}' \
--output table
If a VPN connection comes back, you must delete the connection first, since it is what generates the hourly charge.
Danger: Deleting a VPN connection tears down the tunnel to your on-premises network. If anyone is relying on it, even intermittently, you will cut their connectivity. Confirm ownership and check flow logs before running the next command.
aws ec2 delete-vpn-connection \
--vpn-connection-id vpn-0abc123456789def0
Step 3: Re-attach or delete the gateway
If the VGW should be in use, attach it to the correct VPC instead of deleting it:
aws ec2 attach-vpn-gateway \
--vpn-gateway-id vgw-0abc123456789def0 \
--vpc-id vpc-0123456789abcdef0
If the gateway is truly dead, delete it:
Danger: Deletion is irreversible. A new VGW will have a different ID, which means you would have to update any documentation, route configuration, or on-premises customer gateway references that pointed at the old one. Tag and document before you delete.
aws ec2 delete-vpn-gateway \
--vpn-gateway-id vgw-0abc123456789def0
Console steps
- Open the VPC console and go to Virtual Private Network (VPN) > Virtual Private Gateways.
- Find the gateway with a State of Available and no VPC in the VPC ID column.
- Select it, open the Site-to-Site VPN Connections view, and confirm no connections reference it.
- Choose Actions > Delete virtual private gateway, or Actions > Attach to VPC if it should be in use.
Tip: If you manage VGWs with Terraform or CloudFormation, do not delete them by hand. Removing the resource from your IaC and applying the change keeps state and reality in sync. Manual deletion of a managed resource leaves your Terraform state stale and your next plan confusing.
How to prevent it from happening again
The root cause is almost always lifecycle: a VGW gets created and never gets cleaned up when its purpose ends. Close that gap with tagging, IaC, and policy gates.
Manage VGWs through Terraform
When you define the gateway and its attachment together, deleting the VPC or removing the module also tears down the gateway. The attachment is explicit, so a detached state never silently persists.
resource "aws_vpn_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "prod-vgw"
Owner = "network-team"
Environment = "production"
ManagedBy = "terraform"
}
}
Setting vpc_id directly on the resource means Terraform creates and attaches in one step. There is no window where the gateway exists detached.
Require ownership tags with policy-as-code
Block the creation of any VGW that lacks an owner and environment tag. With Open Policy Agent against a Terraform plan:
package vpc.vgw
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_vpn_gateway"
not resource.change.after.tags.Owner
msg := sprintf("VGW %s must have an Owner tag", [resource.address])
}
An owner tag turns an orphaned gateway from an anonymous mystery into a known item you can route back to a team.
Add a CI gate and a scheduled scan
- Run the OPA check in your pull request pipeline so untagged or detached-by-design gateways never merge.
- Schedule a weekly Lensix scan or a small Lambda that lists detached VGWs and posts them to Slack. Catching drift within days, rather than at audit time, keeps the cleanup trivial.
Tip: Pair this with an AWS Config rule or an EventBridge rule on DeleteVpc events. If a VPC is deleted while a VGW is attached, AWS detaches the gateway automatically and leaves it orphaned. That is the exact moment you want an alert.
Best practices
- Prefer Transit Gateway for hub-and-spoke networking. If you are running multiple VPN connections across several VPCs, a Transit Gateway centralizes the attachments and removes the need for per-VPC VGWs, which cuts down on exactly this kind of orphaned resource.
- Tag every networking resource at creation. Owner, environment, and a ticket reference make cleanup decisions fast and defensible.
- Treat detached as a deletion candidate, not a steady state. A VGW should be attached within minutes of creation. If it has been detached for more than a day, something went wrong.
- Always delete the VPN connection before the gateway. The connection is the billable component, and a dangling connection is worse than a dangling gateway.
- Reconcile IaC state regularly. Run
terraform planon a schedule to catch drift, including resources that were created or deleted outside your pipeline. - Document customer gateway and routing dependencies. Before deleting any VGW, confirm no on-premises device or route table still references it.
Cleaning up an unused Virtual Private Gateway is rarely urgent, which is precisely why it gets ignored. Build the detection into a recurring scan, attach an owner to every gateway you create, and the problem stops recurring.

