Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

What to expect in Terraform 0.12?

Ringo
December 12, 2018

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

Ringo

December 12, 2018
Tweet

More Decks by Ringo

Other Decks in Technology

Transcript

  1. 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
  2. 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)
  3. 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
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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"]}
  10. 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
  11. 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
  12. 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
  13. 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!
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. ringods @ We need you! • More people in the

    core organising team • More presenters • More topics Contact me via Meetup or Twitter