Official Documentation https://developer.hashicorp.com/terraform

Installation

https://developer.hashicorp.com/terraform/downloads

Commands

Main Commands

terraform+  
init Prepare your working directory (including provider plugins) for other commands
validate Check whether the configuration is valid
plan Show changes required by the current configuration
apply Create or update infrastructure
destroy Destroy previously-created infrastructure

All Other Commands

terraform+  
console Try Terraform expressions at an interactive command prompt
fmt Reformat your configuration in the standard style
force-unlock Release a stuck lock on the current workspace
get Install or upgrade remote Terraform modules
graph Generate a Graphviz graph of the steps in an operation
import Associate existing infrastructure with a Terraform resource
login Obtain and save credentials for a remote host
logout Remove locally-stored credentials for a remote host
metadata Metadata related commands
output Show output values from your root module
providers Show the providers required for this configuration
refresh Update the state to match remote systems
show Show the current state or a saved plan
state Advanced state management
taint Mark a resource instance as not fully functional
test Experimental support for module integration testing
untaint Remove the ‘tainted’ state from a resource instance
version Show the current Terraform version
workspace Workspace management

Frequently Used Flags

--refresh=false to skip reconciling the real state and the state file terraform.tfstate

Import Existing Resources to tfstate

1
2
terraform import <RESOURCE TYPE>.<NAME> <ID>
terraform import module.<MODULE NAME>.<RESOURCE TYPE>.<NAME> <ID>

Please note that <ID> is specific to each provider, where you should check the provider documentation.

Remove Resource from tfstate

1
2
terraform state rm <RESOURCE TYPE>.<NAME>
terraform state rm module.<MODULE NAME>.<RESOURCE TYPE>.<NAME>

Terraform Configuration Language Basics

Terraform language was previously known as HCL (HashiCorp Configuration Language).

Value Types

Types Example
String “Hello”
Number 1.23
Bool true
List [1, 2, 2, 3]
Set [“us-west-1a”, “us-west-1c”]
Map {name = “Mabel”, age = 52}
Null null

Structural Types:

Types Schema Example
Tuple [<TYPE>, <TYPE>, …] [“a”, 15, true]
Object { <KEY> = <TYPE>, <KEY> = <TYPE>, … } {name = “John”, age = 52}

Syntax

1
2
3
4
<BLOCK TYPE> <LABELS> {
  <ARGUMENT NAME> = <ARGUMENT VALUE>  # a comment
  # ...
}

Resource Block

1
2
3
4
resource "<RESOURCE TYPE>" "<NAME>" {
  <KEY1> = <VALUE1>
  # ...
}

<RESOURCE TYPE> is usually in the format of <PROVIDER>_<TYPE>, e.g. aws_instance

Resource Providers Documentation https://registry.terraform.io/browse/providers

Meta-Arguments

  • depends_on

    to explicitly specify a dependency which may affect the order of resource provisioning.

    e.g.

    1
    2
    3
    4
    5
    6
    7
    
     resource "aws_instance" "example" {
       ami = "ami-a1b2c3d4"
       instance_type = "t2.micro"
       depends_on = [
         aws_iam_role_policy.example
       ]
     }
    
  • life_cycle

    to specify the lifecycle behavior of a resource.

    e.g.

    1
    2
    3
    
     lifecycle {
       create_before_destroy = true
     }
    

    arguments:

    • create_before_destroy
    • prevent_destroy (cannot prevent terraform destroy from destroying the resource)
    • ignore_changes
    • replace_triggered_by
  • count

    e.g.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
     resource "local_file" "example" {
       count    = length(var.filenames)
       filename = var.filenames[count.index]
       content  = "I'm number ${count.index + 1}"
     }
    
     variable "filenames" {
       default = [
         "first_file.txt",
         "second_file.txt",
         "third_file.txt",
       ]
     }
    

    count.index refers to the distinct index number (starting with 0) corresponding to this instance.

    <TYPE>.<NAME> refers to the resource block.

    <TYPE>.<NAME>[<INDEX>] refers to individual instances.

  • for_each

    e.g.

    1
    2
    3
    4
    5
    
     module "bucket" {
       for_each = toset(["assets", "media"])
       source   = "./publish_bucket"
       name     = "${each.key}_bucket"
     }
    

    e.g.

    1
    2
    3
    4
    5
    6
    7
    8
    
     resource "azurerm_resource_group" "rg" {
       for_each = {
         a_group = "eastus"
         another_group = "westus2"
       }
       name     = each.key
       location = each.value
     }
    

    each.key refers to the map key or set member corresponding to this instance.

    each.value refers to the map value corresponding to this instance. (If a set was provided, this is the same as each.key)

    <TYPE>.<NAME> refers to the resource block.

    <TYPE>.<NAME>["<KEY>"] refers to individual instances.

Variable Block

1
2
3
4
5
variable "<NAME>" {
  type = <TYPE>
  default = <VALUE>
  # ...
}

The default type is any, which can be specified as:

  • string
  • number
  • bool
  • list(<TYPE>)
  • set(<TYPE>)
  • map(<TYPE>)
  • object({<ATTR NAME> = <TYPE>, ... })
  • tuple([<TYPE>, ...])

Output Block

Output block helps to print out the value and make it able to be referenced by other modules.

1
2
3
4
Output "<VARIABLE NAME>" {
  value = <VALUE>
  # ...
}

Locals Block

Use it to assign a local variables to reduce code duplication.

1
2
3
4
5
locals {
  <VAR>  = <VALUE>
  <VAR2> = <VALUE2>
  # ...
}

Data Block

Data sources allow Terraform to use information defined outside of Terraform, defined by another separate Terraform configuration, or modified by functions.

Data sources are READ-ONLY infrastructure resources.

1
2
3
4
data "<TYPE>" "<NAME>" {
  <KEY1> = <VALUE1>
  # ...
}

Reference expression: data.<TYPE>.<NAME>.<ATTRIBUTE>

References to Values

Input Variables

var.<NAME>

E.g. var.mylist[0] (list), var.members["age"] (map), var.user_information.name (object)

Resource Attributes

<RESOURCE TYPE>.<NAME>.<ATTRIBUTE>

E.g. aws_instance.example.ami, aws_instance.example.ebs_block_device[*].device_name

Local Values

local.<NAME>

From Other Modules

module.<MODULE NAME>.<OUTPUT NAME>

String Templates

Interpolation

${...}

E.g. "Hello, ${aws_instance.myec2.id}"

Heredoc String

<< and <<-

For Loop & If Condition

1
"Hello, %{ if var.name != "" }${var.name}%{ else }unnamed%{ endif }!"
1
2
3
4
5
<<EOT
%{ for ip in aws_instance.example[*].private_ip ~}
server ${ip}
%{ endfor ~}
EOT

Provider Version Constraints

example:

1
2
3
4
5
6
7
8
terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = ">= 1.2.0, < 2.0.0, != 1.3.0"
    }
  }
}

operators:

  • = (or no operator): Allows only one exact version number. Cannot be combined with other conditions.

  • !=: Excludes an exact version number.

  • >, >=, <, <=: Comparisons against a specified version, allowing versions for which the comparison is true. “Greater-than” requests newer versions, and “less-than” requests older versions.

  • ~>: Allows only the rightmost version component to increment. For example, to allow new patch releases within a specific minor release, use the full version number: ~> 1.0.4 will allow installation of 1.0.5 and 1.0.10 but not 1.1.0. This is usually called the pessimistic constraint operator.

Configure Remote State

e.g.

1
2
3
4
5
6
7
terraform {
  backend "s3" {
    bucket = "mybucket"
    key    = "path/to/my/key"
    region = "us-east-1"
  }
}

Provisioners

You can use provisioners to model specific actions on the local machine or on a remote machine in order to prepare servers or other infrastructure objects for service.

  • local-exec

    e.g.

    1
    2
    3
    4
    5
    6
    7
    8
    
     resource "aws_instance" "web" {
       # ...
    
       provisioner "local-exec" {
         command = "echo ${self.private_ip} >> private_ips.txt"
         on_failure = continue  # by default on_failure = continue
       }
     }
    
  • remote-exec

    e.g.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
     resource "aws_instance" "web" {
       # ...
    
       # Establishes connection to be used by all
       # generic remote provisioners (i.e. file/remote-exec)
       connection {
         type     = "ssh"
         user     = "root"
         password = var.root_password
         host     = self.public_ip
       }
    
       provisioner "remote-exec" {
         inline = [
           "puppet apply",
           "consul join ${aws_instance.web.private_ip}",
         ]
       }
     }
    

    Compared with exec provisioners or user data, custom image is usually a batter practice!

  • file

    e.g.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
     resource "aws_instance" "web" {
       # ...
    
       # Copies the myapp.conf file to /etc/myapp.conf
       provisioner "file" {
         source      = "conf/myapp.conf"
         destination = "/etc/myapp.conf"
       }
     }
    

Assign Input Variables

  1. Command Line -var

    terraform apply -var 'image_id=ami-abc123' -var 'availability_zone_names=["us-east-1a","us-west-1c"]'

  2. Variable Definitions Files .tfvars

    terraform apply -var-file="testing.tfvars"

    1
    2
    3
    4
    5
    6
    
     # testing.tfvars
     image_id = "ami-abc123"
     availability_zone_names = [
       "us-east-1a",
       "us-west-1c",
     ]
    
  3. Environment Variables TF_VAR_<NAME>

    export TF_VAR_image_id=ami-abc123

    export TF_VAR_availability_zone_names='["us-east-1a","us-west-1c"]'

Loading Order (from low to high priority):
(1) Environment Variables
(2) terraform.tfvars
(3) *.auto.tfvars (alphabetical order)
(4) -var or -var-file command line flags

Logs

Set Log Levels

export TF_LOG=<log_level>

  • INFO
  • WARNING
  • ERROR
  • DEBUG
  • TRACE

Set Log File Path

export TF_LOG_PATH=<log_file_path>

unset TF_LOG_PATH


Creative Commons License Licensed under CC BY-SA 4.0

Tags: ,

Categories:

Created:

Updated:

Comments