This is part of a collection of articles covering an Introduction to Advanced Terraform, it is deliberately of a certain style, you can find out more in the first post.

Introduction

Terraform is a powerful Infrastructure as Code (IaC) tool, allowing us to define, provision, and manage infrastructure through declarative configuration files. While incredibly effective, Terraform code can sometimes become complex and difficult to understand, especially for newcomers or those less familiar with its syntax. In this article, I'll show a technique for writing Terraform code that will allow other users to easily understand and interact with your infrastructure.

Key Principles of User-Friendly Terraform

  • Abstraction: Hide complex data manipulation and resource configuration away, allow users to easily define resources in your locals blocks.
  • Clear Definition: Terraform Objects use a structure that can resemble the attributes of the resource you're configuring. This makes it easier for users to understand and interact with your code.
  • Meaningful Naming: Use clear and descriptive names for variables, resources, and modules. This improves readability and makes self-documentation possible.

Example: User-Friendly EC2 Configuration

Let's analyze the provided locals block example, demonstrating how it embodies these principles:

locals {
  ec2 = {
    dev = {
      arryw-app = {
        region        = "eu-west-1"
        count         = 2
        az            = ["eu-west-1a", "eu-west-1b"]
        ami           = "ami-0c55b159cbfafe1f0"
        instance_type = "t2.micro"
        another_key   = "another-value"
      }
    }
    prod = {
      my-app = {
        # ... similar configuration with production values
      }
    }
  }
}

Analysis

  • Abstraction: Data preprocessing (which often includes more complex logic) is hidden away in other Terraform (.tf) files. locals blocks like this present a clean interface of configuration parameters.
  • Clarity: The structure resembles an object with key-value pairs. The keys closely mirror attribute names in an aws_instance resource, aiding understanding, while allowing flexibility to add other attributes that may be used for filtering or to define how many instances to create.
  • Flexibility: The try function (if properly incorporated into resource blocks) allows for many missing attributes to be optional, streamlining user input.

How to Use This Code

Imagine the locals block is referenced in your Terraform configuration. You could create EC2 instances using code like this:

resource "aws_instance" "dev_instance" {
  for_each = lookup(local.ec2, var.environment)

  ami           = try(each.value.ami, data.aws_ami.ec2[each.key].id) # if ami doesn't exist, refer to a data lookup
  instance_type = try(each.value.instance_type, "t2.micro") # if instance_type is not defined, use a default value
  # ... other attributes
}

Benefits

  1. Ease of Use: Users who only need to come in and define a few parameters can do so without needing to understand the underlying complexity.
  2. Maintainability: The structure of the locals block makes it easier to add new attributes or modify existing ones.
  3. Reduced Errors: The well-structured locals block aids in preventing incorrect attributes from being assigned.

Conclusion

By following these principles, you can create Terraform code that is easy to understand and use. This approach is particularly beneficial when working in a team environment, where multiple people may need to interact with the same codebase. By making your Terraform code user-friendly, you can improve collaboration, reduce errors, and increase the overall efficiency of your infrastructure management processes.