NSX

NSX-T Automation using Terraform

The how!

Posted by Chris Noon on Mon, Aug 10, 2020

I started the series with a post about why I wanted to use Terraform. Now we have the why out of the way, let’s get to the fun stuff… the how!

I use Terraform for 2 things. The first is to spin up SDDC’s on VMC and configure the VMC NSX-T piece. The second is spinning up NSX-T environments in the lab, which will hopefully make it into production one day. In this post, I’ll focus on the VMC element.

The VMC Environment

I’ll start by assuming you know what VMC on AWS is and have a high-level understanding of its concepts and what it delivers.

I’ll skip the step for deploying a VMC SDDC using Terraform, but if you want to read how to do that, then check out Nico’s post, which got me started.

Let’s dive straight into the NSX-T element on VMC and start by discussing a few concepts. When NSX-T is deploying in a VMC environment, it deploys a T0 and 2 T1’s. You, as a user, are unable to change the T0 and the T1’s are preconfigured as a CGW (Cloud Gateway) and an MGW (Management Gateway). The MGW is used for the management components such as vCenter, ESXi hosts and NSX Manager(s). The CGW is used for the workload segments which you deploy later on.

While NSX-T is the SDN of choice for VMC, it isn’t a “fully functioning” version of NSX and there are differences in both the UI and the features. Below is a screenshot of the UI, which you can see differs from the standard NSX view. Functionality is almost identical, but there is no Load Balancing natively within the environment.

VMC Environment

Importing the T1 information

Let’s talk more about the pre-created T1’s, the CGW and the MGW. They are pre-created with some default rules to allow vCenter and ESXi outbound internet access. These rules need to be imported into our Terraform code so that we don’t lose them. Alternatively, we could re-create them, which removes and re-adds the rules.

There are a couple of prerequisites we have to configure before we start. We need to import these T1’s into the Terraform code and the Firewall rules along with it. Let’s start by creating resources for T1 imports.

 1# Filename policies.tf
 2# MGW definition
 3resource "nsxt_policy_gateway_policy" "mgw_policy" {
 4  category     = "LocalGatewayRules"
 5  display_name = "default"
 6  domain       = "mgw"
 7}
 8
 9# CGW definition
10resource "nsxt_policy_gateway_policy" "cgw_policy" {
11  category     = "LocalGatewayRules"
12  display_name = "default"
13  domain       = "cgw"
14}

Gilles Chekroun messaged me about this article and mentioned he and another colleague where caught out trying to ‘terraform destory’ the NSX-T environment. They circumvented this by adding ‘lifecycle { prevent_destroy = true }’ to the MGW and CGW configuration files as below. Thank you Gilles.

1resource “nsxt_policy_gateway_policy” “mgw_policy” {
2lifecycle { prevent_destroy = true }
3
4resource “nsxt_policy_gateway_policy” “cgw_policy” {
5lifecycle { prevent_destroy = true }

Now I’ll import the Firewall rules from both T1’s into the Terraform code.

1terraform import nsxt_policy_gateway_policy.cgw_policy cgw/default
2
3terraform import nsxt_policy_gateway_policy.mgw_policy mgw/default

The preconfigured state of the T1’s has now been imported into Terraform. In the past, we could then move on to creating our custom elements, but lately, we need another step. While the pre-created Firewalls have been imported, running a ‘terraform plan’ will suggest removing said rules, this isn’t what we want. Update the ‘policies.tf’ file to the below. This information can be found using the ‘terraform show’ command.

 1# MGW definition
 2resource "nsxt_policy_gateway_policy" "mgw_policy" {
 3  category     = "LocalGatewayRules"
 4  display_name = "default"
 5  domain       = "mgw"
 6  rule {
 7    action                = "ALLOW"
 8    destination_groups    = []
 9    destinations_excluded = false
10    direction             = "IN_OUT"
11    disabled              = false
12    display_name          = "vCenter Outbound Rule"
13    ip_version            = "IPV4_IPV6"
14    logged                = false
15    profiles              = []
16    scope = [
17      "/infra/labels/mgw",
18    ]
19    services = []
20    source_groups = [
21      "/infra/domains/mgw/groups/VCENTER",
22    ]
23    sources_excluded = false
24  }
25  rule {
26    action                = "ALLOW"
27    destination_groups    = []
28    destinations_excluded = false
29    direction             = "IN_OUT"
30    disabled              = false
31    display_name          = "ESXi Outbound Rule"
32    ip_version            = "IPV4_IPV6"
33    logged                = false
34    profiles              = []
35    scope = [
36      "/infra/labels/mgw",
37    ]
38    services = []
39    source_groups = [
40      "/infra/domains/mgw/groups/ESXI",
41    ]
42    sources_excluded = false
43  }
44}
45
46# CGW definition
47resource "nsxt_policy_gateway_policy" "cgw_policy" {
48  category     = "LocalGatewayRules"
49  display_name = "default"
50  domain       = "cgw"
51  rule {
52    action                = "DROP"
53    destination_groups    = []
54    destinations_excluded = false
55    direction             = "IN_OUT"
56    disabled              = false
57    display_name          = "Default VTI Rule"
58    ip_version            = "IPV4_IPV6"
59    logged                = false
60    profiles              = []
61    scope = [
62      "/infra/labels/cgw-vpn",
63    ]
64    services         = []
65    source_groups    = []
66    sources_excluded = false
67  }
68}

Now when issuing the ‘terraform plan’ command, no changes need to be made. This is what we want, no changes to the pre-configured state.

 1Refreshing Terraform state in-memory prior to plan…
 2 The refreshed state will be used to calculate this plan, but will not be
 3 persisted to local or remote state storage.
 4 nsxt_policy_gateway_policy.cgw_policy: Refreshing state… [id=default]
 5 nsxt_policy_gateway_policy.mgw_policy: Refreshing state… [id=default]
 6 
 7 No changes. Infrastructure is up-to-date.
 8
 9 This means that Terraform did not detect any differences between your
10 configuration and real physical resources that exist. As a result, no
11 actions need to be performed.

Firewall Rules

I’ll now add some IP groups and some Firewall rules on top of the default configuration. Specifically to allow UI access into the vCenter. Let’s start by creating an IP Group on the MGW for my home/office IP addresses.

 1resource "nsxt_policy_group" "CN_Networks_MGW" {
 2  display_name = "CN_Networks"
 3  description  = "Created from Terraform"
 4  domain       = "mgw"
 5 
 6  criteria {
 7    ipaddress_expression {
 8      ip_addresses = ["1.2.3.4" , "5.6.7.8"]
 9    }
10  }
11}

Issuing a ‘terraform plan’ shows the new group will be added to the configuration. Issue a ‘terraform apply’ to push the configuration to the SDDC.

 1Refreshing Terraform state in-memory prior to plan…
 2 The refreshed state will be used to calculate this plan, but will not be
 3 persisted to local or remote state storage.
 4 nsxt_policy_gateway_policy.cgw_policy: Refreshing state… [id=default]
 5 nsxt_policy_gateway_policy.mgw_policy: Refreshing state… [id=default]
 6 
 7 An execution plan has been generated and is shown below.
 8 Resource actions are indicated with the following symbols:
 9 create 
10 Terraform will perform the following actions:
11 # module.main-NSXT.nsxt_policy_group.CN_Networks_MGW will be created
12 resource "nsxt_policy_group" "CN_Networks_MGW" {
13 description  = "Created from Terraform"
14 display_name = "CN_Networks"
15 domain       = "mgw"
16 id           = (known after apply)
17 nsx_id       = (known after apply)
18 path         = (known after apply)
19 revision     = (known after apply)
20 criteria {
21 ipaddress_expression { ip_addresses = [ "1.2.3.4",
22 "5.6.7.8",
23 ]
24 }
25 }
26 } 
27 Plan: 1 to add, 0 to change, 0 to destroy.
28 
29 Note: You didn't specify an "-out" parameter to save this plan, so Terraform
30 can't guarantee that exactly these actions will be performed if
31 "terraform apply" is subsequently run.

Mgmt Groups

Let’s now add a Firewall rule allowed the ‘CN_Networks’ into vCenter. These rules have to be placed in the ‘policies.tf’ file as this is where the resource has been created/imported.

1    rule {
2    display_name          = "vCenter Inbound Rule"
3    source_groups         = ["/infra/domains/mgw/groups/49f25c78-6222-4e80-a2bf-2bd213367415"]
4    destination_groups    = ["/infra/domains/mgw/groups/VCENTER"]
5    services              = ["/infra/services/HTTPS"]
6    action                = "ALLOW"
7    scope                 = ["/infra/labels/mgw"]
8  }

The management access to vCenter has now been configured and my “office users” can now manage the vCenter using the UI.

Workload Segments

The final element we are going to configure today is workload segments. I will deploy network infrastructure for the typical web, app and database model. Start by creating a new file called ‘segments.tf’. In that file, I have defined the 3 segments and referenced the pre-configured transport zone.

 1# Reference the Transport Zone
 2data "nsxt_policy_transport_zone" "TZ" {
 3  display_name = "vmc-overlay-tz"
 4}
 5
 6# Create the segments
 7resource "nsxt_policy_segment" "web" {
 8  display_name        = "Web"
 9  description         = "Terraform provisioned Segment"
10  connectivity_path   = "/infra/tier-1s/cgw"
11  transport_zone_path = data.nsxt_policy_transport_zone.TZ.path
12  subnet {
13    cidr              = "192.168.1.1/24"
14  }
15}
16
17resource "nsxt_policy_segment" "app" {
18  display_name        = "App"
19  description         = "Terraform provisioned Segment"
20  connectivity_path   = "/infra/tier-1s/cgw"
21  transport_zone_path = data.nsxt_policy_transport_zone.TZ.path
22  subnet {
23    cidr              = "192.168.2.1/24"
24  }
25}
26
27resource "nsxt_policy_segment" "db" {
28  display_name        = "Db"
29  description         = "Terraform provisioned Segment"
30  connectivity_path   = "/infra/tier-1s/cgw"
31  transport_zone_path = data.nsxt_policy_transport_zone.TZ.path
32  subnet {
33    cidr              = "192.168.3.1/24"
34  }
35}

File Structure

I have created a number of files within the NSX-T Terraform directory.

1ip_groups_mgw.tf
2policies.tf
3provider_call.tf
4segments.tf
5variables.tf

Let’s work through them in a logical order and explain what they each do. At this stage, I’d like to mention Terraform files can be named anything you like. Don’t think because I’ve called the file ‘segments.tf’, that you can’t name it ‘bilals_magic_layer2_networkys.tf’. All *.tf files in the directory get reviewed and the combination of these files build out your infrastructure. What does each of my files do?

variables.tf = specify the NSX Manager hostname and the VMC authentication token as variables.

provider_call.tf is the file that calls the ‘nsxt’ terraform provider, which gets download with a ‘terraform init’. It also references the NSX Manager hostname and authentication token.

ip_groups_mgw.tf = Define the IP groups used on the MGW.

polcies.tf = Defines the imported policies and the newly added policies.

segments.tf = Defines the data entry for the transport zone in VMC and defines the 3 segments created for the VMC environment.

Useful Commands

There are a number of commands I used when creating the environment and blog. Let’s run through them now.

 1# Used the initialise Terraform and download the defined provider.
 2terraform init
 3
 4# Used to format all files in the directory correctly.
 5terraform fmt
 6
 7# Used to validate the Terraform files and ensure there are no syntax errors.
 8terraform validate
 9
10# Used to collate the Terraform files and echo changes.
11terraform plan
12
13# Used to apply the Terraform files to the resource (VMC).
14terraform apply
15
16# Same as 'terraform apply' without the need to confirm after the file checks.
17terraform apply --auto-approve 

Closing

The environment build has been fairly basic, but what it does provide is re-usable code to get an environment of your own up and running using Terraform.

Most importantly, we can look at this as a disposable environment, with all this code in place, you can spin it up and down with relative ease moving forward. Once the SDDC is stood up, use the below commands to get that same Network infrastructure put in place within seconds.

1terraform init 
2
3terraform import nsxt_policy_gateway_policy.cgw_policy cgw/default
4
5terraform import nsxt_policy_gateway_policy.mgw_policy mgw/default
6
7terraform apply --auto-approve 

The distinct benefit for me here; is the ability to spin a VMC environment (including NSX-T configuration) up and down in a matter of hours. The SDDC creation taking 99% of the time. This disposable environment type gives me a very cost-effective and quick way of scaling out in case of a planned or unplanned event. It also provides a potential DR solution with a relatively low RTO (few hours).

This post has been very specific to my needs. If there is something you want me to cover, let me know and I’ll either add it to this post or create a new post around the topic.