Skip to content

Support Passing and Evaluating Deferred Expressions (Functions) via Variables at Point of Use #38110

@emoshaya

Description

@emoshaya

Terraform Version

Terraform v1.14.3

Use Cases

Terraform variables are values, not expressions. If a user passes a string that looks like an expression (e.g., ${upper("test")}), Terraform treats it as a literal string (or it is rejected depending on context). There is currently no supported mechanism to:

  1. Pass an expression via a variable (including Terraform functions), and
  2. Have Terraform evaluate that expression when the variable is used as an argument to a resource or module.

This prevents certain “configuration-driven indirection” patterns, such as:

  • Passing a naming rule or formatting rule as configuration
  • Having a module accept a “value expression” to compute a final name/path/ID
  • Avoiding repeated boilerplate expressions across multiple call sites/resources
  • Driving transformations from environment configuration without duplicating expression logic
variable "function_name_rule" {
  type = string
}

# Caller wants:
# function_name_rule = "$${upper(var.base_name)}"

resource "aws_lambda_function" "example" {
  function_name = var.function_name_rule   # would be ideal if evaluated here
}

Attempted Solutions

Using interpolation-like syntax inside a string, such as:

function_name_rule = "$${upper("test")}"

Terraform currently treats this as a literal string; there is no supported evaluation step to interpret it as an expression.

  • Workarounds like locals or duplicating the expression at each use site defeat the goal of configuration-driven reuse.

Proposal

Introduce an opt-in, explicit way to represent deferred expressions as a distinct type/value that can be passed through variables and evaluated at the point of use in resource/module arguments.

Option A: New Expression Value Constructor + Explicit Evaluation

Call site:


variable "name_expr" {
  type = expr(string) # or a dedicated expression type
}

module "x" {
  source    = "./x"
  name_expr = expr(upper("test"))
}

Inside module or resource usage:

resource "aws_lambda_function" "example" {
  function_name = eval(var.name_expr)
}

Properties:

  • expr(...) produces an expression value (not a string)
  • eval(...) explicitly evaluates it in the current scope at the point of use
  • Terraform can validate types (expr(string)), and prevent arbitrary evaluation where not permitted
  • Keeps evaluation explicit (no “magic string interpolation”)

Option B: String-Based Deferred Interpolation

Allow a string convention like $${ ... } anywhere variables are used, and evaluate it at use sites. Example:

function_name = "$${upper("test")}"

This is more ergonomic, but is also more ambiguous, harder to statically analyze, and risks confusing “data” vs “code”.

Why This Is Useful

  • Enables reusable “rules” for naming/formatting without repeating expressions
  • Allows modules to accept transformation logic from callers in a controlled way
  • Improves ergonomics for environments with strong naming conventions
  • Keeps dependency graph correctness if expression evaluation still flows through Terraform’s standard expression model

References

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions