Terraform Template File - How to apply on real world

Terraform Template File

Terraform is an accessible infrastructure as a code software tool that implements a uniform CLI workflow to handle hundreds of cloud services. In addition, the Terraform enables us to create codes cloud APIs into declarative configuration files. And, on the most helpful feature that helps us to make our code more dynamically, it’s the Terraform Template File.

The Terraform template file template_file data source renders a template from a template string, usually loaded from an external file. We will analyze some real examples where you can change and apply according to your necessity. You can implement that approach anywhere on your Terraform project even inside the Modules.

Terraform Template File Example

Creating the User Data with Terraform

Terraform Template File replacing Bash Variables:

Let’s imagine a scenario where we need to configure our userdata. AWS userdata is the collection of commands/data you can present to an instance at launch time. For illustration, if you are starting an EC2 instance and need to have docker installed on the lately started EC2, you can present a collection of bash commands in the userdata field of the AWS EC2 config page. So, basically, we can use the userdata to automate deployments.

We can have one Template File that will handle the variables in our bash script file.

Let’s our bash script, how it looks likes:

#!/bin/bash -xe
yum update -y
amazon-linux-extras install docker=${DOCKER_VERSION}  -y
amazon-linux-extras install ansible2=${ANSIBLE_VERSION} -y
yum install -y zip unzip

To execute our bash script, we need to replace the variable ${DOCKER_VERSION} on our userdata with the correct value. And here is where the Terraform Template File will help us.

Now, let’s see how we can do it on the Terraform:

Terraform Pass Variable to Template File

data "template_file" "userdata" {
  template = file("${path.module}/userdata.sh")
  vars = {
    DOCKER_VERSION             = var.DOCKER_VERSION
    ANSIBLE_VERSION            = var.ANSIBLE_VERSION
  }
}

Now, to use the template file “usedata“, we can reference it as data.template_file.userdata.rendered

Like this AWS AWS Launch Configuration, needs user data, in our scenario:

resource "aws_launch_configuration" "bitslovers-lc" {
  name_prefix          = "${var.my_environment_name}-lc-"
  image_id             = "ami-12345"
  instance_type        = var.INSTANCE_TYPE
  security_groups      = [aws_security_group.inst.id]
  user_data            = data.template_file.userdata.rendered
  key_name             = "bitslovers-keypair"
  iam_instance_profile = aws_iam_instance_profile.iam_profile.name
 }

Terraform Template File For Loop

Now that you have learned how to use the Template File let’s see how we can go forward, build a FOR LOOP, use values from one ARRAY or List, and then change a file according to it.

Using Array

For our following example, let’s suppose that we need to configure DNSMasq four our server, and our configuration file and template looks like this one below:

#DNSMasq Server Configuration
bind-interfaces
user=dnsmasq
group=dnsmasq
pid-file=/var/run/dnsmasq.pid
cache-size=1000
max-cache-ttl=300
neg-ttl=60
strict-order
bogus-priv
no-resolv
expand-hosts
server=/bitslovers.com/10.180.1.2
server=/bitslovers.com/10.170.1.2
server=/bitslovers.com/10.160.1.2
server=8.8.8.8
no-dhcp-interface=

The configuration file above is the final result we would like to achieve when terraform writes the template to file.

Let’s see how we can generate this configuration file dynamically using Terraform.

resource "local_file" "dnsmasq_conf" {
  filename = "${path.module}/dnsmasq-dynamic.conf.tmpl"
  file_permission = "666"
  content  = <<-EOT
    bind-interfaces
    user=dnsmasq
    group=dnsmasq
    pid-file=/var/run/dnsmasq.pid
    cache-size=1000
    max-cache-ttl=300
    neg-ttl=60
    strict-order
    bogus-priv
    no-resolv
    expand-hosts
    %{ for ip in var.dns_servers }
    server=/bitslovers.com/${ip}
    %{ endfor }
    server=8.8.8.8
    no-dhcp-interface=
EOT
}

We need to declare the variables that we will store all IP's on our list.

variable "dns_servers" {
  type        = list(string)
  description = "Private IP DNS"
}

So, if we define the dns_servers:

dns_servers = ["10.180.1.2", "10.170.1.2", "10.160.1.2"]

The configuration file will be created according to our desired result.

Terraform Template File JSON Example

It is also possible to write a JSON file dynamically using Terraform. Let's see how easy it is.

In that case, it's usually complex and slow to formulate a template that will produce valid JSON that will be rendered correctly when using lots of unique insertion sequences and directives.

Alternatively, you can compose a template that consists only of a particular interpolated call to jsonencode defining the value to encode applying regular Terraform expression syntax as in the following samples:

locals {
  sg_cidr_config_json = jsonencode({
     "SG_CIDR": [for addr in ip_address : "${addr}/${subnet}"],
  })
}

With the corresponding input as the sg-cidr.tmpl sample in the past section will compose a correct JSON representation of the given data arrangement without manually handling escaping or delimiters. Based on the examples above, the repetition of ip_address is performed by applying a for expression rather than template directives.

Result:

{"SG_CIDR":["10.180.0.0/24","10.170.0.0/24"]}

The same can be done for the YAML format, inside the Locals:

YAML Example

locals {
  sg_cidr_config_json = yamlencode({
     "SG_CIDR": [for addr in ip_address : "${addr}/${subnet}"],
  })
}

Here we just need to change the encode function to yamlencode instead of jsonencode

Easy, right? Let's see another great example.

Terraform Template File Conditional

Terraform Template File IF Statement

For our example, let's suppose that we need to configure a new user to access our EKS Cluster on AWS.

Our configuration file must look like this one:

Version: v1
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
data:
  mapRoles: |
    - "groups":
      - "system:bootstrappers"
      - "system:nodes"
      "rolearn": "arn:aws:iam::123413703580:role/ec2-eks-workers"
      "username": "system:node:{{EC2PrivateDNSName}}"
  mapUsers: |
    - "groups":
      - "system:masters"
      "userarn": "arn:aws:iam::123413703580:user/bitslovers_user_admin"
      "username": "bitslovers_user_admin"

Basically, we need to generate the mapRoles and mapUsers dynamically if we have defined the List values for our variable.

Let's see how we can do it on our Terraform:

So, this is our auth-configmap.yaml.tpl:

apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
data:
  mapRoles: |
    ${indent(4, map_ec2_worker_roles_yaml)}
%{if map_extra_iam_roles_yaml != "[]" }
    ${indent(4, map_extra_iam_roles_yaml)}
%{ endif }
%{if map_additional_iam_users_yaml != "[]" }
  mapUsers: |
    ${indent(4, map_additional_iam_users_yaml)}
%{ endif }

So, in case if our list is empty (if map_extra_iam_roles_yaml != "[]"), will not break our code.

And don't forget to declare the variables:

auth_configmap_template = "${path.module}/auth-configmap.yaml.tpl"
map_ec2_worker_roles_yaml            = trimspace(yamlencode(local.map_worker_roles))
map_extra_iam_roles_yaml    = trimspace(yamlencode(var.map_additional_iam_roles))
map_additional_iam_users_yaml    = trimspace(yamlencode(var.map_additional_iam_users))

And finally, use the template_file:

resource "template_file" "auth_configmap" {
  template = file(local.auth_configmap_template)
  vars = {
    map_ec2_worker_roles_yaml        = local.map_ec2_worker_roles_yaml
    map_extra_iam_roles_yaml         = local.map_extra_iam_roles_yaml
    map_additional_iam_users_yaml    = local.map_additional_iam_users_yaml
  }
}

Conclusion

Terraform Template File is the most critical feature, in my opinion, because it gives us a lot of flexibility to handle complex configuration files easily.

Check also, how to execute Terraform on Gitlab CI, with Plan, Apply and Destroy phase.

Boost your Terraform Skills:

How and When to use Terraform Modules?

Do you know what is Terraform Data and How to use it?

Learn How to use Output on your Terraform and share data across multiple configurations.

Create multiple copies of the same resource using Terraform Count.

Do you know the difference between Locals and Variables on Terraform?

Execute Terraform on Gitlab CI.

How to use the Gitlab CI Variables

Effective Cache Management with Maven Projects on Gitlab.

Pipeline to build Docker in Docker on Gitlab.

How to Autoscaling the Gitlab Runner.

How to use Gitlab CI: Deploy to elastic beanstalk

What is AWS KMS and how to use it?

Leave a Comment

Your email address will not be published.