Terraform is an excellent tool for defining infrastructure as code to increase productivity and transparency. Terraform enables you to safely and predictably create, change, and improve production infrastructure.
As a declarative language, Terraform enforces simplicity by design giving you an accurate view of the infrastructure you’re deploying at the expense of allowing you to do more complicated task such as conditionals and loops which you’re accustomed to in a procedural language. The lack of conditionals makes it difficult to create reusable Terraform modules.
Luckily, there’re certain workarounds we can use to achieve our goal. We’ll achieve this using a few primitives namely – a meta-parameter called count plus a large number of interpolation functions provided by Terraform that allow you to do certain types of loops and if-statements.
Conditionals
An excellent example of where you might want to use conditionals is having the option of deploying AWS NAT Gateways or NAT Instances for the VPC created by TF VPC module. Here are the types of conditionals we’ll go over:
- If-statements
- If-else-statements
If-statements
We’ll startup with a basic scenario where our Terraform module creates an EC2 instance and an Elastic IP that’ll be attached to the created instance:
resource "aws_instance" "example" {
ami = "${var.ami}"
instance_type = "${var.instance_type}"
tags {
Name = "${var.service_name}"
}
}
resource "aws_eip" "example" {
instance = "${aws_instance.example.id}"
}
The module takes 3 variables as inputs:
variable "service_name" {
description = "The name of the service"
}
variable "ami" {
description = "The ID of an AMI for the service"
}
variable "instance_type" {
description = "The type of EC2 Instance for the service"
}
We can then use this module to deploy two services, a Website and API:
module "website" {
source = "/modules/services"
service_name = "website"
ami = "ami-abcd1234"
instance_type = "t2.large"
}
module "api" {
source = "/modules/services"
service_name = "api"
ami = "ami-efgh5678"
instance_type = "m4.xlarge"
}
This works as expected except we only want the public IP attached to our Website instance and not the API as it won’t be public facing and will be consumed internally by the Website. To address this, let’s add another variable as an input for our module:
variable "create_eip" {
description = "If set to true, create an EIP for the service"
}
We can accomplish our goal by making use of the count
meta-parameter provided by Terraform. count
controls the number of identical resources to create. By default, in Terraform a boolean true is converted to a 1 and a boolean false is converted to a 0. Let’s update the module to utilise count
:
resource "aws_eip" "example" {
count = "${var.create_eip}"
instance = "${aws_instance.example.id}"
}
If var.create_ip
is true, the module will create one EIP and if var.create_ip
is false, the module will create zero EIPs:
module "website" {
source = "/modules/services"
service_name = "website"
ami = "ami-abcd1234"
instance_type = "t2.large"
create_eip = true
}
module "api" {
source = "/modules/services"
service_name = "api"
ami = "ami-efgh5678"
instance_type = "m4.xlarge"
create_eip = false
}
If-else-statements
Now that we are able to do an if-statement, let’s look at if-else-statements. Building on from our module, we will like to create an Elastic Load Balancer if no EIP is created as our API will be load balanced. Let’s update the module:
resource "aws_elb" "bar" {
name = "foobar-terraform-elb"
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
count = "${1 - var.create_eip}"
access_logs {
bucket = "foo"
bucket_prefix = "bar"
interval = 60
}
listener {
instance_port = 8000
instance_protocol = "http"
lb_port = 80
lb_protocol = "http"
}
listener {
instance_port = 8000
instance_protocol = "http"
lb_port = 443
lb_protocol = "https"
ssl_certificate_id = "arn:aws:iam::123456789012:server-certificate/certName"
}
health_check {
healthy_threshold = 2
unhealthy_threshold = 2
timeout = 3
target = "HTTP:8000/"
interval = 30
}
instances = ["${aws_instance.example.id}"]
cross_zone_load_balancing = true
idle_timeout = 400
connection_draining = true
connection_draining_timeout = 400
tags {
Name = "foobar-terraform-elb"
}
}
We added an ELB resource and are setting the count to 1 - var.create_eip
which means if var.create_eip
is true (1), count for the ELB resource will be false (1 – 1 = 0) meaning it won’t be created but if it is false (0), count for the ELB resource will be true (1 – 0 = 1) so our ELB get’s created.
Conclusion
Although Terraform is a declarative language, it includes a large number of tools that give the language a surprising amount of flexibility and expressive power. In a follow up post I’ll touch on more complicated if-statements and if-else-statements and also have a look at how to do loops with Terraform. Follow us on Twitter to be notified when it comes out.
New to Terraform? Our Terraform post is the perfect starting point.
If you need help with Terraform, DevOps practices, or AWS at your company, feel free to reach out to us.
At OSO, our experts can maintain your DevOps platform and be responsible for day-to-day operational issues, allowing you to develop and ship your product without the need for internal DevOps hires.
Fore more content:
How to take your Kafka projects to the next level with a Confluent preferred partner
Event driven Architecture: A Simple Guide
Watch Our Kafka Summit Talk: Offering Kafka as a Service in Your Organisation