DEV Community

Cover image for Advanced HCL for Terraform Day 9
Avesh
Avesh

Posted on

Advanced HCL for Terraform Day 9

Advanced HCL: Expressions, Functions, and Dynamic Blocks

HashiCorp Configuration Language (HCL) is the backbone of Terraform, providing a rich syntax for defining and managing infrastructure. As you progress with Terraform, mastering advanced HCL features such as expressions, built-in functions, and dynamic blocks is essential for creating reusable, maintainable, and dynamic configurations. This article covers complex expressions, built-in functions, dynamic blocks, the use of count and for_each, and conditional resource creation, with hands-on examples.


1. Complex Expressions

Definition: Complex expressions allow you to combine variables, functions, and conditional logic to calculate values dynamically in Terraform configurations.

Example:

variable "environment" {
  default = "dev"
}

resource "aws_instance" "example" {
  ami           = var.environment == "prod" ? "ami-123456" : "ami-789012"
  instance_type = var.environment == "prod" ? "t3.large" : "t3.micro"
}
Enter fullscreen mode Exit fullscreen mode

Here:

  • The ami and instance_type attributes are determined dynamically based on the environment variable using a ternary expression.

Use Cases:

  • Dynamic resource attributes.
  • Conditional variable assignments.

2. Built-in Functions

Definition: Terraform provides a variety of built-in functions to manipulate strings, numbers, collections, and more.

Commonly Used Functions:

  1. String Functions:
   variable "name" {
     default = "terraform"
   }

   output "upper_name" {
     value = upper(var.name) # Output: "TERRAFORM"
   }
Enter fullscreen mode Exit fullscreen mode
  1. Numeric Functions:
   variable "number" {
     default = 5
   }

   output "double_number" {
     value = var.number * 2 # Output: 10
   }
Enter fullscreen mode Exit fullscreen mode
  1. Collection Functions:
   variable "list" {
     default = ["a", "b", "c"]
   }

   output "first_element" {
     value = element(var.list, 0) # Output: "a"
   }
Enter fullscreen mode Exit fullscreen mode

Best Practices:

  • Use functions to clean up repetitive logic.
  • Combine functions with expressions for maximum flexibility.

3. Dynamic Blocks

Definition: Dynamic blocks enable the creation of multiple similar resource or nested block configurations without duplicating code.

Hands-on Example:

resource "aws_security_group" "example" {
  name        = "example-sg"
  description = "Example security group"

  dynamic "ingress" {
    for_each = [
      { from_port = 80, to_port = 80, protocol = "tcp" },
      { from_port = 443, to_port = 443, protocol = "tcp" }
    ]

    content {
      from_port   = ingress.value.from_port
      to_port     = ingress.value.to_port
      protocol    = ingress.value.protocol
      cidr_blocks = ["0.0.0.0/0"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • The dynamic block iterates over a list of ingress rules and creates multiple ingress blocks.
  • Reduces duplication and enhances maintainability.

Best Practices:

  • Use dynamic blocks for repetitive nested blocks.
  • Avoid overusing them for simple configurations to maintain readability.

4. Count and for_each

Definition: count and for_each are mechanisms for creating multiple resources from a single resource block, depending on your needs.

Count Example:

resource "aws_instance" "example" {
  count = 3

  ami           = "ami-123456"
  instance_type = "t2.micro"
  tags = {
    Name = "example-${count.index}"
  }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • Creates three EC2 instances with tags example-0, example-1, and example-2.

For_each Example:

resource "aws_instance" "example" {
  for_each = {
    dev  = "ami-123456",
    prod = "ami-789012"
  }

  ami           = each.value
  instance_type = "t2.micro"
  tags = {
    Name = each.key
  }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • Creates two EC2 instances, one for dev and one for prod, using the corresponding AMIs.

Best Practices:

  • Use count for simple lists.
  • Use for_each for maps or when you need access to keys.

5. Conditional Creation

Definition: Conditional creation allows you to enable or disable resources based on specific conditions, improving flexibility and cost efficiency.

Hands-on Example:

variable "create_instance" {
  default = false
}

resource "aws_instance" "example" {
  count = var.create_instance ? 1 : 0

  ami           = "ami-123456"
  instance_type = "t2.micro"
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • The count argument dynamically determines whether the resource is created based on the create_instance variable.

Use Case:

  • Toggle resources for dev/test environments.
  • Reduce costs by disabling unused infrastructure.

Conclusion

Advanced HCL features like expressions, built-in functions, dynamic blocks, count, for_each, and conditional creation provide unparalleled flexibility and power for managing infrastructure. By mastering these concepts, you can create configurations that are not only efficient but also adaptable to complex real-world scenarios. Incorporate these techniques into your Terraform workflows to streamline resource management and reduce maintenance overhead.

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more