Let’s Terraform the vTM: Part 4 / 4

Let’s Terraform the vTM: Part 4 / 4

Continuing from Part 1, Part 2, and Part 3, in this final instalment we’ll finish our configuration by adding things like SSL offload and L7 routing.

vTM has an embedded language called TrafficScript, that can be used to run business logic over Requests (what vTM sees from the client) and/or Responses (what vTM sees from the pool node). It has full access to everything in Requests and Responses (if vTM is configured to perform SSL Offload), which includes headers, cookies, request strings, body, etc.. Based on the logic you put into your TrafficScript (TS), vTM can do all kinds of things – check out my older post on integrating 3rd party services with your Single Page App web site, for example. For this post, we’ll use it to do L7 routing.

Terraform supports including external code in-line, which is perfect for small chunks of such code, but I like separating concerns. Create a directory files under our working directory try-vtmtf, and inside it create a file called vs1_request_rule.tpl with the following contents:

if(http.getPath() == "/api") {
    pool.use("${pool_name}");
}

 

This is a template for TS code with one variable – pool_name. Running terraform apply will inject a name of our API pool to produce the actual TrafficScript code. Our VS1 will then run it on all incoming requests, and if HTTP Path contains the string /api, vTM will send that request to the nodes in the pool with the name of our API pool that we’ve passed to it.

Let’s add this into our main.tf:

# Our Request Rule needs API pool name, so we handle this through a template
data "template_file" "vs1_request_rule" {
  template = "${file("${path.module}/files/vs1_request_rule.tpl")}"

  vars {
    pool_name = "${vtm_pool.api_pool.name}"
  }
}

resource "vtm_rule" "vs1_l7_routes" {
  name    = "${local.uniq_id}_VS1-L7-Routes"
  content = "${data.template_file.vs1_request_rule.rendered}"
}

 

This will create a template data source called vs1_request_rule of type template_file with one variable – pool_name that we set to the name of our API pool. When accessed, this data source will take the contents of the template file set in template parameter, inject the variable(s), and return the resulting file.

We can see how this is used in the vtm_rule resource, where content parameter consumes what this data source produces.

Since template_file requires yet another provider, we’ll need to run terraform init once more before proceeding, so that Terraform can download it. Once done, you can run terraform plan and see the rendered contents of the template in the proposed creation of the vtm_rule.

Note: In cases where your rules are small (such as above), you may prefer to include them directly into the template using heredoc syntax. It is certainly more compact, and comes to personal preference as to whether you want to mix Terraform templating code (HCL) with other code (TrafficScript) or not.

Here’s an example for how you could do the same as above (separate TrafficScript file and data "template_file" "vs1_request_rule") using this combined approach, where the TS code is included between the EOF markers. In this case we inject the pool name by referring directly to the appropriate Terraform resource – ${vtm_pool.api_pool.name}.

resource "vtm_rule" "vs1_l7_routes" {
  name = "${local.uniq_id}_VS1-L7-Routes"

  # Content is TrafficScript, with an embedded Terraform variable
  #
  content = <<EOF
if(http.getPath() == "/api") {
    pool.use("${vtm_pool.api_pool.name}");
}
EOF
}

 

To make this rule active we’ll need to add it to our VS as a parameter; but let’s take care of SSL offload prerequisites first.

Our template requires a certificate, so we are going to create a self-signed one. In our try-vtmtf container, run the following:

openssl req -newkey rsa:2048 -nodes \
    -keyout domain.key -out domain.csr \
    -subj "/C=US/ST=New York/L=Brooklyn/O=Example Brooklyn Company/CN=*.corp.com"

openssl x509 -signkey domain.key \
    -in domain.csr -req \
    -days 365 -out domain.crt

 

This should have produced three files – domain.crt, domain.csr, and domain.key. Paste their contents (starting / ending with -----BEGIN.. / -----END..) into the three new variables in our terraform.tfvars file between the respective EOF markers:

ssl_cert_pub = <<EOF
-----BEGIN CERTIFICATE-----
MIIDUjCCAjoCCQDGcM4C0OfWCDANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQGEwJV
[ ... skipped ... ]
64+OgfhZ2bb3eiFsm2Azf55AWqbb5pr3tDwrBidg5R176Cl6l00=
-----END CERTIFICATE-----
EOF

ssl_cert_pri = <<EOF
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCq0CEK/ua37JDM
[ ... skipped ... ]
htQ3q8eLTYwApsXNHdGwQyME
-----END PRIVATE KEY-----
EOF

ssl_cert_req = <<EOF
-----BEGIN CERTIFICATE REQUEST-----
MIICsDCCAZgCAQAwazELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE5ldyBZb3JrMREw
[ ... skipped ... ]
K4nc/E5mq4JkmBEdiDg/yxUnUvI=
-----END CERTIFICATE REQUEST-----
EOF

 

Hopefully now it’s double-clear why we don’t want to check terraform.tfvars into code repository! 🙂

Let’s add the corresponding variables into our variables.tf:

variable "ssl_cert_pri" {
  description = "Private SSL Cert for the Virtual Server"
}

variable "ssl_cert_pub" {
  description = "Public SSL Cert for the Virtual Server"
}

variable "ssl_cert_req" {
  description = "Signing Request for the SSL Cert for the Virtual Server"
}

 

And the code for the SSL Server Key resource to the main.tf:

# SSL Server Certificates for the Virtual Server's SSL Offload.
#
resource "vtm_ssl_server_key" "ssl_cert" {
  name = "${local.uniq_id}-server-corp.com"
  note = "SSL Server Cert for corp.com"

  private = "${var.ssl_cert_pri}"
  public  = "${var.ssl_cert_pub}"
  request = "${var.ssl_cert_req}"
}

 

Finally, let’s update our Virtual Server so that it:

  • Uses our TrafficScript Rule;
  • Has SSL Offload turned on; and
  • Listens on the port 443 instead of 80.

Replace the resource "vtm_virtual_server" "vs1" { .. } that you have in your main.tf with the following:

# The Virtual Server
#
resource "vtm_virtual_server" "vs1" {
  name          = "${local.uniq_id}_VS1"
  enabled       = "true"
  listen_on_any = "${local.should_listen_on_any}"

  # We need to use compact() to get rid of values "" which will be there
  # in case we didn't have any TIP Groups
  listen_on_traffic_ips = ["${compact(local.tigs_list)}"]

  # Default pool = "Main"
  pool                    = "${vtm_pool.main_pool.name}"
  port                    = "443"
  protocol                = "http"
  ssl_decrypt             = "true"
  ssl_server_cert_default = "${vtm_ssl_server_key.ssl_cert.name}"
  request_rules           = ["${vtm_rule.vs1_l7_routes.name}"]
}

 

The notable changes are:

  • port: 80 -> 443
  • Added ssl_decrypt, ssl_server_cert_default, and request_rules

Now, run terraform apply, and if all went well – you should have your configuration in its final state, all wrapped up and working!

If not – do not despair 🙂 My copy of the template is available in my GitHub repo, where you can download it and compare with yours.

And once you’re done playing – run terraform destroy, to see the configuration you’ve applied to your vTM disappear as a whole, leaving the rest of the configuration (if any) intact.

Happy terraforming!

About Dmitri Kalintsev

Some dude with a blog and opinions ;) View all posts by Dmitri Kalintsev

One response to “Let’s Terraform the vTM: Part 4 / 4

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: