Terraform Quick Start

后来还用到了Python Terraform package, 结合Python Click 以及其他custom Python package 去构造glue code.

Introduction

To alter a planet for the purpose of sustaining life.

This article from IBM can give you a good overview:

  • Terraform vs Kubernetes
  • Terraform vs Ansible

Removing manual build process, adopting declarative approach to deploy infrastructure as code, reusable, idempotent and consistent repeatable deployment.

Use gcloud API or client binary can do the same work as Terraform, so what are the benefits:

  • cloud agnostic, multi-cloud portable.
  • Unified workflow: If you are already deploying infrastructure to Google Cloud with Terraform, your resources can fit into that workflow.
  • Full lifecycle management: Terraform doesn’t only create resources, it updates, and deletes tracked resources without requiring you to inspect the API to identify those resources.
  • Graph of relationships: Terraform understands dependency relationships between resources.

Terraform 文档非常informative,结构清晰: Terraform之于cloud infra on public cloud 就相当于 helm之于application on k8s, 大大简化了操作复杂性,自动快速部署,同时做到了复用,versioning等特性。但得去了解cloud provider中提供的resources 的用途,搭配。

Github Repo

resource files in github: https://github.com/ned1313/Getting-Started-Terraform

Composition

Terraform executable: download from web (or build terraform docker image) Terraform files: using hashicorp configure language DSL Terraform plugin: interact with provider: AWS, GCP, Azure, etc Terraform state file: json and don’t touch it but you can view it to get deployment detial

You can have multiple terraform files: .tf, when run terraform it will stitch them together to form a single configuration. 比如把variables, outputs, resources, tfvars分开。

tfvars file by default named as terraform.tfvars, otherwise when run plan you need to specify the file path. This tfvars file is usually generated from some meta-data configuration, then combining with variable declaration file.

Commands

To execute terraform command, build a docker image and mount cloud auth credentials when start container. BTW, if you run on google compute VM, the SDK will inject the host auth automatucally in container. If you update the terraform file with different configuration, rerun init, plan and apply.

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# list all commands
terraform --help
terraform version

# create workspace, see below section
terraform workspace

# linter
terraform validate

# show a tree of providers in main the sub-modules
# for example, google, kubernetes, random, null, locals they are all providers
terraform providers

# init will download plugin, for example, aws, gcp or azure..
terraform init

# will show you the diff if you update your terraform file
# load terraform.tfvars by default, if not, need to specify
terraform plan -out plan.tfplan


# will generate a tfstate file
# perform creation as much parallel as possible
# --auto-approve: for script, no interactive
terraform apply "plan.tfplan" [--auto-approve]

# -state: output state to specific file
# when run different env with a single definition file
terraform apply -state=qa-env.tfstate -var environment=qa "plan.tfplan"

# Manually mark a resource as tainted, forcing a destroy and recreate
# on the next plan/apply.
terraform taint <google_compute_instance.vm_instance>

# output terraform state or plan file in a human-readable form
# show what has been created
terraform show

# show output variable value
# useful for scripts to extract outputs from your configuration
terraform output [output name]

# Update variables
terraform refresh

# show objects being managed by state file
terraform state list

# destroy Terraform-managed infrastructure
terraform destroy [--auto-approve]

Syntax

Hashicorp configuration language, basic block syntax:

1
2
3
4
5
6
7
block_type label_one [label_two] {
key = value

embedded_block {
key = value
}
}

怎么知道resource的名称呢? find the provider, then search the resources: https://www.terraform.io/docs/configuration/resources.html 还有random provider,比如产生随机数.

Provider

Support mutliple providers, all written in Go. https://www.terraform.io/docs/providers/index.html

Provisioner

在deploy infrastructure 之后的配置操作,比如使用ansible or shell script as privisioners.

Provisioner can be ran in creation or destruction stage, you can also have multi-provisioner in one resources and they execute in order in resource.

Provisioner can be local or remote:

  • file: copy file from local to remove VM instance.
  • local-exec: executes a command locally on the machine running Terraform, not the VM instance itself.
  • remote-exec: executes on remote VM instance.

Terraform treats provisioners differently from other arguments. Provisioners only run when a resource is created, adding a provisioner does not force that resource to be destroyed and recreated. Use terraform taint to tell Terraform to recreate the instance.

Functions

https://www.terraform.io/docs/configuration/functions.html

You can experiment with functions in Terraform console, this can help with troubleshooting.

1
2
3
4
5
6
7
8
9
10
11
12
13
# first run terraform init
# it will auto load tfvars file and variables
terraform console
> lower("HELLO")
> merge(map1, map2)
> file(path)
> min(2,5,90)
> timestamp()
# modular
> 34 % 2
> cidrsubnet(var.network_address_space, 8, 0)
# lookup value in a map
> lookup(local.common_tags, "Bill", "Unknown")

For example:

1
2
3
4
5
6
7
variable network_info {
default = "10.1.0.0/16"
}

# split network range by adding 8 bits, fetch the first one subnet
# 10.1.0.0/24
cidr_block = cidrsubnet(var.network_info, 8, 0)

Resource arguments

https://www.terraform.io/docs/configuration/resources.html#meta-arguments common ones:

  • depends_on: make sure terraform creates things in right order
  • count: create similar resources
  • for_each: create resources not similar
  • provider: which provider should create the resource

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
resource "aws_instance" "web" {
# index start from 0
count = 3
tags {
Name = "web-${count.index + 1}"
}

depends_on = [aws_iam_role_policy.custom_name]
}

resource "aws_s3_bucket" "storage" {
for_each = {
food = "public-read"
cash = "private"
}
# access key and value
bucket = "${each.key}-${var.bucket_suffix}"
acl = each.value
}

Variables

Other way to use variables rather than specifying in single .tf file.

The scenario, we need development, QA(Quality Assurance)/UAT(User Acceptance Testing), production environment, how to implement with one configuration and multiple inputs?

The variable values can be from, precedence from low to high:

  • environment variable: TF_VAR_<var name>.
  • file: terraform.tfvars or specify by -var-file in terraform command.
  • terraform command flags -var.

You can override variables and precedence, select value based on environment, for example:

1
2
3
4
5
6
7
8
9
10
11
# specify default value in tf file
variable "env_name" {
type = string
default = "development"
}

# or specify in tfvars file
env_name = "uat"

# or specify in command line
terraform plan -var 'env_name=production'

Variable types:

  • string, the default type if no explicitly specified
  • bool: true, false
  • number (integer/decimal)
  • list (index start from 0)
  • map, value type can be number, string and bool

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
variable "project" {
type = string
}

variable "web_instance_count" {
type = number
default = 1
}

# list
variable "cidrs" { default = [] }

# map
variable "machine_types" {
# map type and key is string
type = map(string)
default = {
dev = "f1-micro"
test = "n1-highcpu-32"
prod = "n1-highcpu-32"
}
}

machine_resource = lookup(var.machine_types, var.environment_name)

In terraform, the same syntax ${} for interpolation as in bash:

1
2
3
4
5
6
7
8
9
10
# local variable definition
locals {
# random_integer is a terraform resource
tags = "${var.bucket_name_prefix}-${var.environment_tag}-${random_integer.rand.result}"
}

# use
resource "aws_instance" "example" {
tags = local.tags
}

Workspace

Workspace is the recommended way to working with multiple environments, for example:

  • state management
  • variables data
  • credentials management

State file example, we have dev, QA, prod three environments, put them each into separate folder, when run command, specify the input and output:

1
2
3
4
5
6
# for dev environment
# -state: where to write state file
# -var-file: load file
terraform plan -state="./dev/dev.state" \
-var-file="common.tfvars" \
-var-file="./dev/dev.tfvars"

Workspace example, there is a terraform.workspace built-in variable can be used to indicate the workspace currently in, then use it in map variable to select right value for different environment. (不用再去分别创建不同的folder for different environment了)

1
2
3
4
5
6
7
8
9
10
11
12
13
# create dev workspace and switch to it
# 类似于git branch
terraform workspace new dev
# show workspace
terraform workspace list
terraform plan -out dev.tfplan
terraform apply "dev.tfplan"

# now create QA workspace
terraform workspace new QA

# switch workspace
terraform workspace select dev

Special terraform variable to get workspace name

1
2
3
locals {
env_name = lower(terraform.workspace)
}

Managing secrets

Hashicorp Vault is for this purpose. it can hand over credentials from cloud provider to terraform and set ttl for the secrets.

Or you can use environment variable to specify the credentials, terraform will pick it automatically, but bear in mind to use the right env var name. For example:

1
2
3
# 注意这个和前面的TF_VAR_<var name> 不一样,这里是secret
export AWS_ACCESS_KEY_ID=xxx
export AWS_SECRET_ACCESS_KEY=xxx

Module

Make code reuse eaiser: https://www.terraform.io/docs/configuration/modules.html

Terraform registry, similar concept with Helm, Docker: https://registry.terraform.io/ Using module block to invoke local or remote modules.

  1. root module
  2. support versioning
  3. provider inheritance

Module components:

  • variables input
  • resources
  • output values (calling part will take this in)

Google Cloud Platform

Good tutorial: https://learn.hashicorp.com/tutorials/terraform/google-cloud-platform-build

If run terraform apply get permission issues, add the service account used to IAM, than grant it roles. Then retry the apply command.

用Terraform 建造的VM instance network 没有ssh allow firewall rule, 要自己添加: https://cloud.google.com/compute/docs/troubleshooting/troubleshooting-ssh

Terrafrom provisions GKE with additional node pool: https://learn.hashicorp.com/terraform/kubernetes/provision-gke-cluster

Resources

Commonly use reource types for terraform resource block:

  • google_compute_network
  • google_compute_instance
  • google_compute_address
  • google_storage_bucket
  • google_container_cluster
  • google_container_node_pool
0%