Terraform guide 6 - Terraform 0.12

Terraform: Terraform 0.12

Setup and disclaimer

  1. Install Terraform 0.12

    1
    2
    3
    4
    5
    6
    cd /tmp
    sudo curl -O https://releases.hashicorp.com/terraform/0.12.2/terraform_0.12.2_linux_amd64.zip
    sudo unzip terraform_0.12.2_linux_amd64.zip
    sudo cp terraform /usr/bin/terraform12
    # check up
    terraform12 version
  2. Setup a Terraform 0.12 directory

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    mkdir /home/your_user/terraform/t12
    cd /home/your_user/terraform/t12
    cp -r /home/your_user/terraform/basics .
    cd basics
    rm -r .terraform
    # test
    terraform12 init

    # also copy AWS/storage
    cd /home/your_user/terraform/t12
    cp -r ../AWS/storage .
    cd storage
  3. Edit main.tf

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #---------storage/main.tf---------

    # Create a random id
    resource "random_id" "tf_bucket_id" {
    byte_length = 2
    }

    # Create the bucket
    resource "aws_s3_bucket" "tf_code" {
    bucket = "${var.project_name}-${random_id.tf_bucket_id.dec}"
    acl = "private"

    force_destroy = true

    tags = {
    Name = "tf_bucket"
    }
    }
  4. Work with Terraform

    • Terraform 12
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      # setup AWS keys
      export AWS_ACCESS_KEY_ID="[ACCESS_KEY]"
      export AWS_SECRET_ACCESS_KEY="[SECRET_KEY]]"
      export AWS_DEFAULT_REGION="us-east-1"

      terraform12 init
      # deploy
      terraform12 apply -var project_name=la-terraform -auto-approve
      # clean up
      terraform12 destroy -var project_name=la-terraform -auto-approve
      ls -la
      rm -r .terraform terraform.tfstate*
    • Older version
      1
      2
      3
      terraform init
      terraform apply -var project_name=la-terraform -auto-approve
      terraform destroy -var project_name=la-terraform -auto-approve

Working with resources

  • environment: /home/your_user/terraform/t12/storage
  • example with storage
    1. Edit main.tf
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      # Create a random id
      resource "random_id" "tf_bucket_id" {
      byte_length = 2
      }

      # Create the bucket
      resource "aws_s3_bucket" "tf_code" {
      bucket = format("la-terraform-%d", random_id.tf_bucket_id.dec)
      acl = "private"

      force_destroy = true

      tags = {
      Name = "tf_bucket"
      }
      }
    2. Work with Terraform
      1
      2
      3
      4
      5
      6
      7
      8
      9
      # Setup AWS access key
      export AWS_ACCESS_KEY_ID="[ACCESS_KEY]"
      export AWS_SECRET_ACCESS_KEY="[SECRET_KEY]]"
      export AWS_DEFAULT_REGION="us-east-1"
      terraform12 init
      terraform12 apply -auto-approve

      # clean up
      terraform12 destroy -auto-approve

Input variables

Refactor the previous storage module by adding in variables

  1. Edit variables.tf
    1
    2
    3
    variable "project_name" {
    type = string
    }
  2. Edit main.tf
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # Create a random id
    resource "random_id" "tf_bucket_id" {
    byte_length = 2
    }

    # Create the bucket
    resource "aws_s3_bucket" "tf_code" {
    bucket = format("%s-%d", var.project_name, random_id.tf_bucket_id.dec)
    acl = "private"
    force_destroy = true
    tags = {
    Name = "tf_bucket"
    }
    }
  3. Work with Terraform
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # setup AWS access key
    export AWS_ACCESS_KEY_ID="[ACCESS_KEY]"
    export AWS_SECRET_ACCESS_KEY="[SECRET_KEY]]"
    export AWS_DEFAULT_REGION="us-east-1"
    terraform12 plan -var project_name=la-terraform
    terraform12 apply -var project_name=la-terraform -auto-approve

    # clean up
    terraform12 destroy -var project_name=la-terraform -auto-approve

Output values

Refactor the previous storage module by adding outputs of the S3 bucket name and project_name variable.

  1. Edit outputs.tf
    1
    2
    3
    4
    5
    6
    7
    output "bucketname" {
    value = aws_s3_bucket.tf_code.id
    }

    output "project_name" {
    value = var.project_name
    }
  2. Work with Terraform
    1
    2
    3
    4
    5
    terraform12 init
    terraform12 apply -var project_name=la-terraform -auto-approve

    # clean up
    terraform destroy -var project_name=la-terraform -auto-approve

Expressions

Dynamic nested blocks ‘for-each’

  • environment: ~/terraform/t12/loops
  1. Edit main.tf
    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
    variable "vpc_cidr" {
    default = "10.123.0.0/16"
    }

    variable "accessip" {
    default = "0.0.0.0/0"
    }

    variable "service_ports" {
    default = ["22", "22"]
    }

    resource "aws_vpc" "tf_vpc" {
    cidr_block = var.vpc_cidr
    enable_dns_hostnames = true
    enable_dns_support = true

    tags = {
    Name = "tf_vpc"
    }
    }

    resource "aws_security_group" "tf_public_sg" {
    name = "tf_public_sg"
    description = "Used for access to the public instances"
    vpc_id = aws_vpc.tf_vpc.id

    # this defines a for-each loop
    dynamic "ingress" {
    for_each = var.service_ports
    content {
    from_port = ingress.value
    to_port = ingress.value
    protocol = "tcp"
    cidr_blocks = [var.accessip]
    }
    }
    }
  2. Work with Terraform
    1
    2
    3
    4
    5
    export AWS_ACCESS_KEY_ID="[ACCESS_KEY]"
    export AWS_SECRET_ACCESS_KEY="[SECRET_KEY]"
    export AWS_DEFAULT_REGION="us-east-1"
    terraform12 init
    terraform12 plan

Dynamic nested blocks ‘for’

  • environment: ~/terraform/t12/dynamic
  1. Edit `main.tf:``

    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
    52
    53
    54
    55
    56
    57
    58
    variable "vpc_cidr" {
    default = "10.123.0.0/16"
    }

    variable "accessip" {
    default = "0.0.0.0/0"
    }

    variable "service_ports" {
    default = [
    {
    from_port = "22",
    to_port = "22"
    },
    {
    from_port = "80",
    to_port = "80"
    }
    ]
    }

    resource "aws_vpc" "tf_vpc" {
    cidr_block = var.vpc_cidr
    enable_dns_hostnames = true
    enable_dns_support = true

    tags = {
    Name = "tf_vpc"
    }
    }

    resource "aws_security_group" "tf_public_sg" {
    name = "tf_public_sg"
    description = "Used for access to the public instances"
    vpc_id = aws_vpc.tf_vpc.id

    dynamic "ingress" {
    for_each = [ for s in var.service_ports: {
    from_port = s.from_port
    to_port = s.to_port
    }]

    content {
    from_port = ingress.value.from_port
    to_port = ingress.value.to_port
    protocol = "tcp"
    cidr_blocks = [var.accessip]
    }
    }
    }

    output "ingress_port_mapping" {
    value = {
    # for loop
    for ingress in aws_security_group.tf_public_sg.ingress:
    format("From %d", ingress.from_port) => format("To %d", ingress.to_port)
    }
    }
  2. Work in Terraform:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    export AWS_ACCESS_KEY_ID="[ACCESS_KEY]"
    export AWS_SECRET_ACCESS_KEY="[SECRET_KEY]"
    export AWS_DEFAULT_REGION="us-east-1"
    terraform12 init
    terraform12 plan
    terraform12 apply -auto-approve

    # clean up
    terraform12 destroy -auto-approve*

Functions

  • Terraform 0.12 and later have built-in functions, previous versions have Interpolation Syntax
  • example using cidrsubnet function to calculate a subnet address within a given IP network address prefix
  • environment: ~/terraform/t12/functions
  1. Edit main.tf
    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
    variable "vpc_cidr" {
    default = "10.123.0.0/16"
    }

    variable "accessip" {
    default = "0.0.0.0/0"
    }

    variable "subnet_numbers" {
    default = [1, 2, 3]
    }

    resource "aws_vpc" "tf_vpc" {
    cidr_block = var.vpc_cidr
    enable_dns_hostnames = true
    enable_dns_support = true

    tags = {
    Name = "tf_vpc"
    }
    }

    resource "aws_security_group" "tf_public_sg" {
    name = "tf_public_sg"
    description = "Used for access to the public instances"
    vpc_id = aws_vpc.tf_vpc.id

    ingress {
    from_port = "22"
    to_port = "22"
    protocol = "tcp"
    vidr_blocks = [
    for num in var.subnet_numbers:
    # num = netnum -> value got from calculation from vpc_cidr dafault net: 16+8=24
    cidrsubnet(aws_vpc.tf_vpc.cidr_block, 8, num)
    ]
    }
    }
  2. Work with Terraform
    1
    2
    3
    4
    5
    export AWS_ACCESS_KEY_ID="[ACCESS_KEY]"
    export AWS_SECRET_ACCESS_KEY="[SECRET_KEY]"
    export AWS_DEFAULT_REGION="us-east-1"
    terraform12 init
    terraform12 plan

Upgrade process example

  1. Refactor variables.tf
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    variable "vpc_cidr" {
    default = "10.123.0.0/16"
    }

    variable "accessip" {
    default = "0.0.0.0/0"
    }

    # add the service ports, to populate the block
    variable "service_ports" {
    default = [
    {
    from_port = "22",
    to_port = "22"
    },
    {
    from_port = "80",
    to_port = "80"
    }
    ]
    }
  2. Refactor variables.tf
    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
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    resource "aws_vpc" "tf_vpc" {

    ## interpolation symbols are no loger needed
    ## cidr_block = "${var.vpc_cidr}"
    cidr_block = var.vpc_cidr
    enable_dns_hostnames = true
    enable_dns_support = true

    ## now we need and equals sign on tags
    ## tags {
    tags = {
    Name = "tf_vpc"
    }
    }

    resource "aws_security_group" "tf_public_sg" {
    name = "tf_public_sg"
    description = "Used for access to the public instances"
    ## interpolation symbols are no loger needed
    ## vpc_id = "${aws_vpc.tf_vpc.id}"
    vpc_id = aws_vpc.tf_vpc.id


    ## refator as loop, and remove interpolation symbols
    ## #SSH
    ## ingress {
    ## from_port = 22
    ## to_port = 22
    ## protocol = "tcp"
    ## cide_blocks = ["$var.accessip"]
    ## }
    ## #HTTP
    ## ingress {
    ## from_port = 80
    ## to_port = 80
    ## protocol = "tcp"
    ## cide_blocks = ["$var.accessip"]
    ## }

    ## set as dynamic block, with maps for each,
    ## whose values are stored in `variables.tf`
    dynamic "ingress" {
    for_each = [ for s in var.service_ports: {
    from_port = s.from_port
    to_port = s.to_port
    }]

    content {
    from_port = ingress.value.from_port
    to_port = ingress.value.to_port
    protocol = "tcp"
    cidr_blocks = [var.accessip]
    }
    }

    egress {
    from_port = 0
    to_port = 0
    protocol = "-1"
    cidr_blocks = ["0.0.0.0/0"]
    }
    }
  3. Refactor output.tf
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
        output "public_sg" {
    value = aws_security_group.tf_public_sg.id
    }

    ## add mapping for ingress port
    output "ingress_port_mapping" {
    value = {
    for ingress in aws_security_group.tf_public_sg.ingress:
    format("From %d", ingress.from_port) => format("To %d", ingress.to_port)
    }
    }
    ```
    4. Launch Terraform to test it
    ```bash
    terraform init
    terraform validate
    terraform plan
    terraform apply –auto-approve
    # select a region and go