I’ve used Postman and then Python to configure new services(virtual servers) on F5 LTM I have in my lab, but now I want to take a new step and go into Infrastructure as a code and use Terraform to push my config. I’ll be using Terraform and an AS3 template to do this. Terraform is an infrastructure as code tool and with AS3, you can deploy an application service configuration on the BIG-IP system using a declarative representational state transfer (REST) API. I am not going to explain more about these 2, I just want to share a working example.
The goal here would be to create an application with 2 services.
To do this is will need these files:
- the AS3 template file
- the variables files, where I create the variables
- the tfvars file, where I define my variables
- the main file, where I put it all together so the template gets rendered
- certificate file, where I generate a self-signed certificate/key
- locals file
AS3 template file
The AS3 template is a JSON file where I define how my application which consists of 2 services should look. I’ve started with defining some shared objects that I could use with both services:
"Shared": {
"class": "Application",
"template": "shared",
"enable": true,
"${POOL1}": {
"class": "Pool",
"loadBalancingMode":"${LB_MODE}",
"monitors": [
"${MONITOR}"
],
"members": [
{
"servicePort": ${SERVICEPORT},
"serverAddresses": ${MEMBERS_1}
}
]
},
"${HTTP_PROFILE}":{
"class": "HTTP_Profile",
"xForwardedFor": true
},
"${IRULE_NAME}": {
"class": "iRule",
"remark": "choose private pool based URI",
"iRule": {
"url": "${IRULE}"
}
}
},
Then I went to define my services. I’ve tried to use different ways to define different objects to experiment and have an example of multiple ways of doing things. For example, for irule I’ve used “url” to fetch it from Git. Then I used “use” for the objects from the Shared ‘application’ and also used some built-in options like for the TCP profile.
Here is an example of one of the services:
"${SERVICE2}": {
"class": "Service_HTTPS",
"virtualAddresses": [
"${VIP2}"
],
"virtualPort": 443,
"persistenceMethods": [
"${PERSISTANCE}"
],
"profileTCP": {
"egress": "wan",
"ingress": "lan"
},
"profileHTTP": {
"use": "/${TENANT}/Shared/${HTTP_PROFILE}"
},
"snat": "auto",
"iRules": [
{ "use": "/${TENANT}/Shared/${IRULE_NAME}"}
],
"pool": "${S2_POOL1}",
"serverTLS": "webtls"
},
"${S2_POOL1}": {
"class": "Pool",
"monitors": [
"${S2_MONITOR}"
],
"members": [
{
"servicePort": ${SERVICEPORT},
"serverAddresses": ${S2_MEMBERS_1}
}
]
},
"webtls": {
"class": "TLS_Server",
"cipherGroup": {"bigip": "/Common/f5-secure"},
"tls1_0Enabled": false,
"tls1_1Enabled": false,
"ssl3Enabled": false,
"sslEnabled": false,
"certificates": [{
"certificate": "webcert"
}]
},
"webcert": {
"class": "Certificate",
"remark": "in practice we recommend using a passphrase",
"certificate": {
"base64": "${cert}"
},
"privateKey": {
"base64": "${key}"
}
}
}
}
}
}
variables files
In these files, I’ve created the variables. I gave 2 files, one for each service.
variable "SERVICE2" {
type = string
}
variable "VIP2" {
type = string
}
variable "S2_POOL1" {
type = string
}
variable "S2_MONITOR" {
type = string
}
variable "S2_MEMBERS_1" {
type = list
}
tfvars file
In this file, I am defining what values, the variables I’ve created previously, should have like pool names, irule url, profiles, pool members, etc. for each of my services.
// Tenant 1 config variables
//
// Service 1
TENANT = "Tenant_01"
SERVICE1 = "service_01"
VIP = "10.0.0.131"
HTTP_PROFILE = "http-xForwardedFor"
PERSISTANCE = "cookie"
IRULE_NAME = "rule1"
IRULE = "https://raw.githubusercontent.com/czirakim/F5_AS3/master/Tenant1/rule1.irule"
POOL1 = "webpool"
POOL2 = "abc_pool"
LB_MODE = "least-connections-member"
MONITOR = "http"
MEMBERS_1 = ["1.1.1.1","10.9.9.9"]
MEMBERS_2 = ["8.8.8.8","8.8.4.4"]
SERVICEPORT = 8080
//
// Service2
SERVICE2= "service_02"
VIP2 = "10.0.0.132"
S2_POOL1 = "service_02_webpool"
S2_MONITOR = "http"
S2_MEMBERS_1 = ["1.1.1.2","9.9.9.9"]
main file
In the main file, I put all the things together so the template gets rendered and all the variables get mapped to the ones used in the template.
data template_file "init" {
template = file("tenant_template.json")
vars = {
TENANT = var.TENANT
SERVICE1 = var.SERVICE1
SERVICE2 = var.SERVICE2
VIP = var.VIP
VIP2 = var.VIP2
HTTP_PROFILE= var.HTTP_PROFILE
IRULE_NAME= var.IRULE_NAME
IRULE= var.IRULE
POOL1 = var.POOL1
POOL2 = var.POOL2
LB_MODE = var.LB_MODE
PERSISTANCE = var.PERSISTANCE
MONITOR = var.MONITOR
MEMBERS_1 = jsonencode(var.MEMBERS_1)
MEMBERS_2 = jsonencode(var.MEMBERS_2)
SERVICEPORT = var.SERVICEPORT
S2_POOL1 = var.S2_POOL1
S2_MONITOR = var.S2_MONITOR
S2_MEMBERS_1 = jsonencode(var.S2_MEMBERS_1)
}
}
resource "bigip_as3" "as3-tenant" {
as3_json = data.template_file.init.rendered
}
certificate file
Here I generate a self-signed certificate in Terraform. This should only be used internally. In the case of production services, these should be replaced with something else. There are some examples on the Internet of how to get a certificate using AWS, GCP, etc.
resource "tls_private_key" "example" {
algorithm = "RSA"
rsa_bits = 2048
}
resource "tls_self_signed_cert" "example" {
private_key_pem = tls_private_key.example.private_key_pem
subject {
common_name = "example.com"
organization = "ACME Examples, Inc"
country = "US"
}
validity_period_hours = 8766 //1 year
dns_names = ["example.com", "example.net"]
allowed_uses = [
"key_encipherment",
"digital_signature",
"server_auth",
]
}
locals file
In this file, I am defining some local variables for the cert and the key I’ll be using in my template. They take the values of the resources I’ve created in the certificate file.
locals {
cert = base64encode(tls_self_signed_cert.example.cert_pem)
key = base64encode(tls_private_key.example.private_key_pem)
}
Conclusion
So this is it, I have to thank the F5 DevCentral community for helping and check the useful links if you want to learn more about AS3.
Source code: https://github.com/czirakim/F5_AS3/tree/master
Useful links:
https://my.f5.com/manage/s/article/K23449665
https://community.f5.com/t5/technical-articles/embracing-as3-foundations/ta-p/324982
https://clouddocs.f5.com/products/extensions/f5-appsvcs-extension/latest/declarations/http-services.html#configuring-an-http-profile-with-a-proxy-connect-profile
https://clouddocs.f5.com/products/extensions/f5-appsvcs-extension/latest/refguide/declaration-purpose-function.html