Migrating Local Terraform

CLI Migration

  1. Check version: the cloud block is available in Terraform v1.1, plus a Terraform account
  2. add the cloud block to your backend configuration
    1
    2
    3
    4
    5
    6
    7
    8
    terraform {
    cloud {
    organization = "my-org"
    workspaces {
    tags = ["networking"]
    }
    }
    }
  3. specify one or more Terraform Cloud workspaces for the state files
  4. run terraform init

API migration

Integrations

VCS supported

  • GitHub
    • GitHub.com
    • GitHub.com (OAuth)
    • GitHub Enterprise
  • GitLab
    • GitLab.com
    • GitLab EE and CE
  • Bitbucket
    • Bitbucket Cloud
    • Bitbucket Server
  • Azure
    • Azure DevOps Server
    • Azure DevOps Services

How it works

  • Terraform Cloud needs:

    • access a list of repositories, to let you search for repos when creating new workspaces.
    • register webhooks with your VCS provider, to get notified of new commits to a chosen branch.
    • download the contents of a repository at a specific commit in order to run Terraform with that code.
  • Webhooks: monitor new commits and pull requests.

    • When someone adds new commits to a branch, any Terraform Cloud workspaces based on that branch will begin a Terraform run. Usually a user must inspect the plan output and approve an apply, but you can also enable automatic applies on a per-workspace basis. You can prevent automatic runs by locking a workspace.
    • When someone submits a pull request/merge request to a branch, any Terraform Cloud workspaces based on that branch will perform a speculative plan with the contents of the request and links to the results on the PR’s page. This helps you avoid merging PRs that cause plan failures.
  • SSH keys: required for Azure and Bitbucket, only for cloning. All other operations can be done by HTTPS.

Workspaces in Terraform Cloud

Component Local Terraform Terraform Cloud
Terraform configuration On disk In linked version control repository, or periodically uploaded via API/CLI
Variable values As .tfvars files, as CLI arguments, or in shell environment In workspace
State On disk or in remote backend In workspace
Credentials and secrets In shell environment or entered at prompts In workspace, stored as sensitive variables
  • It uses workspaces instead of directories
    • State versions: Each workspace retains backups of its previous state files. Although only the current state is necessary for managing resources, the state history can be useful for tracking changes over time or recovering from problems.
    • Run history: When Terraform Cloud manages a workspace’s Terraform runs, it retains a record of all run activity, including summaries, logs, a reference to the changes that caused the run, and user comments.
  • After creating a workspace
    • Allows to edit variables
      • terraform.tfvars or -var-file=terraform.tfvars
      • environment variables with export on shell
      • special environment variables (TF_PARALELISM sets up flag: terraform plan -paralelism<N> and terraform apply -paralelism<N>))
    • Creates a webhook with VCS

Runs in Terraform Cloud

  • Workspace has runs links.
  • CLI or API

Run workflows

  • UI/VCS-driven: primary mode of operation
  • API-driven: more flexible but requires you to create some tooling
  • CLI-driven: uses Terraform’s standard CLI tools to execute runs in Terraform Cloud

Environment

  • Terraform workersVM: single-use Linux VMs
  • Network access to VCS and Infra providers: needs access to all resources managed
  • Concurrency and run queueing: uses multiple workers
  • State access and authentication: stores state for its workspaces

Environment variables

Variable Name Description Example
TFC_RUN_ID A unique identifier for this run run-CKuwsxMGgMd4W7Ui
TFC_WORKSPACE_NAME The name of the workspace used in this run prod-load-balancers
TFC_WORKSPACE_SLUG The full slug of the configuration used in this run. This consists of the organization name and workspace name, joined with a slash acme-corp/prod-load-balancers
TFC_CONFIGURATION_VERSION_GIT_BRANCH The name of the branch that the associated Terraform configuration version was ingressed from main
TFC_CONFIGURATION_VERSION_GIT_COMMIT_SHA The full commit hash of the commit that the associated Terraform configuration version was ingressed from abcd1234...
TFC_CONFIGURATION_VERSION_GIT_TAG The name of the tag that the associated Terraform configuration version was ingressed from v0.1.0

Run States and Stages

graph TD

subgraph PENDING
A[pending]
end

subgraph PLAN
B[fetching]
C[planning]
D[confirmation]
end

subgraph POST-PLAN
E[running tasks]
end

subgraph COST-ESTIMATION
F[cost estimating]
G[cost estimated]
end

subgraph POLICIY CHECK
H[policy check]
I[policy override]
J[policy checked]
end

subgraph APPLY
K[cost estimating]
end

subgraph COMPLETED
T[applied]
U[applied errored]
V[plan errored]
W[planned and finished]
X[cancelled]
Y[plan errored]
Z[discarded]
end

A -- user discards before start --> Z
A -- 1st in queue --> B

B -- can not connect to VCS --> Y
B -- success --> C
C -- cancel --> X
C -- terraform plan failed --> Y
C -- success and no further policy stages required --> W
C -- success --> D
D -- success, run tasks enabled --> E
D -- success, cost estimation enabled --> F
D -- success, no cost estimation, sentinel policies --> F
D -- success, no cost estimation, no policies, auto-apply --> K
D -- success, no cost estimation, no policies, no get authorize manually --> D
D -- success, no cost estimation, no policies, reject authorize manually --> Z

E -- mandatory tasks failed --> V
E -- advised tasks failed --> K
E -- user cancels --> X

F -- success --> G
F -- no policy checks nor applies -->  W
G -- success --> H

H -- hard policy fails --> V
H -- soft policy fails --> I
H -- success --> J
I -- user overrides --> J
I -- user discards --> Z
J -- auto-applied or manual apply --> K
J -- duser discards --> Z

K -- success --> T
K -- fails --> U
K -- user cancels --> Z
  • Pending Stage: Terraform Cloud hasn’t started action on a run yet. Terraform Cloud processes each workspace’s runs in the order they were queued, and a run remains
    • Pending state: Terraform Cloud hasn’t started action on a run yet. Terraform Cloud processes each workspace’s runs in the order they were queued, and a run remains pending until every run before it has completed.
  • The Plan Stage: A run goes through different steps during the plan stage depending on whether or not Terraform Cloud needs to fetch the configuration from VCS. Terraform Cloud automatically archives configuration versions created through VCS when all runs are complete and then re-fetches the files for subsequent runs.
    • Fetching: If Terraform Cloud has not yet fetched the configuration from VCS, the run will go into this state until the configuration is available.
    • Planning: Terraform Cloud is currently running terraform plan.
    • Needs Confirmation: terraform plan has finished. Runs sometimes pause in this state, depending on the workspace and organization settings.
  • Post-Plan Stage: The post-plan phase executes any configured run tasks after the plan is complete, so it only occurs if there are run tasks enabled for the workspace. All runs can enter this phase, including speculative plans. During this phase, Terraform Cloud sends information about the run to the configured external system and waits for a passed or failed response to determine whether the run can continue.
    • Running tasks: Terraform Cloud is waiting for a response from the configured external system(s).
      • External systems must respond initially with a 200 OK acknowledging the request is in progress. After that, they have 10 minutes to return a status of passed, running, or failed, or the timeout will expire and the task will be assumed to be in the failed status.
  • Cost Estimation Stage: only occurs if cost estimation is enabled. After a successful terraform plan, Terraform Cloud uses plan data to estimate costs for each resource found in the plan.
    • Cost Estimating: Terraform Cloud is currently estimating the resources in the plan.
    • Cost Estimated: The cost estimate completed.
  • Policy Check Stage: This stage only occurs if Sentinel policies are enabled. After a successful terraform plan, Terraform Cloud checks whether the plan obeys policy to determine whether it can be applied.
    • Policy Check: Terraform Cloud is currently checking the plan against the organization’s policies.
    • Policy Override: The policy check finished, but a soft-mandatory policy failed, so an apply cannot proceed without approval from a user with permission to manage policy * overrides for the organization. The run pauses in this state.
    • Policy Checked: The policy check succeeded, and Sentinel will allow an apply to proceed. The run sometimes pauses in this state, depending on workspace settings.
      Leaving this stage:
  • Apply Stage
    • Applying: Terraform Cloud is currently running terraform apply.
  • Completion
    • Applied: The run was successfully applied.
    • Planned and Finished: terraform plan’s output already matches the current infrastructure state, so terraform apply doesn’t need to do anything.
    • Apply Errored: The terraform apply command failed, possibly due to a missing or misconfigured provider or an illegal operation on a provider.
    • Plan Errored: The terraform plan command failed (usually requiring fixes to variables or code), or a hard-mandatory Sentinel policy failed. The run cannot be applied.
    • Discarded: A user chose not to continue this run.
    • Canceled: A user interrupted the terraform plan or terraform apply command with the “Cancel Run” button.

Users, Teams, and Organizations

  • Users: individual members
  • Teams: group of users
    • Owner team: can manage an organization
  • Organizations: shared space of teams to collaborate on workspaces

Migration example

  1. Check terraform infra locally
  2. Generate your access keys and API keys on Terraform Cloud
  3. Set up you workspace session on https://app.terraform.io/session
  4. Create Your API Token for Terraform CLI Login
  5. Prepare your backend configuration on main.tf
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
      terraform {
    backend "remote" {
    organization = "<YOUR ORG NAME>"
    workspaces {
    name = "lab-migrate-state"
    }
    }

    required_providers {
    aws = {
    source = "hashicorp/aws"
    version = "~> 3.27"
    }
    }
    }
  6. On CLI
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # INIT
    # login
    terraform login
    # format files
    terraform fmt
    terraform init
    # verify terraform.tfstate.backup exists
    ls
    # clean up
    rm -rf terraform.tfstate

    # APPLY
    terraform apply
    # confirm state on cloud via browser

CLI to manipulate a Terraform deployment

  • Task

      graph TD
    A[Main Terraform code]
    B[default workspace]
    C[test workspace]
    D[default workspace state file is terraform.tfstate]
    E[test workspace state file resides in terraform.tfstate.d directorys]
    F(terraform.workspace value is default)
    G(terraform.workspace value is test)
    H(deploys to PRO account)
    I(deploys to staging account)
    
    A --> B
    B --> D
    D --> F
    F --> H
    A --> C  
    C --> E
    E --> G
    G --> I
  • Terraform code

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # depending on the workspace, we select different regions
    provider "aws" {
    region = terraform.workspace == "default" ? "us-east-1" : "us-west-2"
    }

    # Get Linux AMI ID using SSM Parameter endpoint in us-east-1
    data "aws_ssm_parameter" "linuxAmi" {
    name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
    }

    # Create and bootstrap EC2 in us-east-1
    resource "aws_instance" "ec2-vm" {
    ami = data.aws_ssm_parameter.linuxAmi.value
    instance_type = "t3.micro"
    associate_public_ip_address = true
    vpc_security_group_ids = [aws_security_group.sg.id]
    subnet_id = aws_subnet.subnet.id
    tags = {
    Name = "${terraform.workspace}-ec2"
    }
    }
  • CLI commands required

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    # WORKSPACES
    # check which workspaces we have. WeAt least we get default
    terraform workspace list
    terraform workspace new test
    # check we have automatically switched to the new one
    terraform workspace list

    # DEPLOY ON TEST WORKSPACE
    terraform init
    # avoid inputing "yes" all the time with auto-approve
    terraform apply --auto-approve
    # check deployment
    terraform state list

    # DEPLOY ON DEFAULT
    terraform workspace select default
    # check we are indeed on default
    terraform workspace list
    # check state is empty for this workspace
    terraform state list
    terraform init
    # avoid inputing "yes" all the time with auto-approve
    terraform apply --auto-approve
    # check deployment
    terraform state list
    # check there is a terraform.tfstate.d directory, which contains the test workspace
    ls

    # CLEAN UP TEST
    terraform workspace select test
    terraform workspace destroy --auto-approve
    terraform workspace select default
    terraform workspace delete test

Build and test Terraform module

  • Tasks

      graph LR
    
    subgraph Modules
    A[main.tf]
    B[variables.tf]
    C[outputs.tf]
    end
    
    D[main_code.tf]
    E(EC2 VM)
    
    B -- variables:inputs --> D
    C -- outputs returned by module --> D
    D --> E
  • Terraform code

    • Terraform VPC module
      • main.tf
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        # vpc module
        provider "aws" {
        region = var.region
        }

        resource "aws_vpc" "this" {
        cidr_block = "10.0.0.0/16"
        }

        resource "aws_subnet" "this" {
        vpc_id = aws_vpc.this.id
        cidr_block = "10.0.1.0/24"
        }

        data "aws_ssm_parameter" "this" {
        name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
        }
      • variables.tf
        1
        2
        3
        4
        variable "region" {
        type = string
        default = "us-east-1"
        }
      • outputs.tf
        1
        2
        3
        4
        5
        6
        7
        # critical to exporting values
        output "subnet_id" {
        value = aws_subnet.this.id
        }
        output "ami_id" {
        value = data.aws_ssm_parameter.this.value
        }
    • Terraform project module
      • main.tf
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        variable "main_region" {
        type = string
        default = "us-east-1"
        }

        provider "aws" {
        region = var.main_region
        }

        # invokes the VPC module
        module "vpc" {
        source = "./modules/vpc"
        region = var.main_region
        }

        resource "aws_instance" "my-instance" {
        ami = module.vpc.ami_id
        subnet_id = module.vpc.subnet_id
        instance_type = "t2.micro"
        }
      • outputs.tf
        1
        2
        3
        4
        output "PrivateIP" {
        description = "Private IP of EC2 instance"
        value = aws_instance.my-instance.private_ip
        }
  • CLI

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    # check terraform status
    terraform version

    # create project structure
    mkdir terraform_project
    cd terraform_project
    mkdir -p modules/vpc
    cd /home/cloud_user/terraform_project/modules/vpc/

    # main project directory
    cd ~/terraform_project

    # DEPLOY AND TEST
    # format the code in all of your files pre-deployment
    terraform fmt -recursive
    terraform init
    terraform validate
    # review actions that will be performed when you deploy
    terraform plan
    # deploy
    terraform apply --auto-approve
    # check
    terraform state list

    # CLEAN UP
    terraform destroy

CLI

Basics

1
2
3
4
5
6
7
8
9
10
11
12
# check
terraform version
# switch to working directory
terraform -chdir=<path_to/tf> <subcommand>
# initialize directory
terraform init
# create ane execution plan
terraform plan
# apply changes
terraform apply
# clean up
terraform destroy

Plan deploy and clean up

1
2
3
4
5
6
7
8
9
10
11
12
# output a deployment plan
terraform plan -out <plan_name>
# output a destroy plan
terraform plan -destroy
# apply an specific plan
terraform apply <plan_name>
# only apply changes to targeted resources
terraform apply -target=<resource_name>
# pass a variable via the command line
terraform apply -bar my_variable=<variable>
# get provider info used in configuration
terraform providers

Syntax

Hashicorp Configuration Language (HCL)

tf syntax (default HCL)

  • Documentation

  • Syntax

    1
    2
    3
    4
    5
    6
    7
    8
    resource "aws_vpc" "main" {
    cidr_block = var.base_cidr_block
    }

    <BLOCK TYPE> "<BLOCK LABEL>" "<BLOCK LABEL>"{
    # block body
    <IDENTIFIER> = EXPRESSION # argument
    }
  • Components

    • blocks: containers for objects like resources.
    • arguments: assign a value to a name.
    • expressions: represent a value.
    • identifers: names (types, resources, variables)
    • commnents
      1
      2
      3
      4
      5
      6
      # single line comment
      // single line comment
      /*
      multi-line comment
      multi-line comment
      */
  • Example .tf

    1
    2
    3
    4
    5
    6
    7
    variable "example": {
    default = "hello"
    }
    resource "aws_instance" "example": {
    instance_type = "t2.micro"
    ami = "ami-abc123"
    }

tf.json syntax

  • Example tf.json
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    {
    "variable": {
    "example": {
    "default": "hello"
    }
    }
    }

    {
    "resource": {
    "aws_instance": {
    "example": {
    "instance_type": "t2.micro",
    "ami": "ami-abc123"
    }
    }
    }
    }

Directories and files

  • file extension: .tf or .tf.json.
  • encoding: UTF-8 (recommended) or CLRF.
  • modules
    • collection of Terraform files on a directory.
    • module: a module is the top level config files on directory. Nested directories are not included, they are considered other modules.
    • root module: working directory wjere Terrafrom is invokled. Terraform configuration consist of root module + some child modules.

Resources

  • Definition

    1
    2
    3
    4
    resource "aws_instance" "web" {
    ami = "ami-a1b2c3d4"
    instance_type = "t2.micro"
    }
  • Resource types

    • providers: plugins for Terraform, offers a collection for resource types
    • arguments: specific to the selected resource typoes
    • documentation: which every provioder uses to describes its resource types and arguments
  • Meta arguments

    • depends_on: specify hidden dependencies
    • count: create multiple resource instances according to a count
    • for_each: create multiple instances according to a map or set of strings
    • provider: select a non-default provider configuration
    • lifecyle: set lifecycle configurations
    • provisioners and connection: take extra actions after resource creation
  • Operation timeouts

    1
    2
    3
    4
    5
    resource "aws_db_instance" "example" {
    # ...
    create = "60m"
    delete = "2h"
    }
  • How to apply configs

    • create: create resources that exist in the config, but are not associated with real infra object in the state.
    • destroy: destroy resources that exist in the state but no longer in the config.
    • update in-place: update in-place resources whose arguments have existed.
    • destroy and re-create: destroy and re-create resources whose arguments have changed, but which cannot be updated in-place due to remote API limitations.
  • Resource behaviour

    • accessing resource attributes
      • expression with Terraform modules.
      • read-only attributes with info obtained from remote APIs.
      • providers included data sources.
    • resource dependencies
      • most secure dependencies are handled automatically.
      • Terraform analyzes expressions within a resource block to find references to other objects, and treats those referencies as ordering requirements when creating, updating or destroying resources.
      • it’s usually not mecessary to manually specify dependencies cannot be recognized implicitly in the configuration.
    • local resources only
      • specialized resources types that operate only within Terraform itself, calculating some results and saving those results in the state for future use (ssh-keys, self-signed certs…)

Variables

Input

  • Reserved words

    • source
    • version
    • providers
    • count
    • for_each
    • lifecycle
    • depends_on
    • local
  • Optional arguments for variable declarations

    • default (it makes the var optional)
    • type (types accepted)
      • string
      • number
      • bool
      • list<type>
      • set<type>
      • map<type>
      • object({<attribute> = <type>, ...})
      • tuple[<type>, ...]
    • description (documentation)
    • validation (block validation rules, in addition to types)
    • sensitive (limit Terraform UI output)
  • Examples

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    variable "image_id" {
    type = string
    description = "The id of the machine (AMI) to use for the server"

    validations {
    # check access to values uin var
    condition = length(var.image_id) > 4 && substr(var.image.id, 0, 4)=="ami-"
    error_message = "The image id value must be a valid AMI id, starting with \"ami-\"."
    }
    }

    variable "user_information" {
    type = object ({
    name = string
    address = string
    })
    sensitive = true
    }
  • Assign value to variables

    • Environment variables
      1
      2
      export TF_VAR_image_id=ami_123abc
      terraform plan
    • variable definition files (.tfvars, ..tfvars.json)
      • Examples
        1
        terraform apply -var-file="testing.tfvars"
        1
        2
        3
        4
        5
        image_id = "ami-123abc"
        availability_zone_names = [
        "eu-west-1",
        "eu-west-2"
        ]
      • Automatically loaded files
        • terraform.tfvars, terraform.tfvars.json
        • any files ending with .auto.tfvars, .auto.tfvars.json
    • CLI: -var
      1
      terraform apply -var="image_id=ami-123abc" -var='image_list=["ami-456drf","image_id=ami-789ghi"]'
    • Terrafrom workspace

Output

  • Definition

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    output "instance_ip_addr"
    availability_zone_names = aws_instance.server.private_ip
    description = "The private IP od the main server instance"

    # last resort, add comment explaining why
    depends_on[
    /*
    security group rule must be created before this IP address could be
    actually used, otherwise the services will be unreachable
    */
    aws_security_group_rule.local._access
    ]
    }
  • Arguments and constraints

    • module.<module_name>.<output_names>
    • optional
      • description
      • sensitive
      • depends_on

Local

  • Like function temporary local variables
    1
    2
    3
    4
    5
    6
    resource "aws_instance" "example" {
    # ...
    # local can only be accessed in expressions
    # within the module in which they are declared
    tags = local.common_tags
    }

Modules

Definition

  • Collection of .tf or .tf.json files kept together on a directory

  • Types (encapsulated)

    • Root module (files in main working directory)
      1
      2
      3
      4
      module "aws_elb" "example" {
      # retrieve a value from child package
      instances = modules.servers.instance_ids
      }
    • Child modules (called by the root module, can declare output to export to caller)
      1
      2
      3
      4
      module "servers" {
      source = "./app-cluster"
      servers = 5
      }
    • Published modules (downloaded from private or public registry)
      1
      2
      3
      4
      5
      module "servers" {
      source = "./hashicorp/consul/aws"
      version = "0.0.5"
      servers = 3
      }
      • Arguments for calling
        • source (required)
        • version (fopr published modules)
        • input variables (defiend in the module)
        • meta_arguments (for-each, depends_on, count, providers)
  • Refactoring code

    • Terraform can see the new location of the module block as an entorely different resource.
    • Use terraform state mv to inform that the block was moved
      • When passing resource addresses, use prefix module.<module_name>
      • If called with count or for each, then use prefix module.<module_name>[index]
    • Taint (deprecated, use Replace) a resource in a module
      1
      2
      # terraform taint module.salt_master.aws_instance.salt_master
      terraform apply -replace="aws_instance.example[0]"

Module Sources

  • Used during terraform init
  • Types
    • Local path
      1
      2
      3
      module "consult" {
      source = "./consult"
      }
    • Module registry
      • Terraform public registry
        1
        2
        3
        4
        module "consul" {
        source = "hashicorp/consul/aws"
        version = "0.1.0"
        }
      • Other registry
        1
        2
        3
        4
        5
        6
        module "consul" {
        # namespace = "app.terraform.io/namespace"
        # provider = azurerm
        source = "app.terraform.io/example-corp/k8s-cluster/azurerm"
        version = "1.1.0"
        }
      • Git repository
        1
        2
        3
        4
        5
        6
        module "vpc" {
        source = "git::https:://hashicorp/consul/vpc.git"
        }
        module "storage" {
        source = "git::ssh:://username@example.com/storage.git?ref=v1.2.0"
        }
      • Mercurial repository
        1
        2
        3
        module "vpc" {
        source = "hg::https:://hashicorp/consul/vpc.hg"
        }
      • HTTP Terraform get (zxip, tar.bz, tar.gz, tar.xz)
        1
        2
        3
        module "vpc" {
        source = "https:://example.com/vpc-module.zip"
        }
    • SaaS examples (unprefixed URLs or SSH)
      • Cloud Hosted Terraform: "app.terraform.io/namespace"
      • GitHub: "github.com/hashicorp/example" or "git@github.com:hashicorp/example.git"
      • Bitbucket public repos: "bitbucket.org/hashicorp/example"
      • AWS S3: s3::https://s3-eu-west-1.amazonws.com/examplecorp/vpc-module.zip
        • Uses environment variables AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, or ./AWS/credentials file, or EC2 instance profile.
      • GCS bucket (Google): gcs::https://googleapis.com/storage/v1/modules/vpc-module.zip
        • Uses environment variables AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, or ./AWS/credentials file, or EC2 instance profile.

Expressions and Functions

  • Expressions: reference or compute values on a configuration

    • Types: string, number, bool, list/tuple, map/object, null
    • Named values available: resources, input variables, lcoal values, child module outputs, data sources, file system and workspace info, block-local values
    • Conditionals: condition ? true_val : false_val
  • Functions: transform and combine values on expressions

    • FUNCION_NAME(<ARGUMENT_1>,<ARGUMENT_2>)
    • You can test them directy on CLI via terraform console
      1
      2
      3
      terraform console
      max(3,2)
      exit

State

  • State is used by Terraform to map real world resources to your configuration, keep track of metadata, and to improve performance for large infrastructures.
  • Stored by default in a local file named terraform.tfstate (it is recommended to save a backup), but it can also be stored remotely, which works better in a team environment.

Backend Configuration

  • Storage for Terraform snapshots
  • Each Terraform config can specify a backend (local or remote) for state storage
  • Only used by Terraform CLI top determine
    • where the state is stored
    • where the operations are performed
  • Backend supports multiple workspaces: AzureRM, Consul, COS, GCS, Kubernetes, Local, Manta, Posgres, Remote, S3
  • Backend block
1
2
3
4
5
6
7
8
9
10
terraform {
# only one backend block
backend "remote" {
# can not refer to named values
organization = "corp_example"
workspaces {
name = ex-app-prod
}
}
}
  • When it changes in a config, you must re-run terraform init
  • When it changes, Terraform gives you the option to migrate to your state

Types

Local

1
2
3
4
5
6
terraform {
backend "local" {
path = "/path/to/terraform.tfstate"
# workspace_dir path to non-default workspaces
}
}
1
2
3
4
5
6
7
# datasource configuration
data "terraform_remote_state" "zland" {
backend "local"
config = {
path = "${path.module}/../../terraform.tfstate"
}
}

Remote

1
2
3
4
5
6
7
8
9
terraform {
backend "remote" {
hostname = "app.terraform.io"
organization = "company"
workspaces {
name = "my-prod-app"
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
# datasource configuration
data "terraform_remote_state" "foo" {
backend "remote"
config = {
# required
organization = "company"
workspaces {
name = "my-prod-app"
# optional: prefix
}
# optional: token, hostname
}
}

S3

1
2
3
4
5
6
7
terraform {
backend "s3" {
bucket = "mybucket"
key = "path/to/my/key"
region = "eu-west-1"
}
}
1
2
3
4
5
6
7
8
9
# datasource configuration
data "terraform_remote_state" "foo" {
backend "s3"
config {
bucket = "terraform-state-prod"
key = "terraform.tfstate"
region = "eu-west-1"
}
}

Azurerm

1
2
3
4
5
6
7
8
terraform {
backend "azurerm" {
resource_group_name = "StorageAccount-ResourceGroup"
storage_account_name = "abcd1234"
container_name = "tfstate"
container_key = "prod.terraform.tfstate"
}
}
1
2
3
4
5
6
7
8
9
10
11
12
# datasource configuration
data "terraform_remote_state" "foo" {
backend = "azurerm" {
storage_account_name = "abcd1234"
container_name = "tfstate"
container_key = "prod.terraform.tfstate"
# optional
use_azuread_auth = true
subscription_id = "00000000-0000-0000-000000000000"
tenant_id = "00000000-0000-0000-000000000000"
}
}

Working with State

  • Uses of state

    • Metadata: track resources dependencies
    • Performance: as cache
    • Sync: share file between team members (remote, as read-only)
  • Lock

    • State locking happens automatically on all operations that could write state. You won’t see any message that it is happening.
    • If state locking fails, Terraform will not continue.
    • Can be unlocked terraform force-unlock LOCK_ID (not recommended)

Workspaces

  • Workspaces are separate instances of state data, that can be used from the same working directory.
  • You can use workspaces to manage multiple non-overlapping groups of resources with the same configuration.
    • Every initialized working directory has at least one workspace. (If you haven’t created other workspaces, it is a workspace named default.)
    • For a given working directory, only one workspace can be selected at a time.
    • Most Terraform commands (including provisioning and state manipulation commands) only interact with the currently selected workspace.
    • Use the terraform workspace select command to change the currently selected workspace.
    • Use the terraform workspace list, terraform workspace new, and terraform workspace delete commands to manage the available workspaces in the current working directory.

Plan, deploy and cleanup infrastructure

Command Action
terraform apply --auto-approve Apply changes without being prompted to enter “yes”
terraform destroy --auto-approve Destroy/cleanup deployment without being prompted for “yes”
terraform plan -out plan.out Output the deployment plan to plan.out
terraform apply plan.out Use the plan.out plan file to deploy infrastructure
terraform plan -destroy Outputs a destroy plan
terraform apply -target=aws_instance.my_ec2 Only apply/deploy changes to the targeted resource
terraform apply -var my_region_variable=us-east-1 Pass a variable via command—line while applying a configuration
terraform apply -lock=true Lock the state file so it can’t be modified by any other Terraform apply ormodification action (possible only where backend allows locking)
terraform apply refresh=false Do not reconcile state file with real—world resources(helpful with large complex deployments for saving deployment time)
terraform apply --parallelism=5 Number of simultaneous resource operations
terraform refresh Reconcile the state in Terraform state file with real-world resources
terraform providers Get information about providers used in current configuration

Terraform Workspaces

Command Action
terraform workspace new mynewworkspace Create a new workspace
terraform workspace select default Change to the selected workspace
terraform workspace list List out all workspaces
terraform workspace show List out all workspaces
terraform workspace delete example Display the current workspace

Terraform state manipulation

Command Action
terraform state show aws_instance.my_ec2 Show details stored in Terraform state for the resource
terraforn1 state pull > terraform.tfstate Download and output terraform state to a file
terraform state mv aws_iam_role.my_ssm_role module.custom_module Move a resource tracked via state to different module
terraform state replace-provider hashicorp/aws registry.custom.com/aws Replace existing provider with another
terraform state list List all the resources tracked in the current state file
terraform state rm aws_instance.myinstace Unmanage a resource, delete it from Terraform state file

Terraform Import and Outputs

Command Action
terraform import aws_instance.new_ec2_in-stance i-abcd1234 Import EC2 instance with id i—abcd1234 into the Terraform resource named “new_ec2_instance” of type “aws_instance”
terraform import 'aws_instance.new_ec2_instance[0]' i-abcd1234 Same as above, imports a real-world resource into an instance of Terraform resource
terraform output List all outputs as stated in code
terraform output instance_public_ip List a specific declared output
terraform output -json List all outputs in JSON

Format, automplete amd validate

Command Action
terraform -instaII-autocomplete Setup tab auto-completion, requires logging back in
terraform fmt Format code per HCL canonical standard
terraform validate Validate code for syntax
terraform validate -backend=false Validate code skip backend validation

Initialize your Terraform working directory

Command Action
terraform init Initialize directory, pull down providers
terraform init -get-plugins=false Initialize directory, do not download plugins
terraform init -verify-plugins=false Initialize directory, do not verify plugins for Hashicorp signature

Terraform miscellaneous commands

Command Action
terraform version Display Terraform binary version, also warns if version is old
terraform get -update=true Download and update modules in the ”root” module

Terraform Console (Test out Terraform interpolations)

  • Echo an expression into terraform console and see its expected result as output
    1
    echo 'join(",",["foo",'bar"])'| terraform console
  • Terraform console also has an interactive CLI just enter “terraform console”
    1
    echo '1 + 5' | terraform console
  • Display the Public IP against the “my_ec2” Terraform resource as seen in the Terraform state file
    1
    echo "aws_instanoe.my_ec2.public_ip" | terraform console

Terraform Graph (dependency graphing)

Command Action
terraform graph I dot -Tpng > graph.png Produce a PNG diagram showing relationship and dependencies between Terraform resources in your configuration/code

Terraform Taint/Untaint and Replace

Command Action
terraform replace aws_instance.my_ec2 Taint resource to be recreated on next apply
terraform taint aws_instance.my_ec2 Taint resource to be recreated on next apply (deprecated)
terraform untaint aws_instance.my_ec2 Remove taint from a resource
terraform force-unlock LOCK_ID Force-unlock a locked state file, LOCK_ID provided when locking the State file beforehand

Terraform Cloud

Command Action
terraform login Obtain and save API token for Terraform cloud
terraform logout Log out of Terraform Cloud, defaults to hostname app.terraform.io

Introduction to TF

  • Build, change, version infrastructure safely and efficienty, locally or on the cloud.
graph LR
  A[terraform_config.tf]
  B[terraform.tfvars]
  C[Terraform]
  D[Terraform.tfstate]
  E[Cloud provider]
  F[Cloud environment]
  G[Team working with terraform]
  H[Terraform Cloud]

  A --> C
  B --> C
  C --> D
  C --> E
  E --> C
  E --> F
  F --> E
  D --> H
  H --> D
  G --> H
  • Components
    • Infrastructure as code
      • Terraform Configuration Language: describes it using a high-level configuration syntax
      • versioned: reused and shared
    • Execution plans
      • Planning steps: avoid any surprises when Terraform manipulates infrastructure.
    • Resource graph
      • build infra as efficiently as possible
      • operators get insight into dependencies and resources
    • Change automation
      • applied complex changes with minimal interaction.
      • combination of the execution plan and resource graph, you will know exactly what Terraform will change and in what order
      • avoid many possible human errors

Setting Up Your Environment

Instalation

  • Pre-compiled binary: download and add to path

    • configure automplete by adding the following line to ~/.bashrc, and restart shell after it
      1
      terraform -install-autocomplete
  • OS package manager

    • Windows: Chocolatey
    • Mac: homebrew
    • Linux: depends on distro and package manager
      1
      2
      3
      4
      sudo yum install -y yum-utils
      yum-config-manager --add-repo https:://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
      sudo yum -y install terraform
      terraform --help

Nice to have

  • Hasicorp Terraform syntax highlight and autompletion plugin/extension for your IDE
0%