Table of Contents

Terraform

Debugging

# https://developer.hashicorp.com/terraform/internals/debugging
# investigage errors
export TF_LOG="TRACE"
export TF_LOG_PATH="./terraform.log"

Glossary

Terraform Module A Terraform module is a set of Terraform configuration files in a single directory.

Installation https://askubuntu.com/questions/983351/how-to-install-terraform-in-ubuntu

Introduction: https://www.terraform.io/intro/getting-started/build.html

Skeleton project https://github.com/skipidar/terraform-skeleton

Apply terraform bash script


#!/bin/bash
set -eo pipefail

if [[ ! -d ".terraform" ]]
then
  terraform init
fi

terraform validate
terraform plan

#terraform apply

Upgrade terraform provider

https://developer.hashicorp.com/terraform/tutorials/configuration-language/provider-versioning


terraform init -upgrade

Initializing the backend...

Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.0"...
- Installing hashicorp/aws v5.21.0...
- Installed hashicorp/aws v5.21.0 (signed by HashiCorp)

Terraform has made some changes to the provider dependency selections recorded
in the .terraform.lock.hcl file. Review those changes and commit them to your
version control system if they represent changes you intended to make.

Terraform has been successfully initialized!

Main

Create “main.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.0"
    }
  }
}

provider "aws" {
  profile = "default"
  region  = format("%s", var.aws_region)
}

Variables

Create “variables.tf


variable "aws_region" {
  description = "The AWS region to deploy the resources into"
  type = string
  default = "eu-central-1"
}

variable "aws_account_id" {
  description = "The AWS account identifier of the project"
  type = string
  default = "1234567891234"
}

variable "prefix" {
  description = "The resource prefix"
  type = string
  default = "alf-dev-con1"
}


locals {
  iot_policy = "${var.prefix}-thing2"
}



locals defines inner variables. Only here one can combine other variables

Use the variable


provider "aws" {
  profile = "default"
  region  = var.region
}

Data

When you define data blocks Terraform communicates with the AWS provider to query AWS and fetch the list of requested resources, mentioned in data-block.


You can apply filters.


# Find the latest available AMI that is tagged with Component = web
data "aws_ami" "web" {
  filter {
    name   = "state"
    values = ["available"]
  }

  filter {
    name   = "tag:Component"
    values = ["web"]
  }

  most_recent = true
}

Create templates.tf


data "template_file" "tf_iot_policy" {
  vars = {
    aws_region = "${var.aws_region}"
    aws_account_id = "${var.aws_account_id}"
  }
  template = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iot:Connect",
        "iot:Publish",
        "iot:Receive",
        "iot:Subscribe"
      ],
      "Resource": "arn:aws:iot:$${aws_region}:$${aws_account_id}:*"
    }
  ]
}
EOF
}

Usage

resource "aws_iot_policy" "iot_policy" {
  name   = "${local.iot_policy}"
  policy = "${data.template_file.tf_iot_policy.rendered}"
}

Dynamic block

See https://spacelift.io/blog/terraform-dynamic-blocks

To replace the repetitive code as here in a module:

resource "azurerm_virtual_network" "dynamic_block" {
  name                = "vnet-dynamicblock-example-centralus"
  resource_group_name = azurerm_resource_group.dynamic_block.name
  location            = azurerm_resource_group.dynamic_block.location
  address_space       = ["10.10.0.0/16"]

  subnet {
    name           = "snet1"
    address_prefix = "10.10.1.0/24"
  }

  subnet {
    name           = "snet2"
    address_prefix = "10.10.2.0/24"
  }

  subnet {
    name           = "snet3"
    address_prefix = "10.10.3.0/24"
  }

  subnet {
    name           = "snet4"
    address_prefix = "10.10.4.0/24"
  }
}

Use the “dynamic” block

resource "azurerm_virtual_network" "dynamic_block" {
  name                = "vnet-dynamicblock-example-centralus"
  resource_group_name = azurerm_resource_group.dynamic_block.name
  location            = azurerm_resource_group.dynamic_block.location
  address_space       = ["10.10.0.0/16"]

  dynamic "subnet" {
    for_each = var.subnets
    iterator = item   #optional
    content {
      name           = item.value.name
      address_prefix = item.value.address_prefix
    }
  }
}

Declare a variable in your module

variable "subnets" {
  description = "list of values to assign to subnets"
  type = list(object({
    name           = string
    address_prefix = string
  }))
}

USAGE of your module

Assigning values to the variable “subnets”, which are taken by the module above.

subnets = [
  { name = "snet1", address_prefix = "10.10.1.0/24" },
  { name = "snet2", address_prefix = "10.10.2.0/24" },
  { name = "snet3", address_prefix = "10.10.3.0/24" },
  { name = "snet4", address_prefix = "10.10.4.0/24" }
]