What's new in Terraform 0.12
Erik R. Rygg
Enterprise Architect
erik@hashicorp.com | @errygg
1
© 2019 HashiCorp
Terraform
Write, Plan, and Create Infrastructure as Code
2
© 2019 HashiCorp
Infrastructure as Code
Infrastructure as Code
3
© 2019 HashiCorp
Configuration Syntax
Infrastructure as Code
4
© 2019 HashiCorp
State
Infrastructure as Code
5
© 2019 HashiCorp
Terraform 0.11
`random_pet` Simple Example
6
© 2019 HashiCorp
variable "count" {� default = 1�}��variable "default_prefix" {� default = "linus"�}��resource "random_pet" "my_pet" {� count = "${var.count}"� prefix = "${var.default_prefix}"�}�
Terraform 0.11 Examples
Terraform
0.11
main.tf
7
CODE EDITOR
© 2019 HashiCorp
terraform apply��random_pet.my_pet: Creating...� length: "" => "2"� prefix: "" => "linus"� separator: "" => "-"�random_pet.my_pet: Creation complete after 0s (ID: linus-excited-goldfish)��Apply complete! Resources: 1 added, 0 changed, 0 destroyed.��Outputs:��pet_names = [� Pet: linus-excited-goldfish�]�
Terraform 0.11 Examples
Terraform
0.11
8
TERMINAL
© 2019 HashiCorp
Terraform 0.11
`random_pet` Example - a bit more complicated
9
© 2019 HashiCorp
Terraform 0.11
main.tf
Terraform 0.11 Examples
variable "count" { default = 1 }�variable "default_prefix" { default = "linus" }��variable "zoo_enabled" {� default = "0"�}��variable "prefix_list" {� default = []�}��resource "random_pet" "my_pet" {� count = "${var.count}"� prefix = "${var.zoo_enabled == "0" ?
var.default_prefix :
element(var.prefix_list, count.index)}"�}��
10
CODE EDITOR
© 2019 HashiCorp
Terraform 0.11
terraform.tfvars
Terraform 0.11 Examples
zoo_enabled = "1"�prefix_list = [ "linus", "cheetarah", "li-shou"]�count = 3�
11
CODE EDITOR
© 2019 HashiCorp
Terraform 0.11
Terraform 0.11 Examples
random_pet.my_pet[1]: Creating...� length: "" => "2"� prefix: "" => "cheetarah"� separator: "" => "-"�[…] ��Apply complete! Resources: 3 added, 0 changed, 0 destroyed.��Outputs:��pet_names = [� Pet: linus-special-goblin,� Pet: cheetarah-loyal-drake,� Pet: li-shou-witty-turtle�]�
12
TERMINAL
© 2019 HashiCorp
Terraform 0.11
Terraform 0.11 Examples
$ terraform apply��Error: random_pet.my_pet: 1 error(s) occurred:��* random_pet.my_pet: element: element() may not be used with an empty list in:��${var.zoo_enabled == "0" ? var.default_prefix : element(var.prefix_list, count.index)}�
13
TERMINAL
© 2019 HashiCorp
The hack
14
© 2019 HashiCorp
Terraform 0.11
terraform.tfvars
Terraform 0.11 Examples
variable "count" { default = 1 }�variable "default_prefix" { default = "linus" }��variable "zoo_enabled" {� default = "0"�}��variable "prefix_list" {� default = []�}��resource "random_pet" "my_pet" {� count = "${var.count}"� prefix = "${var.zoo_enabled == "0" ?
var.default_prefix :
element(concat(var.prefix_list, list("")),
count.index)}"�}�
15
CODE EDITOR
© 2019 HashiCorp
Terraform 0.11
Terraform 0.11 Examples
$ terraform apply��random_pet.my_pet: Creating...� length: "" => "2"� prefix: "" => "linus"� separator: "" => "-"�random_pet.my_pet: Creation complete after 0s (ID: linus-driving-giraffe)��Apply complete! Resources: 1 added, 0 changed, 0 destroyed.��Outputs:��pet_names = [� Pet: linus-driving-giraffe�]�
16
TERMINAL
© 2019 HashiCorp
Terraform 0.12
17
© 2019 HashiCorp
Terraform 0.11 & Earlier
HCL - HashiCorp Configuration Language
Terraform 0.12
variable "foo" { � default = "bar"�}�
18
CODE EDITOR
© 2019 HashiCorp
Terraform 0.11 & Earlier
HIL - HashiCorp Interpolation Language
Terraform 0.12
foo = "hello ${var.world}"��"${format(“Hello %s”, var.world)}"�
19
CODE EDITOR
© 2019 HashiCorp
Terraform Configuration Language - Terraform v0.12
Terraform 0.12
HCL2
20
© 2019 HashiCorp
So many improvements...
Terraform 0.12
21
© 2019 HashiCorp
Terraform 0.12
First Class Expressions
22
© 2019 HashiCorp
First-Class Expressions
First Class Expressions
23
© 2019 HashiCorp
Terraform 0.11
main.tf
First Class Expressions
variable "ami" {}�variable "instance_type" {}�variable "vpc_security_group_ids" {� type = "list"�}��resource "aws_instance" "example" {� ami = "${var.ami}"� instance_type = "${var.instance_type}"�� vpc_security_group_ids = "${var.vpc_security_group_ids}"�}�
24
CODE EDITOR
© 2019 HashiCorp
Terraform 0.12
main.tf
First Class Expressions
variable "ami" {}�variable "instance_type" {}�variable "vpc_security_group_ids" {� type = list(string)�}��resource "aws_instance" "example" {� ami = var.ami� instance_type = var.instance_type�� vpc_security_group_ids = var.vpc_security_group_ids�}�
25
CODE EDITOR
© 2019 HashiCorp
Terraform 0.11
main.tf
First Class Expressions
variable "ami" {}�variable "instance_type" {}�variable "vpc_security_group_id" {� type = "string"� default = ""�}��resource "aws_instance" "example" {� ami = "${var.ami}"� instance_type = "${var.instance_type}"�� vpc_security_group_ids = "${var.vpc_security_group_id != "" ?
[var.vpc_security_group_id] :
list("")
}"�}�
26
CODE EDITOR
© 2019 HashiCorp
Terraform 0.12
main.tf
First Class Expressions
variable "ami" {}�variable "instance_type" {}�variable "vpc_security_group_id" {� type = string� default = ""�}��resource "aws_instance" "example" {� ami = var.ami� instance_type = var.instance_type�� vpc_security_group_ids = var.security_group_id != "" ?
[var.security_group_id] :
[]�}�
27
CODE EDITOR
© 2019 HashiCorp
Terraform 0.12
Rich Value Types
28
© 2019 HashiCorp
Rich Value Types
Rich Value Types
29
© 2019 HashiCorp
Terraform
Module Layout
Rich Value Types
$ tree�.�├── main.tf�├── terraform.tfvars�└── modules� └── subnets� └── subnets.tf�
30
TERMINAL
© 2019 HashiCorp
Terraform 0.11
main.tf
Rich Value Types
variable "networks" {� type = "list"�}��module "subnets" {� source = "./modules/subnets"� networks = "${var.networks}"� ...�}��output "vpc_id" {� value = "${module.subnets.vpc_id}"�}�
31
CODE EDITOR
© 2019 HashiCorp
Terraform 0.11
main.tf
Rich Value Types
# "subnets" module��variable "networks" {� type = "list"�}��resource "aws_vpc" "example" {� networks = "${var.networks}"� ...�}��output "vpc_id" {� value = "${aws_vpc.example.id}"�}�
32
CODE EDITOR
© 2019 HashiCorp
Terraform 0.12
main.tf
Rich Value Types
variable "networks" {� type = map(object({� network_number = number� availability_zone = string� tags = map(string)� }))�}��module "subnets" {� source = "./modules/subnets"� networks = var.networks�}��output "vpc_id" {� value = module.subnets.vpc_id�}�
33
CODE EDITOR
© 2019 HashiCorp
Terraform 0.12
terraform.tfvars
Rich Value Types
networks = {� "production" = {� network_number = 1� availability_zone = "us-east-1a"� }� "staging" = {� network_number = 2� availability_zone = "us-east-1a"� }�}�
34
CODE EDITOR
© 2019 HashiCorp
Terraform 0.12
subnets.tf
Rich Value Types
# "subnets" modules��variable "networks" {� type = map(object({� network_number = number� availability_zone = string� tags = map(string)� }))�}��resource "aws_vpc" "example" {� networks = var.networks� ...�}��output "vpc" {� value = aws_vpc.example�}�
35
CODE EDITOR
© 2019 HashiCorp
Terraform 0.12
main.tf
Rich Value Types
variable "networks" {� type = map(object({� network_number = number� availability_zone = string� tags = map(string)� }))�}��module "subnets" {
source = "./modules/subnets"� networks = var.networks�}��resource "aws_instance" "my_server" {� subnet_id = module.subnets.vpc.id� az = module.subnets.vpc.availability_zone� ... �}�
36
CODE EDITOR
© 2019 HashiCorp
Terraform 0.12
Improved Error Messages
37
© 2019 HashiCorp
Terraform 0.11
Improved Error Messages
$ terraform plan��Error: Error parsing main.tf: object expected closing RBRACE got: EOF�
38
TERMINAL
© 2019 HashiCorp
Terraform 0.12
Improved Error Messages
$ terraform plan��Error: Argument or block definition required�� on main.tf line 24:� 24: :wq��An argument or block definition is required here.�
39
TERMINAL
© 2019 HashiCorp
Terraform 0.12
Improved Error Messages
$ terraform plan��Error: Invalid operand�� on main.tf line 16, in output "foobar":� 16: value = "${1 + var.foo}"��Unsuitable value for right operand: a number is required.�
40
TERMINAL
© 2019 HashiCorp
Terraform 0.12
Improved Error Messages
$ terraform plan��Error: Unsupported block type�� on main.tf line 36, in outptu "item":� 1: outptu "item" {��Blocks of type "outptu" are not expected here. Did you mean "output"?�
41
TERMINAL
© 2019 HashiCorp
Terraform 0.12
For Expressions
42
© 2019 HashiCorp
For Expressions
For Expressions
43
© 2019 HashiCorp
Terraform 0.11
main.tf
For Expressions
output "instance_private_ip_addresses_map" {� value = "${zipmap(aws_instance.id,aws_instance.private_ip)}"�}�
44
CODE EDITOR
© 2019 HashiCorp
Terraform 0.12
main.tf
For Expressions
output "instance_private_ip_addresses" {� value = {� for instance in aws_instance.example:� instance.id => instance.private_ip� }�}�
45
CODE EDITOR
© 2019 HashiCorp
Terraform 0.12
For Expressions
$ terraform output��instance_private_ip_addresses = {� "i-1234" = "192.168.1.1"� "i-5678" = "192.168.1.2"� "i-9876" = "192.168.1.3"�}�
46
TERMINAL
© 2019 HashiCorp
Terraform 0.11
main.tf
For Expressions
resource "aws_autoscaling_group" "example" {� # ...� tag {� key = "Component"� value = "user-service"� propagate_at_launch = true� }��� tag {� key = "Environment"� value = "production"� propagate_at_launch = false� }���tag { ... }�tag { ... }�tag { ... }�tag { ... }�}�
47
CODE EDITOR
© 2019 HashiCorp
Terraform 0.12
main.tf
For Expressions
locals {� standard_tags = {� Component = "user-service"� Environment = "production"� }�}��resource "aws_autoscaling_group" "example" {� # ...�� dynamic "tag" {� for_each = local.standard_tags�� content {� key = tag.key� value = tag.value� propagate_at_launch = true� }� }�}�
48
CODE EDITOR
© 2019 HashiCorp
Terraform 0.12
Back to `random_pet` Example
49
© 2019 HashiCorp
Terraform 0.11
main.tf
Terraform 0.12 Examples
variable "count" { default = 1 }�variable "default_prefix" { default = "linus" }��variable "zoo_enabled" {� default = "0"�}��variable "prefix_list" {� default = []�}��resource "random_pet" "my_pet" {� count = "${var.count}"� prefix = "${var.zoo_enabled == "0" ?
var.default_prefix :
element(concat(var.prefix_list, list("")), count.index)}"�}�
50
CODE EDITOR
© 2019 HashiCorp
The same example - HCL2
51
© 2019 HashiCorp
Terraform 0.12
main.tf
Terraform 0.12 Examples
variable "pet_count" { default = 1 }��variable "default_prefix" { default = “linus” }��variable "zoo_enabled" {� default = false�}��variable "prefix_list" {� default = []�}��resource "random_pet" "my_pet" {� count = var.pet_count� prefix = var.zoo_enabled ?
element(var.prefix_list,count.index) :
var.default_prefix�}�
52
CODE EDITOR
© 2019 HashiCorp
Terraform 0.12
Terraform 0.12 Examples
$ terraform apply�random_pet.my_pet[0]: Creating...�random_pet.my_pet[0]: Creation complete after 0s��Apply complete! Resources: 1 added, 0 changed, 0 destroyed.��Outputs:��pet_names = [� Pet: linus-beloved-mako�]�
53
TERMINAL
© 2019 HashiCorp
But what does it mean?
54
© 2019 HashiCorp
Terraform 0.12 Enhancements
55
© 2019 HashiCorp
Demo?
56
© 2019 HashiCorp
CFPs Open:
HashiConf USA
(closes 2nd April)
57
© 2019 HashiCorp
hello@hashicorp.com
58
Thank you