Share
Embed
What to expect in Terraform 0.12?
Overview of the new features in Terraform 0.12
Presentation given at the Belgium Hashicorp User Group on 12 December 2018
Transcript
26 slides
-
ringods @ Ringo De Smet Freelance Consultant, Obsessed by flow
in software delivery -
ringods @ Terraform 0.12 HCL: Hashicorp Configuration Language • Unchanged
over the past 4 years • Limitations in the language • Inconsistencies in the language • Result: inventive but unintuitive workarounds -
ringods @ Terraform 0.12: What is announced? • First-class expressions
• For expressions • Dynamic blocks • Generalized “Splat” operator • Conditional improvements • Richt types in module inputs and outputs • Template syntax • Reliable JSON Syntax (not covered) • References as first-class values (not covered) -
ringods @ Terraform 0.12: Intent of the changes • In
0.11 and earlier, a lot happens in the interpolation language • Focus shifts to expressions, outside of the interpolation scope -
ringods @ # Configuration for Terraform 0.11 and earlier 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}" } First-class expressions -
ringods @ # Configuration for Terraform 0.12 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 } First-class expressions -
ringods @ # Configuration for Terraform 0.11 and earlier resource
"aws_instance" "example" { # … # First version vpc_security_group_ids = ["${var.security_group_1}", "${var.security_group_2}"] # Second version vpc_security_group_ids = "${var.security_group_id != "" ? [var.security_group_id] : []}" # Third version vpc_security_group_ids = “${var.security_group_id != "" ? list(var.security_group_id) : list()}" } First-class expressions -
ringods @ # Configuration for Terraform 0.12 resource "aws_instance" "example"
{ # … vpc_security_group_ids = var.security_group_id != "" ? [var.security_group_id] : [] } First-class expressions -
ringods @ resource "aws_security_group" "example" { name = "friendly_subnets" description
= "Allows access from friendly subnets" vpc_id = var.vpc_id ingress { from_port = 0 to_port = 0 protocol = -1 cidr_blocks = [ for num in var.subnet_numbers: cidrsubnet(data.aws_vpc.example.cidr_block, 8, num) ] } } output "instance_private_ip_addresses" { value = { for instance in aws_instance.example: instance.id => instance.private_ip } } For: create lists or maps -
ringods @ output "instance_public_ip_addresses" { value = { for instance
in aws_instance.example: instance.id => instance.public if instance.associate_public_ip_address } } output "instances_by_availability_zone" { value = { for instance in aws_instance.example: instance.availability_zone => instance.id... } } For: optional if and grouping {"us-east-1a": [“i-1234", “i-5678"]} -
ringods @ # Configuration for Terraform 0.11 and earlier resource
"aws_autoscaling_group" "example" { tag { key = "Component" value = "user-service" propagate_at_launch = true } tag { key = "Environment" value = "production" propagate_at_launch = true } } Dynamic Nested Blocks -
ringods @ # Configuration for Terraform 0.12 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 } } } Dynamic Nested Blocks -
ringods @ resource "azurerm_virtual_network" "example" { name = "example-network" resource_group_name
= azurerm_resource_group.test.name address_space = [local.base_cidr_block] location = "West US" dynamic "subnet" { for_each = [for s in var.subnets: { name = s.name prefix = cidrsubnet(local.base_cidr_block, 4, s.number) }] content { name = subnet.name address_prefix = subnet.prefix } } } Dynamic Nested Blocks -
ringods @ variable "subnet_numbers" { default = { "eu-west-1a" =
1 "eu-west-1b" = 2 "eu-west-1c" = 3 } } resource "aws_vpc" "example" { # ... } resource "aws_subnet" "example" { for_each = var.subnet_numbers vpc_id = aws_vpc.example.id availability_zone = each.key cidr_block = cidrsubnet(aws_vpc.example.cidr_block, 8, each.value) } FUTURE: resource for_each Will come post TF 0.12! Also: count in module blocks! -
ringods @ # Configuration for Terraform 0.11 and earlier output
"instance_names" { value = google_compute_instance.main.*.name } # Configuration for Terraform 0.12 output "instance_ip_addrs" { value = google_compute_instance.example.network_interface.*.address } Splat Operator -
ringods @ # Configuration for Terraform 0.11 and earlier output
"instance_names" { value = google_compute_instance.main.*.name[0] } # Configuration for Terraform 0.12 output "instance_ip_addrs" { value = google_compute_instance.example.network_interface[*].access_config[ 0].assigned_nat_ip } Full Splat Operator -
ringods @ Conditional operator • The operator: … ? …
: … • In 0.11: • limited to primitive types • both value expressions where evaluated • In 0.12, both of these restrictions are no more -
ringods @ # Configuration for Terraform 0.12 locals { first_id
= length(azurerm_virtual_machine.example) > 0 ? azurerm_virtual_machine.example[0].id : "" buckets = (var.env == "dev" ? [var.build_bucket, var.qa_bucket] : [var.prod_bucket]) } Conditional Operator -
ringods @ # Configuration for Terraform 0.12 variable "override_private_ip" {
type = string default = null } resource "aws_instance" "example" { # ... (other aws_instance arguments) ... private_ip = var.override_private_ip } Conditionally Omitted Arguments -
ringods @ # Configuration for Terraform 0.12 module "subnets" {
source = "./subnets" parent_vpc_id = "vpc-abcd1234" networks = { production_a = { network_number = 1 availability_zone = "us-east-1a" } production_b = { network_number = 2 availability_zone = "us-east-1b" } staging_a = { network_number = 1 availability_zone = "us-east-1a" } } } Complex values -
ringods @ # Configuration for Terraform 0.12 variable "environment_name" {
type = string } variable "networks" { type = map(object({ network_number = number availability_zone = string tags = map(string) })) } Rich Types -
ringods @ # Configuration for Terraform 0.11 output “vpc_id" {
value = aws_vpc.example.id } # Configuration for Terraform 0.12 output "vpc" { value = aws_vpc.example } Resources and Modules as values -
ringods @ # Configuration for Terraform 0.12 locals { lb_config
= <<EOT %{ for instance in opc_compute_instance.example ~} server ${instance.label} ${instance.ip_address}:8080 %{ endfor } EOT } server example0 192.168.2.12:8080 server example1 192.168.2.65:8080 server example2 192.168.2.23:8080 Template Syntax -
ringods @ We need you! • More people in the
core organising team • More presenters • More topics Contact me via Meetup or Twitter