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.
Table of Contents
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.
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.