Terraform (Series-2)

TABLE OF CONTENT:

  1. What are Data Sources in terraform ?
  2. What are Modules in terraform ?
    • Calling a child module
    • Terraform module location
  3. Important Points
  4. What are String Templates in terraform ?
    • Interpolation
    • Directives
  5. How to use various Conditions in terraform ?
  6. What are Dynamic Blocks in terraform ?
  7. What are Functions in terraform ?
  8. What are template file in terraform ?

What are Data Sources in terraform?

Data source allows you to fetch the data at runtime as an input and gets applied to other resources. Lets look at an example here where you will notice that using data it is fetching the instance details with a provided instance_id.

data "aws_instance" "my-machine1" {          # Fetching the instance
  instance_id = "i-0a0269cf952a02832"
  }

What are Modules in terraform?

Modules are known as containers of resources that are created as individual modules. Root Modules can have multiple individual child modules , data , resources data types and so on.

To call a child module you will need to explicitly define the location of child module using source argument.

module "efs" {                                                   # Module and Label is efs
  source               = "./modules/EFS"                         # Define the Path of Child Module                             
  subnets              = var.subnet_ids
  efs_file_system_name = var.efs_file_system_name
  security_groups      = [module.SG.efs_sg_id]
  role_arn             = var.role_arn
}

In previous example the location of module EFS is one directory behind the current directory, so you defined the local Path as ./mdules/EFS and in some cases the modules are stored in Terraform Registry , GitHub , Bitbucket , Mercurial Repo, S3 bucket , GCS Bucket. Lets learn how to use the sub modules.

module "mymodule1" {                              # Local Path located  Module
  source = "./consul"
}

module "mymodule2" {                              # Terraform Registry located Module
  source = ".hasicorp/consul/aws"
  version = "0.1.0"
}

module "mymodule3" {                              # GIT located  Module
  source = "github.com/automateinfra/"
}

module "mymodule4" {                              # Mercurial located  Module
  source = "hg::https://automateinfra.com/vpc.hg"
}

module "mymodule5" {                               # S3 Bucket located  Module
  source = "s3::https://s3-eu-west-1.amazonaws.com/vpc.zip"
}
Diagram which displays booth ROOT and CHILD modules

Important Points

  1. null : It represents omission or absence
  2. Heredoc Strings: They are used to allow multiline strings in clear way.
<<EOT
Automate
Infra
EOT
  1. path.module: Filesystem path of module
  2. path.cwd : Filesystem path of current working directory
  3. count.index: uses count meta -argument
  4. each.key and each.value used in for_each meta argument

What are String Templates in terraform?

With Interpolation or heredoc or directive string expression as shown below you can build template sequence. These templates are used dynamically for / from other values.

Interpolation: “${}” It has been deprecated after terraform 0.12 but it will be handy to understand the concept here.

"Welcome ${var.name}" 

Directive :

%{if true } or %{endif} or %{endif} 

How to use various Conditions in terraform?

Below are some examples on how to retrieve outputs with different conditions.

aws_instance.myinstance.id      # This will provide you a result with Ec2 Instance details.
aws_instance.myinstance[0].id   # This will provide you a result with first Ec2 Instance details.
aws_instance.myinstance[1].id   # This will provide you a result with second Ec2 Instance details
aws_instance.myinstance.*id     # This will provide you a result with all Ec2 Instance details

Now, let us see few complex examples where different conditions are applied to retrieve outputs.

[for value in aws_instance.myinstance:value.id]        # Returns all instance values by their ids.
var.a != "auto" ? var.a : "default-a"              # if var.a is auto then use var.a else default-a
[for a in var.list : a.instance[0].name]               # var.list[*].instance[0].name
[for a in var.list: upper(a)]                         # iterates over each item in var.list and lists upper case 
[for a in var.list : a => upper(a)]     # list with original objects and corresponding upper case
                                                    # Example : [("a","A"),("b","B"),("c","C")]                                                         

What are Dynamic Block in terraform?

Dynamic blocks are used when any resource or module doesn’t accept repeated arguments inside its block. To simplify the definition, while creating any resource in the module you are not allowed to provide the arguments multiple times such as name and value, instead dynamic settings are used. Below is a basic example of dynamic setting.

resource "aws_instance_ec2" "myinstance" {

 name = "auto" 

dynamic "settings" {
   for_each = var.settings
   instance {
    name = setting.value["name"]
    value = setting.value["value"]
     }
}

What are Functions in terraform?

There are different types of functions which terraform supports such as:

Built in Functions:

min(2,3,4)                                      # The output of this function is 2

join("","","hello", "Automate", "infra")   # The output of this function is hello, Automate , infra

element(["a", "b", "c"], length(["a", "b", "c"])-1)   # The output of this function is c

lookup({a="ay", b="bee"}, "c", "unknown?")            # The output of this function is unknown

jsonencode({"hello"="Automate"})             # The output of this function is {"hello":"Automate"}

jsondecode("{\"hello\": \"Automate\"}")            #  # The output of this function is

                                                                 {
                                                                   "hello" = "Automate"
                                                                  }

templatefile: It reads the file at given directory or path.

Syntax: templatefile(path,vars)

Example of templatefile with Lists

# backend.tpl

%{for addr in ipaddr ~}      # Condition via Directive
backend ${addr}:${port}      # Prints this      
%{end for ~}                 # Condition via Directive

templatefile("${path.module}/backend.tpl, { port = 8080 , ipaddr =["1.1.1.1","2.2.2.2"]})
Result: backend 1.1.1.1:8080
        backend 2.2.2.2:8080

Example of templatefile with map

# backend.tmpl

%{ for key,value in config }
set ${key} = ${value}
%{ endfor ~}

templatefile("${path.module}/backend.tmpl,
     { 
        config = {
              "a" = "automate"
              "i" = "infra"
           } 
      })
Result:  set a = automate
         set i = infra

Can function:

can function evaluates the given expression and returns a boolean value

Example 1:

local.instance {
  myinstance1 = "t2.micro"
  myinstance2 = "t2.medium"
}

can(local.instance.myinstance1)   This is True
can(local.instance.myinstance3)   This is False

Example 2:

variable "time" {
  validation {
     condition  = can(formatdate("","var.time"))   # Checking the 2nd argument
  }
}

try function:

try function checks the expression and returns the first correct option.

local.instance {
  myinstance1 = "t2.micro"
  myinstance2 = "t2.medium"
}

try(local.instance.myinstance1, "second-option") # This is available in local
Output is : t2.micro
try(local.instance.myinstance3, "second-option") # This is not available in local
Output is : "second-option"