There’s a moment in most Terraform projects where you just want to say, “I don’t care what this value is—just accept it.” That’s exactly what the any type constraint allows. But like most shortcuts in infrastructure code, it comes with trade-offs.
Let’s break down what Terraform any type really does, where it makes sense, and where it quietly introduces problems.
What does any mean in Terraform?
In Terraform, the any type constraint means a variable can accept any valid Terraform value: string, number, list, map, object, or even nested combinations.
Here’s the simplest example:
1variable "config" {
2 type = any
3}
4That’s it. No restrictions. Terraform will accept whatever is passed.
This flexibility is powerful—but also dangerous if used carelessly.
A quick real-world use case
Imagine you're writing a reusable module that forwards configuration to another system:
1variable "settings" {
2 type = any
3}
4
5resource "some_service" "example" {
6 configuration = var.settings
7}
8Here, you don’t control the structure of settings. You just pass it through. This is one of the few situations where any type constraint in Terraform actually makes sense.
Here’s where things get tricky
A common mistake developers make is using any to avoid thinking about structure early on. It feels convenient… until your module grows.
Consider this:
1variable "instance_config" {
2 type = any
3}
4Later in your code:
1resource "aws_instance" "example" {
2 instance_type = var.instance_config["type"]
3}
4If someone passes a string instead of a map, Terraform won’t catch it until runtime. That’s where debugging becomes painful.
Key issue: With
any, you lose early validation and type safety.
Using any safely with validation
If you must use any, pair it with a validation block. This gives you some control back.
1variable "input" {
2 type = any
3
4 validation {
5 condition = can(var.input["name"]) && can(var.input["size"])
6 error_message = "Input must include 'name' and 'size'."
7 }
8}
9This pattern ensures required keys exist, even though the type is flexible.
You can go further:
1validation {
2 condition = can(tostring(var.input.name))
3 error_message = "'name' must be a string."
4}
5It’s not as clean as strict typing, but it prevents silent failures.
Better alternative: Explicit object types
Most of the time, you’re better off defining structure explicitly:
1variable "instance_config" {
2 type = object({
3 type = string
4 size = number
5 tags = map(string)
6 })
7}
8Now Terraform will:
- Validate input automatically
- Provide clear error messages
- Make your module self-documenting
This is the preferred approach for production-grade modules.
What about partially flexible inputs?
Sometimes you want flexibility—but not total chaos.
Terraform supports optional attributes in objects:
1variable "app_config" {
2 type = object({
3 name = string
4 replicas = optional(number, 1)
5 labels = optional(map(string), {})
6 })
7}
8This gives you flexibility without giving up structure.
Handling unknown structures dynamically
If you're working with unpredictable data (like decoded JSON), any becomes more reasonable:
1locals {
2 parsed = jsondecode(file("config.json"))
3}
4Here, Terraform infers the type automatically, which is effectively similar to any.
You can safely access values using:
1lookup(local.parsed, "key", null)
2Or defensively:
1try(local.parsed.key, "default")
2These helpers reduce runtime errors when structure isn’t guaranteed.
Performance and maintainability impact
Using Terraform any type constraint doesn’t directly hurt performance—but it does affect:
- Plan clarity: unclear data structures make diffs harder to read
- Team collaboration: others don’t know what shape data should take
- Error detection: failures happen later instead of earlier
In larger teams, these issues compound quickly.
When you should actually use any
There are a few legitimate scenarios:
- Pass-through variables (forwarding input to another module/resource)
- Working with dynamic or external JSON/YAML data
- Prototyping (short-lived, not production code)
Outside of these, it’s usually a sign you should define a proper type.
When to avoid it
Avoid any if:
- You know the expected structure
- You’re building reusable modules
- You want predictable validation and errors
In these cases, using object, map, list, or tuple types is almost always the better choice.
A simple mental model
Think of any like using interface{} in Go or any in TypeScript:
- Flexible? Yes.
- Safe? Not really.
The more your infrastructure grows, the more that lack of safety matters.
Final takeaway
The Terraform any type constraint is a useful escape hatch—but it shouldn’t be your default. Use it deliberately, not lazily.
If you can describe your data, you should. Terraform’s type system is there to protect you from subtle bugs, and leaning into it pays off quickly in real-world projects.