Simplify your Terraform Code with Advanced Functions

Simplify Your Terra Form Code

Terraform – Declarative but Powerful

For years, Foghorn has been helping our clients to automate their infrastructure via code. Doing so enables them to have repeatable and audit-able infrastructure. Historically, the tools available have focused on maintaining a declarative / configuration management toolset, and avoided the temptation to become (or borrow) a full-blown programming language.  A few key capabilities however, like loops and math functions, are hugely valuable even in a declarative / config model. There are always workarounds, such as calling additional code through various “custom resource” methods, but they require additional code, usually running as cloud functions.

Over the last 18 months, we have been investing time into Terraform. Terraform has struck a great balance of adding programming like features without bloating into a programming language that allows procedural code to leak into a declarative model.  All of the customers that started to use Terraform have not regretted it.

For Example

We recently took a project to migrate a customer’s security groups to Terraform. The environment is a simple 3 tier application. A load balancer, an application, and a database with ancillary security groups for maintenance using inbound rules and no outbound rules. Prior to leveraging advanced features available in Terraform’s interpolation engine, the template was 882 lines for 1 environment. When we migrated to terraform we added a few inbound rules and started an outbound filter as well. The Terraform code was 334 lines including additional security group rules.

wc -l *
    334 total

We were able to achieve over 50% reduction in code by utilizing loops and splitting string variables.

variable “ig_tcp_ports_internal_service” {
 type = “map”
 default = {
   “443,443”     = “HTTPs”
   “15000,16000” = “Randomized ports that webserver uses for Management”

resource “aws_security_group_rule” “ig_from_internal_service_tcp” {
 count             = “${length(keys(var.ig_tcp_ports_web)}”
 type              = “ingress”
 from_port         = “${element(split(“,”, element(keys(var.ig_tcp_ports_internal_service), count.index)), 0)}”
 to_port           = “${element(split(“,”, element(keys(var.ig_tcp_ports_internal_service), count.index)), 1)}”
 protocol          = “tcp”
 security_group_id = “${}”
 description       = “${lookup(var.ig_tcp_ports_web, element(keys(var.ig_tcp_ports_internal_service), count.index))}”
 source_security_group_id = “${var.internal_service}”

An update to the security group looks like:

resource “aws_security_group_rule” “in_web_15000_16000” {

type = “ingress”
from_port = 15000
to_port = 16000
protocol = “tcp”
source_security_group_id = “${var.corp_subnet2}.0.0”
security_group_id = “${}”



“17000,19000” = “New Security group rule”

Even though this looks complicated, it produces great results in the AWS console for debugging and compliance/security team auditing and verification. If there are changes, it is just a matter of updating the variable defaults. A 1 line change, compared to a new JSON block of 10 lines.  

Terraform does have some disadvantages.  For example, Cloudformation natively includes a hosted control engine.  To gain this level of functionality, we have to manually build out a CD pipeline that runs terraform jobs.  We have found however, in mutli-cloud implementations as well as some exclusive AWS implementation, that Terraform can allow a more manageable code base.

Overall, I cannot be happier with Terraform. Terraform’s interpolation engine provides immense amounts of power and logic not present in other open source tools on the market. In some cases it enables us to write less code, gives more flexibility, and produces more infrastructure with less effort. If you are interested in seeing or hearing more about how Foghorn uses terraform please reach out.