DEV Community

Cover image for Terraform Lookup Function: Examples, Use Cases and Best Practices
env0 Team for env0

Posted on • Originally published at env0.com

Terraform Lookup Function: Examples, Use Cases and Best Practices

The Terraform lookup function helps maintain clean, DRY, and reusable code. In this blog, we'll explore the various scenarios where the lookup function can be used, and provide some practical examples for both common and more advanced use cases.

Disclaimer

All use cases of Terraform lookup function discussed here work similarly in OpenTofu, the open-source Terraform alternative. However, to keep it simple and familiar for DevOps engineers, we will refer to these as Terraform lookup throughout this blog post.

What is Terraform Lookup Function

The Terraform lookup function is used to retrieve a specific value from a Terraform map based on a particular key. 

For instance, you can use lookup to dynamically fetch the configuration values for your AWS instance based on the environment in which you want it to be deployed.

Syntax of Terraform Lookup Function

The syntax for the Terraform lookup function accepts three arguments: map, key, and default_value, like so:

lookup(map, key, default_value)
Enter fullscreen mode Exit fullscreen mode

Breaking things down, this is what each of these arguments means:

  • map: The map variables containing the key-value pairs
  • key: The key for which the value will be fetched from the map
  • default_value: The value returned if the map does not contain the key

To demonstrate how these come together, let’s look at VPC ID allocation, where we have a string map vpc_ids

Using the code below, you can select the VPC using the lookup function based on the environment, such as dev and prod:

variable "vpc_ids" {
  type        = map(string)
  default = {
    dev     = "vpc-1ae23bn342cd"
    staging = "vpc-1ae23bn34ecd"
    prod    = "vpc-1aa23bn342cd"
  }
}
resource "aws_vpc" "selected" {
  id = lookup(var.vpc_ids, ”prod”, "vpc-default")
}
Enter fullscreen mode Exit fullscreen mode

Use Cases for Terraform Lookup Function

You can utilize the Terraform lookup function in various scenarios, such as with dynamic keys, custom values, and more. Let’s explore them:

  • If you have a map of environment-specific configurations (like ‘production’ and ‘development’), and you want to provide a default value for when a specific key is not found, you can pass the default value in the lookup function to your resources
  • If you want to manage different configurations for multiple environments, you can use the lookup function as it can handle nested maps like a map of string maps
  • If you want to pass the custom value for the region in your Terraform code, you can use lookup to map custom values to internal identifiers or configurations
  • You can allocate resources dynamically using the Terraform lookup function, such as passing the instance_type for the aws_instance resource based on the environment

Now that we have a broad idea of how lookup works and its use cases, let’s dive in a bit into the Terraform map to understand how values are retrieved.

The default_key Argument  

Importantly, the default_value argument helps avoid failures in cases where the key is not present. This makes Terraform lookup a better alternative to var and local which accept static values and cannot handle failures.

Terraform lookup is a better fit when trying to dynamically access values based on the input value or condition, providing an easier and more fault-proof way to manage your Terraform configuration.

A bit About Terraform Map 

To understand how lookup works, it’s also important to have a quick discussion about the Terraform map. 

In short, Terraform map is a data structure that stores a collection of key-value pairs. It is used to store related data and write reusable code, making it easier to manage Terraform configurations.

Maps can be combined with other data structures. Some of these are:

  • map(string): Map contains ‘string’ type values
  • map(number): Map contains ‘number’ (integer or floating-point) type values
  • map(bool): Map contains ‘bool’ (true or false) type values
  • map(list): Map contains list (arrays) type values containing elements of the same type
  • map(set): Map contains set type values containing unique elements of the same type
  • map(object({ ... })): Map contains objects (complex data structures) type values that must conform to a specific structure defined by the object’s attributes

To explain how to use the Terraform map in your Terraform configuration, let’s take a look at this example where we have defined a map variable instance_size containing the ‘string’ type values. 

variable "instance_size" {
  type        = map(string)
  default = {
    t2.micro    = "small"
    t2.medium= "medium"
    t2.large   = "large"
  }
}
Enter fullscreen mode Exit fullscreen mode

Using the Terraform CLI, we will test how we can get the values in the map variable for the specified key using the syntax: map["key"]. Here, the key “t2.micro” gives you the value small.

$ env0 git : (main) x terraform console
> var.instance_size[“t2.micro”]
"small
Enter fullscreen mode Exit fullscreen mode

Examples of How to use the Lookup Function 

The lookup function is particularly useful for managing infrastructure configurations in Terraform. Let us do some hands-on exercises to understand it.

Terraform Lookup with a Map of String

We will start with defining a ‘s3_buckets’ variable to store the s3 bucket configurations, and the ‘environment’ variable to store the environment for which you want to get the s3 bucket configuration in your var.tf file.

variable "environment" {
  type    = string
  default = "production"
}
variable "s3_buckets" {
  type = map(string)
  default = {
    development = "myapp-dev-bucket"
    staging     = "myapp-staging-bucket"
    production  = "myapp-prod-bucket"
  }
}
Enter fullscreen mode Exit fullscreen mode

Next, we will create a aws_s3_bucket resource for the production environment with the s3 bucket name myapp-prod-bucket using the lookup function in your Terraform configuration main.tf file.

resource "aws_s3_bucket" "s3_bucket" {
  bucket = lookup(var.s3_buckets, var.environment, "default-bucket-name")
}
Enter fullscreen mode Exit fullscreen mode

As per the lookup function above, you can check the name of the s3_bucket by running the terraform console command in your terminal.

$ env0 git : (main) x terraform init
$ env0 git : (main) x terraform plan
$ env0 git : (main) x terraform console
> aws_s3_bucket.s3_bucket
"myapp-prod-bucket”
Enter fullscreen mode Exit fullscreen mode

Terraform Lookup with an Object Map 

Here we will start with creating a Terraform configuration in the var.tf file, defining a instance_configs variable which stores the instance_type based on the different environments (development, production). 

In addition, we will also define the ‘environment’ variable to store the development value for which we will create the aws_instance

variable "environment" {
 type    = string
 default = "development"
}
variable "instance_configs" {
  type = map(object({
    instance_type = string
  }))
  default = {
    development = {
      instance_type = "t2.micro"
    }
    production = {
      instance_type = "m5.large"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Deploying the aws_instance for the development environment using the lookup function will fetch the lighter instance_type, based on the value of the environment variable.

resource "aws_instance" "dev_instance" {
  instance_type = lookup(var.instance_configs[var.environment], "instance_type", "t3.micro")
  ami           = "ami-0c55b159cbfafe1f0"
}
Enter fullscreen mode Exit fullscreen mode

To see that it works, you can verify that the value of the instance_type is t2.micro by using the terraform console:

$ env0 git : (main) x terraform init
$ env0 git : (main) x terraform plan
$ env0 git : (main) x terraform console
> aws_instance.dev_instance.instance_type
"t2.micro"
Enter fullscreen mode Exit fullscreen mode

Lookup Function When No Key Value is Found

Now, still using the above example, let’s assume that you would like to change the value for the environment variable to ‘integration’. 

Since it does not exist, the value of the instance_type will change to the default value passed in the lookup function, which is t3.micro

Again, you can verify this by using the Terraform console in your terminal:

$ env0 git : (main) x terraform init
$ env0 git : (main) x terraform plan
$ env0 git : (main) x terraform console
> aws_instance.dev_instance.instance_type
"t3.micro"
Enter fullscreen mode Exit fullscreen mode

Terraform Lookup with Nested Map 

Here, you will configure the ami and the instance_type for an aws_instance resource using the Terraform lookup function with a map of string maps.

First, let's define an instance_config variable, a “map of maps” containing string values for ami and instance_type for the development and production environment in the var.tf file. 

Next, we will create an ‘environment’ variable to store the value of the environment for which you would like to create the aws_instance:

variable "instance_config" {
   type    = map(map(string))
  default = {
    development = {
      ami           = "ami-0abcdef1234567890"
      instance_type = "t2.micro"
    }
    production = {
      ami           = "ami-1234567890abcdef0"
      instance_type = "t2.large"
    }
  }
}
variable "environment" {
   type        = string
  default     = "development"
}
Enter fullscreen mode Exit fullscreen mode

Now, let us filter the ami and instance_id using the Terraform lookup function in the nested form and deploy an aws_instance in the main.tf file. 

resource "aws_instance" "ec2" {
  ami           = lookup(lookup(var.instance_config, var.environment), "ami")
  instance_type = lookup(lookup(var.instance_config, var.environment), "instance_type")
}
Enter fullscreen mode Exit fullscreen mode

You can check the value for the aws_instance resource using the Terraform console in your terminal:

$ env0 git : (main) x terraform init
$ env0 git : (main) x terraform plan
$ env0 git : (main) x terraform console
> aws_instance.ec2.instance_type
"t2.micro"
> aws_instance.ec2.ami
"ami-0abcdef1234567890"
Enter fullscreen mode Exit fullscreen mode




Best Practices of Using Terraform Lookup Function

Here are some of the best practices you should follow while using the Terraform lookup function:

  • Always make sure to provide the default value in the lookup function to handle a failure scenario where the key is not present in the map
  • Avoid the type mismatch error by maintaining the consistency of the map value type and the default value in the lookup function
  • Verify the existence of the key in the input map of the lookup function by using the contains function to ensure that the correct configuration value is passed
  • Make your code more flexible and maintainable using variables or dynamic expressions instead of hardcoding the keys so that you can change them easily

Terraform Lookup with env0

With env0's Custom Workflow capabilities, you can automate the deployment process, manage environment variables, and efficiently build a secure, reliable infrastructure using best practices. 

This section will walk you through the process of leveraging env0’s environment variables with the Terraform lookup function, to deploy EC2 instances based on the environment. 

Step 1: Define the Terraform Configuration

Let’s set up the AWS provider in your provider.tf file: 

provider "aws" {
region = var.region
access_key = var.access_key
secret_key = var.secret_key
}
Enter fullscreen mode Exit fullscreen mode




Step 2: Define Variables

Define your variables in the var.tf file. The environment variable will allow you to customize your infrastructure based on dev or prod environments.

variable "region" {
type = string
}

variable "access_key" {
type = string
}
variable "secret_key" {
type = string
}
variable "environment" {
type = string
default = "dev"
}

Enter fullscreen mode Exit fullscreen mode




Step 3: Define Local Values Using Terraform Lookup Function

You can pass the value to instance_type using the lookup function, where you can dynamically select the instance_types based on the environment in locals.tf.

locals {
instance_types = {
dev = "t2.micro"
prod = "t3.medium"
}
instance_type = lookup(local.instance_types, var.environment, "t2.micro")
}
Enter fullscreen mode Exit fullscreen mode




Step 4: Create EC2 Instance

You can create AWS EC2 instances based on a dynamically generated list of instance_names in your main.tf file.

resource "aws_instance" "Infrasity" {
ami = "ami-068e0f1a600cd311c"
instance_type = local.instance_type
tags = {
Name = "Instance-1"
Environment = var.environment
}
}
Enter fullscreen mode Exit fullscreen mode




Step 5: Integrate with env0

Now, we will integrate our Terraform configuration with env0 by following these steps:

  1. Create Environment: Go to 'CREATE NEW ENVIRONMENT' under your project in env0

  1. Connect GitHub Repository: Connect the GitHub repository to env0, where your Terraform code is stored remotely

  1. Configure Environment Variables: Navigate to the environment in env0. Go to the Settings tab. Under ‘Environment Variables’, add the following variables:
  • ‘TF_VAR_region’: Set this to your desired AWS region
  • ‘TF_VAR_access_key’: Enter your AWS access key
  • ‘TF_VAR_secret_key’: Enter your AWS secret key
  • ‘TF_VAR_environment’: Specify the environment type, either dev or prod

  1. Deploy the Environment: You can deploy your environment from the env0 dashboard. To start the deployment process, either click on 'Run Deploy' or save the environment variables. The process states are shown in the Deployment-Logs section.

After successfully deploying your infrastructure, you can check the instances via the AWS console:

Meanwhile, the Terraform lookup function enables dynamic and flexible configuration management to control and customize infrastructure variables and settings. 

With env0, you can efficiently manage your Terraform environments, apply changes, and monitor your infrastructure using one tool, as you also follow best practices.

Conclusion

By now, you should have a clear understanding of the functionality of the Terraform lookup function and its use cases. 

We looked at how to use the Terraform lookup function with simple or complex Terraform map types and how to use env0’s capabilities with the Terraform lookup function to manage infrastructure effectively.

Frequently Asked Questions

Q. What is the alternative to lookup in Terraform?

The lookup function alternative is the element function. The key distinction between these functions lies in the type of object they target. The element function is designed to iterate over and retrieve values from a list, whereas the lookup function is aimed at maps.

Q. What happens if the specified key is not found in the map?

If the specified key is not present in the map, the Terraform lookup function will return the default value.

Q. Can I use the lookup function with lists or arrays?

The Terraform lookup function cannot be used with a list or array. It is specifically designed for map and object data structures. However, you can access elements within a list using the element function.

Q. Is the Terraform lookup function available for all versions of Terraform?

You can utilize the lookup function in Terraform version 0.12 and above. For earlier versions, you may need to use the map and element functions to achieve similar results.

Q. What is the difference between the Terraform lookup and try function?

Let’s look at the difference between Terraform lookup and try function, which are often mentioned together, with try being a more general form of lookup, preferred by some for its simplicity:

the difference between Terraform lookup and try function

Top comments (1)

Collapse
 
whimsicalbison profile image
Jack

Thank you for writing this article; I thoroughly enjoyed it! I've been using Terraform daily for quite some time now and consider myself nearing expert level, but I hadn't come across this function before. I appreciate the insight!