Daniel Kraszewski
Daniel Kraszewski
Head of Engineering

Short-Circuit Evaluation in Terraform: A Deep Dive

Jul 30, 20254 min read

Short-circuit evaluation is a fundamental concept in programming that can significantly impact how your Terraform configurations behave. Unlike traditional programming languages, Terraform's HashiCorp Configuration Language (HCL) has some unique characteristics when it comes to short-circuit evaluation that every DevOps engineer should understand.

What is Short-Circuit Evaluation?

Short-circuit evaluation is an optimization technique where the second operand of a logical operation is only evaluated if the first operand doesn't determine the result. In most programming languages:

  • For AND operations: if the first condition is false, the second isn't evaluated
  • For OR operations: if the first condition is true, the second isn't evaluated

How Traditional Languages Handle Short-Circuit Evaluation

In languages like JavaScript, Python, or Go, short-circuit evaluation prevents unnecessary computation and potential errors:

// JavaScript example const user = getUser(); if (user && user.password) { // user.password is only checked if user exists console.log('User has password set'); }
# Python example if user is not None and user.password: # user.password only evaluated if user is not None print("User has password set")

The Problem: Terraform's Short-Circuit Evaluation Doesn't Always Work

Here's a real-world example that demonstrates the issue:

variable "user_config" { type = object({ username = string email = string password = optional(string) }) default = null } # ❌ This WILL FAIL with "Attempt to get attribute from null value" locals { needs_random_password = var.user_config != null && var.user_config.password == null }

Why does this fail? Even though we check var.user_config != null first, Terraform still evaluates the entire expression during the parsing phase and discovers that var.user_config.password is trying to access an attribute on a null value.

Terraform's Unique Approach

Terraform's HCL has a more complex relationship with short-circuit evaluation due to its declarative nature and the way it processes configurations during different phases (plan, apply, etc.).

The Reality: Limited Short-Circuit Evaluation

Unlike traditional programming languages, Terraform's short-circuit evaluation is limited because:

  1. Parsing Phase: Terraform parses the entire expression before evaluation
  2. Type Checking: All attribute accesses are validated regardless of conditions
  3. Static Analysis: Terraform needs to understand all possible code paths

Solutions: Safe Ways to Handle Null Values

Solution 1: Explicit Boolean Variables

The most straightforward and readable approach is to use explicit boolean variables with validation:

variable "create_user" { description = "Whether to create the user" type = bool default = false } variable "user_config" { description = "User configuration (required when create_user is true)" type = object({ username = string email = string password = optional(string) }) default = null validation { condition = !var.create_user || var.user_config != null error_message = "user_config must be provided when create_user is true." } } resource "random_password" "user_password" { count = var.create_user && var.user_config.password == null ? 1 : 0 length = 16 special = true } resource "aws_iam_user" "example" { count = var.create_user ? 1 : 0 name = var.user_config.username tags = { Email = var.user_config.email } } resource "aws_iam_user_login_profile" "example" { count = var.create_user ? 1 : 0 user = aws_iam_user.example[0].name password = var.user_config.password != null ? var.user_config.password : random_password.user_password[0].result }

Why this works: By separating the boolean flag from the configuration object, we avoid null attribute access entirely. The validation block ensures data integrity.

Note: Variable validation is a powerful tool that should be leveraged whenever possible. It provides early feedback to users, prevents configuration errors, and makes your modules more robust and user-friendly. The validation block runs during terraform plan and terraform apply, catching issues before resources are created.

Solution 2: Use try() Function

For cases where explicit boolean variables aren't preferred:

locals { needs_random_password = try(var.user_config.password == null, false) } resource "random_password" "user_password" { count = local.needs_random_password ? 1 : 0 length = 16 special = true }

Why this works: The try() function catches any errors during expression evaluation and returns the fallback value (false) if the expression fails. This prevents the "Attempt to get attribute from null value" error by gracefully handling the case where var.user_config is null.

Solution 3: Use can() Function

locals { needs_random_password = can(var.user_config.password) && var.user_config.password == null } resource "random_password" "user_password" { count = local.needs_random_password ? 1 : 0 length = 16 special = true }

Why this works: The can() function tests whether an expression can be evaluated without errors. It returns true if the expression is valid and false if it would cause an error. This allows us to safely check if var.user_config.password is accessible before evaluating it, effectively implementing our own short-circuit logic.

Solution 4: Nested Conditional Expression

locals { needs_random_password = var.user_config != null ? var.user_config.password == null : false } resource "random_password" "user_password" { count = local.needs_random_password ? 1 : 0 length = 16 special = true }

Why this works: The ternary conditional operator (condition ? true_value : false_value) provides proper short-circuit behavior in Terraform. The second part of the expression (var.user_config.password == null) is only evaluated if the first condition (var.user_config != null) is true, preventing null attribute access errors.

Summary

Short-circuit evaluation in Terraform is not the same as in traditional programming languages. The most effective approaches:

  1. Use explicit boolean variables with validation for feature flags and optional resources - this is the most robust approach that leverages Terraform's validation capabilities
  2. Use try() function for optional nested attributes and fallback values
  3. Use can() function to test expression validity
  4. Use variable validation wherever possible to catch errors early and provide better user experience
  5. Combine approaches based on your team's preferences and the complexity of your configuration

The explicit boolean variable approach with validation tends to be more readable, maintainable, and provides the best user experience, while function-based approaches can be more concise for simple cases. Choose what works best for your team and use case, but always consider adding validation to improve reliability.

Additional Resources

RELATED POSTS
Paweł Swiridow
Paweł Swiridow
Senior Software Engineer

Centralized EKS Logging Across AWS Accounts Using Fluent Bit, Pod Identity and OpenSearch

Jul 23, 20254 min read
Article image
Robert Szczepanowski
Robert Szczepanowski
Senior Software Engineer

Kubernetes logs unavailable behind a proxy: Diagnosing API server communication issues

Jul 16, 20253 min read
Article image
Bartłomiej Gałęzowski
Bartłomiej Gałęzowski
Senior Software Engineer

Deploying Next.js on AWS: Why We Migrated from Amplify to ECS

Jul 09, 20255 min read
Article image