Terraform Output Command : Examples, Tips and Best Practices
env0 Team
Posted on May 9, 2024
Think of Terraform as a master builder who uses two essential tools to bring our digital infrastructure to life: inputs and outputs.
Input variables are our way of giving Terraform the blueprint - telling it exactly how we want our digital house to look. On the other hand, output values are Terraform’s way of providing a detailed tour of our new digital home.
This blog will explore how we can effectively utilize terraform output
command to access and share details about our infrastructure, ensuring we have all we need to move in and make it our own.
What are Terraform Output Variables
Terraform output is a feature within Terraform CLI, that enables users to extract and present data from their infrastructure configurations.
These outputs can be considered the return values of a Terraform module. They allow access to resource attributes and other important information after completing the infrastructure.
Outputs are particularly valuable in scenarios where visibility into cloud resources is essential. For example, after creating an AWS S3 bucket, the output can retrieve and display the bucket's name, which other applications or services can utilize.
Terraform Output values vs Terraform Input variable
One quick and easy way to understand what outputs are and how they are used, is by contrasting them with Terraform input variables:
Practical Examples of Terraform Output
Terraform outputs are essential for capturing information about the infrastructure. They are specifically handy for retrieving important details such as public IP addresses, DNS names, and other crucial data from the cloud infrastructure.
Outputs can be used within Terraform to share data between modules, or outside of Terraform to integrate with other systems or provide information to users.
1. Defining Output Values in Terraform Configurations
To define output values in Terraform, you follow these steps:
- Open your Terraform configuration file, typically named main.tf.
- Use the
output
command to define an output value. Provide a name for the output, which will be used to reference it. For example:output “public ip” {}
- Assign a value to the
output
, which is usually an attribute of a resource that Terraform manages.
Here's an example of defining output values for the public IP address and DNS name of an AWS EC2 instance:
output "public_ip" {
value = aws_instance.env0_instance.public_ip
}
output "dns_name" {
value = aws_instance.envo_instance.public_dns
}
In this example, aws_instance.env0_instance
refers to an EC2 instance defined elsewhere in the Terraform configuration.
The .public_ip
and .public_dns
are attributes of the EC2 instance that Terraform will output after the instance is created.
Terraform can expose attributes of resources through output variables, but this approach is not limited to the AWS S3 bucket resource. It can be applied to any resource Terraform manages across various providers.
The process involves identifying the desired attribute from the resource's or provider's documentation on the Terraform registry. Once identified, you can declare an output variable in your Terraform configuration that references this attribute.
Here's an example of how to use the force_destroy
argument within an aws_s3_bucket
resource definition:
resource "aws_s3_bucket" "env0_bucket" {
bucket = "my-unique-env0-bucket"
force_destroy = true
tags = {
Name = "My Env0 Bucket"
Environment = "Development"
}
}
This example demonstrates the use of the force_destroy
argument for an AWS S3 bucket resource in Terraform.
2. Using Terraform Outputs to Retrieve Information
Once you have defined your outputs, you can retrieve them after running terraform apply
.
Terraform will display the outputs at the end of the apply operation in the terminal. Using the terraform output
command, you can also query specific outputs after the fact.
For example, let's assume you have an EC2 instance and want to retrieve its public IP address and DNS name.
First define the EC2 instance in your Terraform configuration.
resource "aws_instance" "envo-instance" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
}
Next. define the outputs for the public IP address and DNS name.
output "public_ip" {
value = aws_instance.envo-instance.public_ip
}
output "dns_name" {
value = aws_instance.envo-instance.public_dns
}
After the apply
is completed, Terraform will display the outputs in the terminal.
You can retrieve them anytime using the terraform output
command, like so:
Following these steps will allow you to use Terraform outputs to manage and retrieve important information from your cloud infrastructure - data that could be essential for maintaining and understanding your deployments.
3. Configuring Terraform Module Output
When working with Terraform modules, outputs can expose specific values that other modules or the root module can consume.
Let's create an S3 bucket within a module and define its output, which can be accessed by the root module or any other module that calls this S3 bucket.
Your first step will be to create a directory for the S3 bucket module, such as modules/s3_bucket. Within that directory, create a file named main.tf, which defines the S3 bucket resource.
resource "aws_s3_bucket" "envo-bucket-098980" {
bucket = "envo-bucket-098980"
}
In the same modules/s3_bucket directory, create an output.tf file to define the output for the S3 bucket name.
output "bucket_name" {
value = aws_s3_bucket.envo-bucket-098980.bucket
}
Now, you can call the S3 bucket module in your root module and access its output.
module "s3_bucket" {
source = "./modules/s3_bucket"
}
In the root module's outputs.tf file, add an output block to retrieve the S3 bucket name from the S3 bucket module.
output "s3_bucket_name" {
value = module.s3_bucket.bucket_name
}
Following these steps, you created a reusable S3 bucket module with Terraform and exposed the bucket name as an output variable.
4. Using Custom Conditional Validation in Output
Terraform allows for the use of custom conditional validation within output values. This feature ensures that an output value meets specific criteria before it is exposed to the rest of your Terraform configuration or external services.
Conditional and validation rules can help prevent the propagation of incorrect or undesired data, which is especially important in automated environments like env0, where Terraform configurations might be applied as part of a CI/CD pipeline.
Suppose you have a Terraform configuration where you want to output an environment name, but only if it starts with the prefix "env0".
First, declare an input variable with the environment name.
variable "environment_env0" {
description = "The name of the environment"
type = string
}
Next, create an output that only outputs the environment name if it starts with "env0". You can use the startswith
function to check the string's prefix.
output "validated_environment_name" {
description = "The environment name if it starts with 'enve*"
value = startswith(var.environment_name, "env") ? var.environment_name "Invalid name
}
In this example, the startswith
function checks if the environment_name
variable starts with the string "env0". If the condition is true, the output will be the environment_name
value. If not, it will output the error message "Invalid name".
When you run terraform apply
, you must provide a value for the environment_name
variable. If the provided name starts with "env0", the output will display the valid environment name.
Otherwise, it will display "Invalid name":
5. Examples of Terraform Output Flags
One of the flags you can use is -json
, which formats the outputs as a JSON object. This is especially useful when you want to pipe the output into other tools that can process JSON, such as jq, a lightweight command-line JSON processor.
Let's go through a step-by-step example. This example will show you how to create an S3 bucket using Terraform, output its name and ARN, and then format the output using jq.
First, you must define your Terraform configuration to create an S3 bucket.
provider "aws" {
region = "us-east-1"
}
resource "aws_s3_bucket" "envo-bucket-jq-0974" {
bucket = "my-envo-bucket"
}
output "bucket_name" {
value = aws_s3_bucket.env0-bucket-jq-0974.bucket
}
output "bucket_arn" {
value = aws_s3_bucket.envo-bucket-jq-0974.arn
}
After applying your Terraform configuration, you can use the terraform output -json
command to format the outputs as JSON. Then, you can pipe this JSON output to jq for further processing.
To get the bucket name and ARN in a clean format, you can run:
terraform output -json | jq -r '.bucket_name.value, .bucket_arn.value'
This command uses jq to extract the values of the bucket_name
and bucket_arn
outputs from the JSON, formatted by terraform output -json
. The -r
flag with jq outputs raw strings, removing the quotes around the values.
This method is beneficial for scripting and automation, where you might need to use Terraform outputs as input and output variables for other commands or scripts.
6. Using Terraform Output Arguments
Terraform outputs can be customized with arguments like sensitive
to manage data visibility and dependencies.
The sensitive argument ensures that values like passwords or keys are not displayed in the console, marking them as sensitive in the Terraform state.
Suppose you have a local secret key that you want to output, but you want to ensure it's marked as sensitive data to prevent it from being displayed in plain text.
variable "secret_key" {
description = "A sensitive secret key"
type = string
}
output "secret_key_output" {
value = var.secret_key
sensitive = true
}
In this example, the sensitive argument is true for the secret_key_output, which means that Terraform will have sensitive values that will not display their value when running terraform apply or terraform output.
Advanced Use-Cases: Multiple S3 Buckets with for_each
In Terraform, advanced output techniques often involve iteration constructs such as for and for_each
loops.
These loops can be used within output blocks to dynamically generate output values based on the state of your resources.
The for_each
loop, in particular, is useful when you want to create many instances of a resource and to sensitive output values for attributes in a structured way.
Suppose you want to create five S3 buckets and output their names and ARNs (Amazon Resource Names).
You can save yourself some time use the for_each
loop to make these buckets and then a for loop within an output block to list their names and ARNs.
First, you'll use a for_each
loop to create multiple S3 buckets based on a set of given names.
variable "bucket_names" {
description = "A set of S3 bucket names"
type = set(string)
default = ["envo-bucket1-one", "envo-bucket2-two", "envo-bucket3-three", "envo-bucket4-four", "envo-buckets-five"]
}
resource "aws_s3_bucket" "envo-bucket-for-each" [
for_each var.bucket_names
bucket = each.value
}
Here, the for_each
loop iterates over the set of bucket names provided in the bucket_names
variable, creating a new S3 bucket for each name.
Next, you'll define an output block that uses a for loop to construct a map of the bucket names and their corresponding ARNs.
output "bucket_details" {
value = { for name, bucket in aws_s3_bucket.envo-bucket-for-each
name > bucket.arn}
description = "A map of S3 bucket names and their ARNS"
}
This output block creates a map where each key is the name of a bucket, and each value is the ARN of that bucket. The for loop iterates over the aws_s3_bucket.buckets resource, which contains all the buckets created by the loop.
Using env0 Workflows Dependency Management
In Terraform, effectively managing dependencies between modules and resources is important for creating reusable infrastructure and reusing variables in each module.
For example, if you need to use the output of an IAM role (e.g., the role's ARN), you typically reference the ARN output and pass it as an input variable to the EKS cluster.
env0 Workflows feature streamlines the process, allowing you to effortlessly utilize outputs from one stack in another, eliminating the need for manual transfers or supplementary tools.
Once set, env0 automatically retrieves and assigns the outputs from designated upstream environments as environment variables in the dependent environments.
Besides saving time and effort, this also enhances security by ensuring that all configurations are centrally managed and consistently updated.
Let's break down how this works.
Step 1: Enable Environment Output
Start by navigating to the 'Policies' tab under 'Project Settings' in your env0 dashboard and activate ‘Environment Output’. This will allow env0 to capture outputs from one environment and make them available in others.
Step 2: Create the First Environment
Set up your first environment. For instance, using this Terraform code to create an AWS S3 bucket:
provider "aws" {
region = "us-east-1"
access_key = var.aws_access_key
secret_key = var.aws_secret_key
}
variable "aws_access_key" {
type = string
default = ""
}
variable "aws_secret_key" {
type = string
default = ""
}
resource "aws_s3_bucket" "aws-bucket-env012" {
bucket = "aws-bucket-env0-1223"
tags = {
Name = "env0bucketaws12"
}
}
output "bucket_name" {
value = aws_s3_bucket.aws-bucket-env012.bucket
}
Once done, run your Terraform code to deploy this environment.
Make sure the deployment is successful and that the output bucket_name
is generated. In this example, this is the output that you will use as input for our next environment.
Step 3: Capture the Output
Return to the project settings in your env0 dashboard and select the 'Variables' tab.
Create a new variable, selecting the bucket_name
output from the deployment you’ve just completed.
Step 4: Set Up the Second Environment
Configure another environment, using the captured ‘bucket_name’ to set up a new resource.
provider "aws" {
region = "us-east-1"
access_key = var.aws_access_key
secret_key = var.aws_secret_key
}
variable "aws_access_key" {
type = string
default = ""
}
variable "aws_secret_key" {
type = string
default = ""
}
variable "bucket_name" {
type = string
default = ""
}
resource "aws_s3_bucket" "bucket-env0-3232" {
bucket = "${var.bucket_name}-env0343"
tags = {
Name = "Bucket 2"
}
}
In the variable section, you'll that the environment output is already auto-added to the new environment:
As you execute the Terraform code, this setup will create another S3 bucket, dynamically naming it using the first environment's output:
And that it’s, now output from the first environment and used it as the input of the second one, allowing you to easily string them together and in a continuous workflow.
Frequently Asked Questions
Q: Where are Terraform Output values stored?
Terraform output values are stored in the state file.
Q: Can we use the count argument for terraform output values?
No, you cannot directly use the count
argument within an output definition in Terraform. The count argument creates multiple instances of a resource or module but cannot be applied directly to outputs.
However, when you have resources created with the count
, you can reference these resources in your outputs using a splat expression or other methods to aggregate or format the values from the multiple instances created.
Q: How do you store Terraform plan output?
To store terraform plan
output, you can direct it to a file using the -out option with the terraform plan command, like so:
terraform plan -out=tfplan
This creates a binary file named tfplan that can be used later with terraform apply
. Redirect the plan to a text file for human-readable output, but note this format is for review only and not for execution.
Q: How can you format or manipulate output values in Terraform?
In Terraform, you can format or manipulate output values using built-in functions within the output definitions in your Terraform configuration files. Here's a short example using the format function to manipulate a string output:
output "formatted_output" {
value = format("This VM has IP: %s", aws_instance.example.public_ip)
}
Terraform provides various functions, such as format, join, split, replace, and mathematical, to manipulate strings, lists, and numbers within your outputs.
Q: Can a Terraform output be a map?
In Terraform, you can define an output as a map
by assigning a map value to the output's value attribute. This is useful for organizing and returning a collection of related values. For example, if you have multiple instances with their respective IDs and IPs, you can output these as a map where each instance ID is a key, and its IP is the corresponding value.
Conclusion
Terraform outputs are essential for effectively managing and disseminating information within Infrastructure as Code (IaC), offering insights such as IP addresses and DNS names.
Unlike input variables that set up resource configurations, outputs facilitate the external sharing of data.
Advanced methods like loops and conditional outputs enhance their practical application in extracting key infrastructure details. Furthermore, output arguments, notably 'sensitive', improve security measures.
Incorporating Terraform outputs into automation tools like env0 can serve as an effective way to manage dependencies and streamline complex deployments and other infrastructure management processes.
Posted on May 9, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.