Handling unexpected scenarios in Terraform can feel like navigating a maze, especially if you’re just starting out. But what if there was a way to make your configurations more resilient and avoid frustrating failures?
Well good news, because that’s exactly what the try
function does! It’s like having a safety net for your Terraform plans, ensuring they run smoothly even when something goes wrong.
In this guide, you’ll discover what the try
function is, why it’s a game-changer, and how to use it to create error-proof configurations. So that by the end, you’ll have the tools to handle Terraform challenges with confidence.
Let's get started…
Sidenote: Want to dive deeper into learning and using Terraform? Then check out my DevOps Bootcamp for Terraform.
Updated for 2025, I cover everything from the fundamentals all the way to provisioning real-world cloud infrastructure on AWS. You can go from total beginner to being able to get hired as a DevOps Engineer or System Administrator, so it’s a great resource for any skill level.
With that out of the way, let’s get into this 5-minute tutorial…
When working with Terraform, you’ll often encounter inputs or configurations that might not behave as expected, such as a missing variable, an invalid value, or a resource that doesn’t return data.
Normally, these kinds of issues would cause your configuration to fail, forcing you to troubleshoot and fix them manually. That’s where the try
function steps in. It’s a built-in tool designed to catch errors before they can disrupt your setup, ensuring Terraform keeps running smoothly.
Here’s how it works:
The try
function evaluates multiple expressions in order and returns the first one that doesn’t produce an error. If none of the expressions succeed, it throws an error, giving you visibility into what went wrong. By doing this, try
not only saves you time but also simplifies how you handle edge cases and optional inputs.
Think of it as an automatic fallback system. Instead of writing complex conditional logic for every possible failure, you can provide a list of options, and Terraform will choose the first valid one. This makes your code cleaner, more maintainable, and less prone to errors.
Here’s the syntax:
try(expression1, expression2, ..., expressionN)
Here's how it works:
As you can imagine, this makes your life a lot easier. Especially when you consider that Terraform configurations often deal with variables or resources that don’t always behave as expected.
Optional variables might not be set, resources could fail to return data, or multi-cloud setups might require juggling different attributes for the same concept. Without a way to handle these uncertainties, your configurations risk becoming error-prone and brittle.
The try
function solves this problem by letting you define fallback values for invalid inputs, missing resources, or provider-specific variations.
I’ll show you some examples of these in action later, but lets break these down quickly so you get the idea.
try
function to handle optional variablesIn shared modules or multi-environment setups, some inputs—like a region
variable—might not always be provided. However, instead of breaking your setup, the try
function ensures there’s always a fallback value to keep your configuration functional.
It gets better though.
try
function to provide fallback values for dynamic resourcesDynamic resources, such as querying for the latest AMI in AWS, might not always return data.
For example
If no AMIs match your criteria, your configuration could fail. But by using the try
function, you can supply a fallback value and ensure your setup stays operational.
try
function to adapt to multi-cloud differencesWhen deploying across multiple cloud providers, resource attributes often differ. AWS uses instance_type
for EC2 instances, while Azure uses vm_size
.
The good news?
Instead of having to write complex conditional logic, the try
function lets you handle these differences gracefully by checking for available attributes and falling back to a default when necessary.
The try
function is incredibly handy to use. You just need to make sure you use it correctly and don’t make a few common mistakes. The good news is, I'll walk you through it all now.
Using the try
function effectively isn’t just about writing valid code—it’s about ensuring your configurations are reliable, maintainable, and resilient to unexpected issues.
By planning carefully, writing clear logic, and testing thoroughly, you can handle errors gracefully and avoid unnecessary troubleshooting later.
Speaking of planning, that brings me to step #1.
Before you start writing Terraform code, it’s important to map out how your try
function will handle potential issues.
null
"us-west-1"
for a region is clear and functional, whereas "unknown"
could lead to confusion or unintended outcomes. This principle applies not only to missing variables but also to dynamic resources or multi-cloud setups where defaults ensure stabilityFor example: Planning for a missing region variable
Suppose some teams using your module forget to set the region
variable. You can plan for this by providing a default:
variable "region" {
default = null
}
output "selected_region" {
value = try(var.region, "us-west-1")
}
Tip: Always choose meaningful fallback values that align with your infrastructure’s goals. This keeps your configurations clear and intentional.
Why a final fallback is critical
Without a final fallback, Terraform throws an error if all expressions fail:
# Without a fallback, this will fail
value = try(null, null)
To avoid this, make sure every try
function ends with a valid fallback:
value = try(null, null, "default_value")
Taking the time to plan ahead saves you from troubleshooting later and ensures your configurations handle edge cases gracefully.
Once you’ve planned for potential issues and chosen meaningful fallback values, it’s time to put try
into action. The key is to write configurations that are both functional and easy to maintain.
Let’s explore a few common use cases and issues to watch out for.
Optional variables are a frequent scenario where try
shines. Instead of letting a missing value cause errors, you can specify a fallback:
output "selected_region" {
value = try(var.region, "us-west-1")
}
It’s tempting to cram as much logic as possible into a single try
function, but this can quickly make your code harder to read and debug. So instead, break your logic into smaller, manageable components:
variable "primary_value" {
default = null
}
variable "secondary_value" {
default = "fallback"
}
output "example_output" {
value = try(var.primary_value, var.secondary_value, "default_value")
}
Dynamic resources, like AMIs, don’t always return data. Not only that, but Dynamic resources often change based on external conditions.
The good news is that using try
ensures your configuration remains stable, even when unexpected issues arise. So instead of letting your configuration fail, you can use try
to set a fallback:
data "aws_ami" "example" {
most_recent = true
owners = ["self"]
}
output "ami_id" {
value = try(data.aws_ami.example.id, "ami-12345678")
}
Watch out for this mistake: Mixing data types in your try
function will cause Terraform to throw an error. All expressions must return the same type:
# Incorrect: Mixing string and number types
value = try("string", 123)
To avoid this, ensure all expressions are consistent:
value = try("string", "fallback_string")
When deploying across multiple cloud providers, you’ll often encounter differing attributes for similar resources.
For example
AWS uses instance_type
, while Azure uses vm_size
. Instead of writing separate conditional logic for each provider, try
lets you handle these variations seamlessly:
output "cloud_instance_type" {
value = try(
aws_instance.example.instance_type,
azurerm_virtual_machine.example.vm_size,
"default-instance-type"
)
}
try
configurationYou won’t face these situations every time, but when you do, the try
function is your safety net. It helps you handle the unexpected without overcomplicating your setup.
Now, let’s move on to testing your configuration to make sure everything runs exactly as planned.
Testing ensures your configuration can handle anything Terraform throws its way, from missing inputs to invalid data. Without proper testing, what works in development might fail spectacularly in production.
The best approach? Start small with isolated tests, then scale up to validate try’s behavior in more complex scenarios. Here’s how you can systematically test your configurations for complete confidence.
Let’s start simple. Set your variables to valid values and check that try
selects the correct one:
variable "primary_value" {
default = "custom"
}
If this works, then our expected output here will be: "custom"
.
Next, let’s test a scenario where things don’t go as planned. If the input is null
, does try
fall back to the next value?
variable "primary_value" {
default = null
}
output "example_output" {
value = try(var.primary_value, "fallback_value")
}
Your output here should be "fallback_value"
. That’s exactly what you’d expect, and try
makes it effortless.
Now for the big test—what happens if everything fails? Remove all valid inputs to check if Terraform throws a meaningful error:
output "example_output" {
value = try(null, null)
}
The result? Terraform will return an error message, letting you know no valid value was found. This is where testing saves you from production headaches.
Once you’re confident in your local testing, it’s time to verify that your configuration behaves consistently across staging, production, and other environments.
This ensures that fallback logic isn’t broken by differences in resource availability or environment-specific configurations.
**For example **
Imagine you’re deploying a module that references storage buckets. In staging, the bucket might be optional, while in production, it’s required. Without proper fallback logic, the deployment could fail or provision resources incorrectly.
By testing, you can:
Testing across valid, invalid, and edge-case scenarios gives you complete confidence in your configuration. It’s like stress-testing your setup to see how it holds up.
That being said though, testing is only part of the process. So once you know everything behaves correctly, it’s time to refine and document your setup for long-term maintainability.
This step not only improves clarity for others (and your future self) but also ensures your code remains maintainable as your infrastructure evolves.
Tools like Terraform’s terraform-docs
can save you hours by automating documentation. Using these tools ensures your fallback logic and configuration decisions are clear to anyone working with your code.
When your fallback logic is straightforward, simpler alternatives like coalesce
or lookup
might be a better fit than try
. These functions focus on specific use cases and are easier to read:
output "better_logic" {
value = coalesce(var.optional_value, "fallback_value")
}
However, for complex fallback chains, stick with try
to maintain flexibility without over complicating your configuration.
Good documentation can save hours of debugging down the road, so make sure to include comments that explain:
"us-west-1"
aligns with a specific regional redundancy) would help someone understand the relevance and help catch any errorsFor example
# Use primary_value if set; fallback to secondary_value, or "default_value" if both are null
output "example_output" {
value = try(var.primary_value, var.secondary_value, "default_value")
}
The try
function in Terraform is more than just a way to handle errors—it’s your toolkit for building smarter, cleaner, and more resilient configurations. By following these steps, you can:
Your challenge: Try using the try
function in a small Terraform project, like an S3 bucket, and test different inputs. Start small, test thoroughly, and watch as your configurations become more resilient.
Don’t forget! If you want to dive deeper into learning and using Terraform, check out my DevOps Bootcamp for Terraform.
I cover everything from the fundamentals all the way to provisioning real-world cloud infrastructure on AWS and getting hired!
Once you join, you'll have the opportunity to ask questions in our private Discord community from me, other students and working professionals.
Not only that, but as a member - you’ll also get access to every course in the Zero To Mastery library!