Skip to content

terraform-ibm-modules/terraform-ibm-site-to-site-vpn

Site to Site VPN Module

Graduated (Supported) pre-commit latest release Renovate enabled semantic-release

This module automates the provisioning of a site-to-site VPN. For more information, see About site-to-site VPN in the IBM Cloud docs.

Overview

terraform-ibm-site-to-site-vpn

This Terraform module provisions a complete Site‑to‑Site VPN solution on IBM Cloud VPC, including VPN gateways, connections, policies, routing, and (optional) route advertisement.

For more information refer here

Key Components

VPN Gateway

  • Creates the VPN gateway instance in specified subnet.
  • Supports both policy-based and route-based VPN configurations.
  • High availability with multiple gateway members across zones.
  • Public IP address automatically assigned for external connectivity.

VPN Policies

IKE Policy :

  • Internet Key Exchange policy for Phase 1 negotiation.
  • Configurable authentication algorithms (SHA-1, SHA-256, SHA-384, SHA-512).
  • Configurable encryption algorithms (AES-128, AES-192, AES-256, 3DES).
  • Configurable Diffie-Hellman groups (2, 5, 14, 15, 16, 17, 18, 19, 20, 21).
  • IKE version support (IKEv1, IKEv2).

IPSec Policy :

  • Internet Protocol Security policy for Phase 2 negotiation.
  • Configurable authentication and encryption algorithms.
  • Perfect Forward Secrecy (PFS) support.
  • Use custom policy if default does not meet peer requirements.

Note:

  • When using existing policy IDs (both IKE and IPSec), ensure that the policy resides in the same region as the VPN Gateway.
  • Within a given region, policy names must be unique. Two policies with the same name cannot coexist in the same region.

VPN Connections

  • Establishes IPSec tunnels between local and peer gateways.
  • Supports multiple connections per gateway for redundancy.
  • Dead Peer Detection (DPD) configuration.
  • Local and peer subnet definitions.

Route Management

  • Custom routes in VPC routing tables for directing traffic through VPN tunnels.
  • Route advertisement capabilities for dynamic routing.
  • Integration with VPC routing tables.
  • Support for both static and dynamic routing.

Important Considerations

Network specific

  • VPC must be created and configured before deploying the VPN gateway.
  • Subnets must exist in the target zones where VPN gateways will be deployed.
  • Local and peer network CIDR blocks must not overlap.
  • Ensure proper network segmentation and IP address planning.
  • Verify that the peer VPN gateway supports IPSec protocols.

Security specific

  • Pre-shared key (PSK) must be configured and shared between both endpoints.
  • IKE and IPSec policies must be compatible between local and peer gateways.
  • Proper authentication methods must be established.
  • Security groups and NACLs must allow VPN traffic.

Please refer Planning considerations for VPN gateways for more information.

Known Limitations

  • IBM permits only one route‑based VPN gateway per zone per VPC. For zone fault tolerance, deploy one VPN gateway per zone.
  • VPN gateway names must be unique within the VPC.
  • Gateway requires /28 subnet and cannot share with other VPC.
  • If peer VPN gateway lacks a public IP, use FQDN identity in VPC.
  • Peer subnets of a VPN gateway connection cannot overlap.
  • Peer address type is immutable — once set as FQDN or IP, it cannot be changed.
  • Route-based mode allows distribute_traffic = true to enable active‑active tunnels; policy‑based does not.
  • If peer is behind NAT, use establish_mode = "peer_only" and supply FQDN and identity overrides because identities must match expected values on negotiation.
  • Creating a route in an ingress routing table with a VPN gateway connection as the next hop is not supported.

Please refer Known issues for VPN gateways for more information.
Tunnel status may remain DOWN for a while after deployment, with delays observed before it shows as UP.

Usage

terraform {
  required_version = ">= 1.9.0"
  required_providers {
    ibm = {
      source  = "IBM-Cloud/ibm"
      version = "X.Y.Z"  # Lock into a provider version that satisfies the module constraints
    }
  }
}

locals {
  region = "us-south"
  ike_policy_config = {
    name                     = "xxx-ike-policy" # Name of the IKE Policy
    authentication_algorithm = "sha256" # Choose the relevant authentication algorithm
    encryption_algorithm     = "aes256" # Choose the relevant encryption algorithm
    dh_group                 = 14 # Provide valid Diffie-Hellman group.
  }
  ipsec_policy_config = {
    name                     = "xxx-ipsec-policy" # Name of the IPSec Policy
    encryption_algorithm     = "aes256" # Choose the relevant encryption algorithm
    authentication_algorithm = "sha256" # Choose the relevant authentication algorithm
    pfs                      = "group_14" # Perfect Forward Secrecy (PFS) protocol value
  }
}

provider "ibm" {
  ibmcloud_api_key = "XXXXXXXXXX"  # replace with apikey value
  region           = local.region
}

module "site_to_site_vpn" {
  source                         = "terraform-ibm-modules/site-to-site-vpn/ibm"
  version                        = "X.X.X" # Replace "X.X.X" with a release version to lock into a specific release
  resource_group_id              = "65xxxxxxxxxxxxxxxa3fd"
  create_vpn_gateway             = true
  tags                           = var.tags
  vpn_gateway_name               = "xxxxx" # Name of the VPN Gateway
  vpn_gateway_subnet_id          = "s..12" # Subnet id where VPN Gateway will be created
  vpn_gateway_mode               = "route" # Can be route or policy

  # Policies
  create_ike_policy   = true
  create_ipsec_policy = true
  ike_policy_config   = local.ike_policy_config
  ipsec_policy_config = local.ipsec_policy_config

  # VPN Connections
  vpn_connections = [
    {
      name         = "xxx-vpn-conn" # VPN Connection name
      peer_address = "X.X.X.X" # Remote VPN gateway IP
      preshared_key = "XXXXXX"

      # Peer Configuration (remote VPN gateway)
      peer_config = [
        {
          address = "X.X.X.X" # Remote Gateway IP address
          cidrs = [X.X.X.X] # Provide CIDRs (Required for Policy based VPN)
          ike_identity = [
            {
              type  = "ipv4_address"
              value = "X.X.X.X" # Remote Gateway IP address
            }
          ]
        }
      ]
      # Local Configuration
      local_config = [
        {
          cidrs = ["10.10.0.0/16"]  # Local VPC CIDRs
          # Minimum of 2 IKE Identities are required for Route based VPN and atmost 1 for Policy based VPN
          ike_identities = [
            {
              type  = "ipv4_address"
              value = module.vpn_gateway.vpn_gateway_public_ip # Use the VPN gateway id
            },
            {
              type  = "ipv4_address"
              value = module.vpn_gateway.vpn_gateway_public_ip # Use the VPN gateway id
            }
          ]
        }
      ]
    }
  ]

  # Routing table and Routes creation
  create_route_table               = true
  routing_table_name               = "xxx-rt" # Name of Routing Table
  accept_routes_from_resource_type = ["vpn_gateway"]
  route_attach_subnet                    = true
  route_subnet_id                        = "s...123" # Subnet id where VPN Gateway is created

  # Add routes
  create_routes = true
  vpc_id        = "vpc-xxxx" # Provide VPC Id.
  routes = [
    {
      name             = "example-vpn-route-1"
      vpn_gateway_name = "xxxxx" # Name of the VPN Gateway
      zone             = "zone-1"
      next_hop         = null # This will be resolved using Connection name
      vpn_connection_name = "xxxx" # Name of the VPN Connection
      destination      = "X.X.X.X" # Provide Remote CIDR
    }
  ]
}

State Migration Guide:

Please refer the state migration document for more information.

Required IAM access policies

You need the following permissions to run this module.

  • IAM services
    • VPC Infrastructure services
      • Editor platform access
    • No service access
      • Resource Group <your resource group>
      • Viewer resource group access

Requirements

Name Version
terraform >= 1.9.0
ibm >= 1.80.3, < 2.0.0
time >= 0.9.1, < 1.0.0

Modules

Name Source Version
vpn_policies ./modules/vpn_policies n/a
vpn_routes ./modules/vpn_routing n/a

Resources

Name Type
ibm_is_vpn_gateway.vpn_gateway resource
ibm_is_vpn_gateway_connection.vpn_site_to_site_connection resource
time_sleep.wait_for_gateway_creation resource

Inputs

Name Description Type Default Required
accept_routes_from_resource_type List of resource types allowed to create routes in this table. list(string) [] no
advertise_routes_to Ingress sources to which routes should be advertised. list(string) [] no
create_route_table Whether to create a new route table. bool false no
create_routes Whether to create VPN routes. bool false no
create_vpn_gateway Whether to create a new VPN Gateway. Set to false to use an existing gateway. bool true no
existing_route_table_id ID of existing route table to use. string null no
existing_vpn_gateway_id ID of existing VPN Gateway to use. Required if create_vpn_gateway is false and vpn_gateway_name is not provided. string null no
resource_group_id The ID of the resource group to use where you want to create the VPN gateway. string n/a yes
route_attach_subnet Whether to attach subnet to the VPN route table. bool false no
route_direct_link_ingress Allow routing from Direct Link. bool false no
route_internet_ingress Allow routing from Internet. bool false no
route_subnet_id Subnet ID to attach to the routing table. string null no
route_transit_gateway_ingress Allow routing from Transit Gateway. bool false no
route_vpc_zone_ingress Allow routing from other zones within the VPC. bool false no
routes List of routes to create.
list(object({
name = string
zone = string
destination = string
next_hop = string
action = optional(string, "deliver")
advertise = optional(bool, false)
priority = optional(number, 2)
vpn_connection_name = optional(string, null)
}))
[] no
routing_table_name Name of the routing table to create. string null no
tags List of Tags for the resource created list(string) null no
vpc_id VPC ID where routes will be created. string null no
vpn_connections List of VPN connections to attach to the VPN gateway.
list(object({
name = string # Name of the VPN connection
preshared_key = string # Required to specify the authentication key of the VPN gateway for the network outside your VPC. Learn More
is_admin_state_up = optional(bool, false) # Flag to control the administrative state of the VPN gateway connection. If set to false (default), the connection is shut down. Set to true to enable the connection.
establish_mode = optional(string, "bidirectional") # Determines IKE negotiation behavior for the VPN gateway connection. Use 'bidirectional' to allow both sides to initiate IKE negotiations and rekeying. Use 'peer_only' to restrict initiation and rekeying to the peer side.
enable_distribute_traffic = optional(bool, false) # Flag for route-based VPN gateway connections to control traffic distribution across active tunnels. When true, traffic is load-balanced otherwise, it flows through the tunnel with the lower public IP.
dpd_action = optional(string, "restart") # Action to perform when the peer is unresponsive. Possible values are - 'restart', 'clear', 'hold', or 'none'.
dpd_check_interval = optional(number, 2) # Interval in seconds between dead peer detection checks for peer responsiveness.
dpd_max_timeout = optional(number, 10) # Time in seconds to wait before considering the peer unreachable.

# Policy configuration per connection

# IKE Policy
create_ike_policy = optional(bool, false) # Flag to create new IKE policy.
existing_ike_policy_id = optional(string, null) # ID of existing IKE policy to use (mutually exclusive with create_ike_policy)

ike_policy_config = optional(object({
name = string
authentication_algorithm = string # sha256, sha384, sha512
encryption_algorithm = string # aes128, aes192, aes256
dh_group = number # 14-24, 31
ike_version = optional(number, 2)
key_lifetime = optional(number, 28800)
}), null) # Provide config only if create_ike_policy is true

# IPSec policy
create_ipsec_policy = optional(bool, false) # Flag to create new IPSec policy
existing_ipsec_policy_id = optional(string, null) # ID of existing IPSec policy to use (mutually exclusive with create_ipsec_policy)

ipsec_policy_config = optional(object({
name = string
encryption_algorithm = string # aes128, aes192, aes256, aes128gcm16, aes192gcm16, aes256gcm16
authentication_algorithm = string # sha256, sha384, sha512, disabled
pfs = string # disabled, group_2, group_5, group_14
key_lifetime = optional(number, 3600)
}), null) # Provide config only if create_ipsec_policy is true

# Peer and Local Configuration
peer_config = optional(list(object({
address = optional(string)
fqdn = optional(string)
cidrs = optional(list(string), [])
ike_identity = list(object({
type = string
value = optional(string)
}))
})), [])

local_config = optional(list(object({
cidrs = optional(list(string), [])
ike_identities = list(object({
type = string
value = optional(string)
}))
})), [])
}))
[] no
vpn_gateway_mode Specifies the VPN configuration mode for IBM Cloud VPN for VPC. Use 'route' for a static, route-based IPsec tunnel or 'policy' for a policy-based tunnel to connect your VPC to another private network. string "route" no
vpn_gateway_name Name of the VPN gateway. Only required if creating a new VPN Gateway. string null no
vpn_gateway_subnet_id The ID of the subnet where the VPN gateway will reside in. string null no

Outputs

Name Description
vpn_connection_policies IKE and IPSec policy details.
vpn_gateway_connection_ids Map of VPN gateway connection IDs, keyed by connection name.
vpn_gateway_connection_modes Map of VPN gateway connection modes: either 'policy' or 'route'.
vpn_gateway_connection_statuses Map of current statuses for each VPN gateway connection, either 'up' or 'down'.
vpn_gateway_crn CRN of the VPN gateway.
vpn_gateway_id ID of the VPN gateway.
vpn_gateway_members List of VPN gateway members.
vpn_gateway_public_ip The IP address assigned to the VPN gateway. Learn more
vpn_gateway_public_ip_2 The Second Public IP address assigned to the VPN gateway member. Learn more
vpn_gateway_status Overall health state of the VPN gateway. Refer here for more information.
vpn_gateway_vpc_info Information about the VPC associated with the VPN gateway.
vpn_routes VPN Routing information.
vpn_status_reasons Map of status reasons explaining the current connection state per connection.

Contributing

You can report issues and request features for this module in GitHub issues in the module repo. See Report an issue or request a feature.

To set up your local development environment, see Local development setup in the project documentation.