Configure IPSec VPN tunnels with terraform in Cloud Director 10.x 1

Configure IPSec VPN tunnels with terraform in Cloud Director 10.x

In my previous blog post, I showed you how to configure IPSec VPN tunnels from within the Cloud Director GUI. But what if you want to automate this repetitive task? In this blog post I will explain how you can achieve this with Terraform. Configuring and maintaining the IPSec VPN tunnels will become more easy this way.

Terraform files

The files mentioned below are the files that are required to achieve this automation task with Terraform.

  • ipsec_vpn_tunnels.tf
  • terraform.tfvars
  • main.tf

ipsec_vpn_tunnels.tf

All IPSec VPN settings we normally configure in the Cloud Director GUI need to be set in the ipsec_vpn_tunnels.tf file. Define a new map for each IPSec VPN tunnel as shown below. This file will eventually be used by the main.tf file to configure the IPSec VPN tunnels based on the information in this file.

More information on the security profile settings can be found on the official vCD terraform provider page.

The example below has some random dummy values.

locals {
  ipsec_vpn_tunnels = {
    to_tenant_1 = {
      ipsec_vpn_description = "IPSEC VPN tunnel 1 - created with Terraform"
      ipsec_vpn_pre_shared_key = "Supersecretpassword"
      ipsec_vpn_local_endpoint_ip_address = "221.201.95.227"
      ipsec_vpn_local_endpoint_networks = "192.168.12.0/24"
      ipsec_vpn_remote_endpoint_ip_address = "87.227.53.36"
      ipsec_vpn_remote_endpoint_networks = "87.227.53.37/32"
      ipsec_vpn_ike_version = "IKE_V2"
      ipsec_vpn_ike_encryption = "AES_256"
      ipsec_vpn_ike_digest = "SHA2_256"
      ipsec_vpn_ike_diffie_hellman_group = "GROUP14"
      ipsec_vpn_ike_association_life_time = 28800
      ipsec_vpn_tunnel_enable_perfect_forward_secrecy = "true"
      ipsec_vpn_tunnel_defragmentation_policy = "COPY"
      ipsec_vpn_tunnel_encryption = "AES_256"
      ipsec_vpn_tunnel_digest = "SHA2_256"
      ipsec_vpn_tunnel_diffie_hellman_group = "GROUP14"
      ipsec_vpn_tunnel_association_life_time = 3600
      ipsec_vpn_dpd_probe_interval = "30"
    },
    to_tenant_2 = {
      ipsec_vpn_description = "IPSEC VPN tunnel 2 - created with Terraform"
      ipsec_vpn_pre_shared_key = "Supersecretpassword"
      ipsec_vpn_local_endpoint_ip_address = "176.5.147.17"
      ipsec_vpn_local_endpoint_networks = "176.5.147.18/32"
      ipsec_vpn_remote_endpoint_ip_address = "222.7.191.76"
      ipsec_vpn_remote_endpoint_networks = "10.0.11.0/24"
      ipsec_vpn_ike_version = "IKE_V2"
      ipsec_vpn_ike_encryption = "AES_256"
      ipsec_vpn_ike_digest = "SHA2_256"
      ipsec_vpn_ike_diffie_hellman_group = "GROUP14"
      ipsec_vpn_ike_association_life_time = 28800
      ipsec_vpn_tunnel_enable_perfect_forward_secrecy = "true"
      ipsec_vpn_tunnel_defragmentation_policy = "COPY"
      ipsec_vpn_tunnel_encryption = "AES_256"
      ipsec_vpn_tunnel_digest = "SHA2_256"
      ipsec_vpn_tunnel_diffie_hellman_group = "GROUP14"
      ipsec_vpn_tunnel_association_life_time = 3600
      ipsec_vpn_dpd_probe_interval = "30"
    }
  }
}

terraform.tfvars

In this file we need to add the information of the Cloud Director tenant so that we are able to talk to the Cloud Director API.

vcd_user = "username"
vcd_pass = "Supersecretpassword"
vcd_url = "https://cloud-director-fqdn/api"
vcd_max_retry_timeout = "60"
vcd_allow_unverified_ssl = true
vcd_org_name = "org-name-here"
vcd_org_vdc = "org-vdc-here"
vcd_vdc_group = "vdcgroup-here"
vcd_edge_name = "edge-name-here"

main.tf

The main.tf file will use the terraform.tfvars variables to connect to the Cloud Director API and will use the ipsec_vpn_tunnels.tf file to create, update and/or delete IPSec VPN tunnels.

# Declaring variables
variable "vcd_user" {}
variable "vcd_pass" {}
variable "vcd_allow_unverified_ssl" {
    default = true
}
variable "vcd_url" {}
variable "vcd_org_name" {}
variable "vcd_org_vdc" {}
variable "vcd_vdc_group" {}
variable "vcd_edge_name" {}
variable "vcd_max_retry_timeout" {
    default = 60
}

terraform {
  required_providers {
    vcd = {
      source = "vmware/vcd"
      version = "3.8.2"
    }
  }
}

# Connection for the VMware Cloud Director Provider
provider "vcd" {
  url      = var.vcd_url
  user     = var.vcd_user
  password = var.vcd_pass
  org      = var.vcd_org_name
  vdc      = var.vcd_org_vdc

  max_retry_timeout    = var.vcd_max_retry_timeout
  allow_unverified_ssl = var.vcd_allow_unverified_ssl

  logging = "true"
}

# Get VDC group
data "vcd_vdc_group" "vdc_group" {
  org  = var.vcd_org_name
  name = var.vcd_vdc_group
}

# Get tenant NSX-T Edge based on vDC group
data "vcd_nsxt_edgegateway" "edge" {
  org      = var.vcd_org_name
  owner_id = data.vcd_vdc_group.vdc_group.id
  name     = var.vcd_edge_name
}

# Maintaining IPSec VPN tunnels
resource "vcd_nsxt_ipsec_vpn_tunnel" "tunnel1" {
  org = var.vcd_org_name

  edge_gateway_id = data.vcd_nsxt_edgegateway.edge.id
  for_each = local.ipsec_vpn_tunnels
  name        = each.key
  description = each.value.ipsec_vpn_description

  pre_shared_key = each.value.ipsec_vpn_pre_shared_key
  local_ip_address = each.value.ipsec_vpn_local_endpoint_ip_address
  local_networks   = [each.value.ipsec_vpn_local_endpoint_networks]
  remote_ip_address = each.value.ipsec_vpn_remote_endpoint_ip_address
  remote_networks   = [each.value.ipsec_vpn_remote_endpoint_networks]

  security_profile_customization {
    ike_version               = each.value.ipsec_vpn_ike_version
    ike_encryption_algorithms = [each.value.ipsec_vpn_ike_encryption]
    ike_digest_algorithms     = [each.value.ipsec_vpn_ike_digest]
    ike_dh_groups             = [each.value.ipsec_vpn_ike_diffie_hellman_group]
    ike_sa_lifetime           = each.value.ipsec_vpn_ike_association_life_time

    tunnel_pfs_enabled           = each.value.ipsec_vpn_tunnel_enable_perfect_forward_secrecy
    tunnel_df_policy             = each.value.ipsec_vpn_tunnel_defragmentation_policy
    tunnel_encryption_algorithms = [each.value.ipsec_vpn_tunnel_encryption]
    tunnel_digest_algorithms     = [each.value.ipsec_vpn_tunnel_digest]
    tunnel_dh_groups             = [each.value.ipsec_vpn_tunnel_diffie_hellman_group]
    tunnel_sa_lifetime           = each.value.ipsec_vpn_tunnel_association_life_time

    dpd_probe_internal = each.value.ipsec_vpn_dpd_probe_interval
  }
}

Example output

When every setting has been configured in the ipsec_vpn_tunnels.tf and terraform.tfvars files, we are now ready to run the terraform plan command.

Terraform will now create the IPSec VPN tunnels.

vkerneladmin@alm-autom01:~/terraform/projects/vCD$ terraform apply
data.vcd_vdc_group.vdc_group: Reading...
data.vcd_vdc_group.vdc_group: Read complete after 0s [id=urn:vcloud:vdcGroup:491c933f-5a34-4a46-956c-5cfa9949ed8e]
data.vcd_nsxt_edgegateway.edge: Reading...
data.vcd_nsxt_edgegateway.edge: Read complete after 1s [id=urn:vcloud:gateway:83e55e3f-de87-4a31-a0b0-d52f6a069a41]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # vcd_nsxt_ipsec_vpn_tunnel.tunnel1["to_tenant_1"] will be created
  + resource "vcd_nsxt_ipsec_vpn_tunnel" "tunnel1" {
      + description        = "IPSEC VPN tunnel 1 - created with Terraform"
      + edge_gateway_id    = "urn:vcloud:gateway:83e55e3f-de87-4a31-a0b0-d52f6a069a41"
      + enabled            = true
      + id                 = (known after apply)
      + ike_fail_reason    = (known after apply)
      + ike_service_status = (known after apply)
      + local_ip_address   = "x.x.x.x"
      + local_networks     = [
          + "192.168.12.0/24",
        ]
      + logging            = false
      + name               = "to_tenant_1"
      + org                = "10000028"
      + pre_shared_key     = (sensitive value)
      + remote_ip_address  = "x.x.x.x"
      + remote_networks    = [
          + "x.x.x.x/32",
        ]
      + security_profile   = (known after apply)
      + status             = (known after apply)
      + vdc                = (known after apply)

      + security_profile_customization {
          + dpd_probe_internal           = 30
          + ike_dh_groups                = [
              + "GROUP14",
            ]
          + ike_digest_algorithms        = [
              + "SHA2_256",
            ]
          + ike_encryption_algorithms    = [
              + "AES_256",
            ]
          + ike_sa_lifetime              = 28800
          + ike_version                  = "IKE_V2"
          + tunnel_df_policy             = "COPY"
          + tunnel_dh_groups             = [
              + "GROUP14",
            ]
          + tunnel_digest_algorithms     = [
              + "SHA2_256",
            ]
          + tunnel_encryption_algorithms = [
              + "AES_256",
            ]
          + tunnel_pfs_enabled           = true
          + tunnel_sa_lifetime           = 3600
        }
    }

  # vcd_nsxt_ipsec_vpn_tunnel.tunnel1["to_tenant_2"] will be created
  + resource "vcd_nsxt_ipsec_vpn_tunnel" "tunnel1" {
      + description        = "IPSEC VPN tunnel 2 - created with Terraform"
      + edge_gateway_id    = "urn:vcloud:gateway:83e55e3f-de87-4a31-a0b0-d52f6a069a41"
      + enabled            = true
      + id                 = (known after apply)
      + ike_fail_reason    = (known after apply)
      + ike_service_status = (known after apply)
      + local_ip_address   = "x.x.x.x"
      + local_networks     = [
          + "x.x.x.x/32",
        ]
      + logging            = false
      + name               = "to_tenant_2"
      + org                = "10000028"
      + pre_shared_key     = (sensitive value)
      + remote_ip_address  = "x.x.x.x"
      + remote_networks    = [
          + "10.0.10.0/24",
        ]
      + security_profile   = (known after apply)
      + status             = (known after apply)
      + vdc                = (known after apply)

      + security_profile_customization {
          + dpd_probe_internal           = 30
          + ike_dh_groups                = [
              + "GROUP14",
            ]
          + ike_digest_algorithms        = [
              + "SHA2_256",
            ]
          + ike_encryption_algorithms    = [
              + "AES_256",
            ]
          + ike_sa_lifetime              = 28800
          + ike_version                  = "IKE_V2"
          + tunnel_df_policy             = "COPY"
          + tunnel_dh_groups             = [
              + "GROUP14",
            ]
          + tunnel_digest_algorithms     = [
              + "SHA2_256",
            ]
          + tunnel_encryption_algorithms = [
              + "AES_256",
            ]
          + tunnel_pfs_enabled           = true
          + tunnel_sa_lifetime           = 3600
        }
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

vcd_nsxt_ipsec_vpn_tunnel.tunnel1["to_tenant_1"]: Creating...
vcd_nsxt_ipsec_vpn_tunnel.tunnel1["to_tenant_2"]: Creating...
vcd_nsxt_ipsec_vpn_tunnel.tunnel1["to_tenant_1"]: Still creating... [10s elapsed]
vcd_nsxt_ipsec_vpn_tunnel.tunnel1["to_tenant_2"]: Still creating... [10s elapsed]
vcd_nsxt_ipsec_vpn_tunnel.tunnel1["to_tenant_1"]: Creation complete after 12s [id=696f75ec-d2ec-47ec-9a23-245634f75519]
vcd_nsxt_ipsec_vpn_tunnel.tunnel1["to_tenant_2"]: Still creating... [20s elapsed]
vcd_nsxt_ipsec_vpn_tunnel.tunnel1["to_tenant_2"]: Creation complete after 26s [id=7d3b3bd4-f42a-4701-824e-996064e3242f]

When you run the terraform apply command again, Terraform checks the changes between terraform.tfstate and the VMware Cloud Director IPSec VPN tunnels to see if any changes need to be made. In my case, no changes has to be made.

IPSec VPN tunnels with terraform
cloud director
Terraform
Automate

Note:

Only IPSec VPN tunnels from the ipsec_vpn_tunnels.tf will be maintaned by Terraform. Existing IPSec VPN tunnels that are created without this terraform script will not be changed.
vkerneladmin@alm-autom01:~/terraform/projects/vCD$ terraform apply
data.vcd_vdc_group.vdc_group: Reading...
data.vcd_vdc_group.vdc_group: Read complete after 0s [id=urn:vcloud:vdcGroup:491c933f-5a34-4a46-956c-5cfa9949ed8e]
data.vcd_nsxt_edgegateway.edge: Reading...
data.vcd_nsxt_edgegateway.edge: Read complete after 0s [id=urn:vcloud:gateway:83e55e3f-de87-4a31-a0b0-d52f6a069a41]
vcd_nsxt_ipsec_vpn_tunnel.tunnel1["to_tenant_1"]: Refreshing state... [id=696f75ec-d2ec-47ec-9a23-245634f75519]
vcd_nsxt_ipsec_vpn_tunnel.tunnel1["to_tenant_2"]: Refreshing state... [id=7d3b3bd4-f42a-4701-824e-996064e3242f]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Final Words

This was a simple way to configure multiple IPSec VPN tunnels with Terraform. I have also created a version that retrieves the Pre-Shared Key through the API from a password manager (PasswordState). You can find the Terraform scripts on my GitHub page.

Leave a Comment