Imagine our Terraform configuration is like a function in a programming language, then outputs are like return values of this function. Terraform outputs allow us to extract values we care about from our various resources created with Terraform, and use them for something outside of our Terraform configuration. Examples of outputs could be:
- Hostname for an API-Gateway
- Connection details for a database
- Randomly generated numbers
- A list of resource IDs for your 200 new virtual machines
An illustration of what outputs are is shown in the figure below.
In this lesson I continue to go through part 8 of the Certified Terraform Associate exam curriculum. This part of the curriculum is outlined below:
Part | Content |
---|---|
8 | Read, generate, and modify configuration |
(a) | Demonstrate use of variables and outputs |
(b) | Describe secure secret injection best practice |
(c) | Understand the use of collection and structural types |
(d) | Create and differentiate resource and data configuration |
(e) | Use resource addressing and resource parameters to connect resources together |
(f) | Use HCL and Terraform functions to write configuration |
(g) | Describe built-in dependency management (order of execution based) |
To be specific: I will cover parts of 8 (a) in this lesson, i.e. Demonstrate use of outputs. We also briefly touch on the subjects in 8 (b), (c), and (e).
Declaring an output#
You declare an output value using the output
block. The output
block accepts one label, which is the symbolic name of the output. This name is used to access outputs from modules1, or when using the terraform output
command.
The general format of the output
block is this:
output "label" {
value = <expression>
description = <string>
sensitive = <bool>
depends_on = <list of resource references>
}
The arguments in the output
block are explained next:
value
takes an expression that evaluates to the value of the output. This could be a fixed value like a string or a number. It could be a property of a resource. It could be a complex object build up from several string concatenations and various properties from several resources.description
is a friendly text describing what the output is.sensitive
is a bool (true
orfalse
, default isfalse
) that specifies if this is a sensitive value, like a password. Iftrue
Terraform will avoid printing the value in clear text in logs.depends_on
is a list of references to resources that this output depends on. This argument is very rarely used and I will not discuss it further in this lesson. If you are curious you can read the official documentation for details.
Apart from the arguments defined above an output
block can optionally include nested precondition
blocks. I have never used a precondition
block in real-life and I did not see any question on the certification exam concerning precondition
blocks. Thus I will not discuss them further, but if you are curious you can read about them in the official documentation.
An output containing a static string looks like this:
output "my_output" {
value = "hello world"
}
That is not a very useful output, since it is static.
Imagine that we have created a virtual machine (an EC2 instance) in AWS with Terraform, and we would like to SSH to this machine. Then we would need the IP-address or hostname of the machine, this would be a good candidate for an output:
output "vm_ip_address" {
description = "IP-address of my EC2-instance"
value = aws_instance.my_ec2_instance.public_ip
}
We could take it one step further and create an output which represents the SSH-command I need to connect to the machine[^ssh]:
output "ssh_command" {
description = "SSH command used to connect to the EC2 instance"
value = "ssh -i mykey.pem ubuntu@${aws_instance.my_ec2_instance.public_ip}"
}
Here I have used string interpolation (${aws_instance.my_ec2_instance.public_ip}
) to include the IP-address in the output string.
Accessing output values#
There are two main ways to access the values of our outputs. The first way involves accessing outputs from a module in a different module, but since I am postponing discussing modules for the future it makes sense to save this method until then.
The second way to access outputs involves using the Terraform CLI. To illustrate how this works I will create this simple Terraform configuration:
// main.tf
terraform {
required_providers {
random = {
source = "hashicorp/random"
}
}
}
resource "random_integer" "lucky_number" {
min = 1
max = 1000
}
output "my_lucky_number" {
value = random_integer.lucky_number.result
}
In this configuration I use the hashicorp/random
provider and I create a random_integer
resource called lucky_number
. This resource creates a random number between min = 1
and max = 1000
. I then create an output for the generated number. I can access the random number by referencing the property called result
on my resource, I do this with random_integer.lucky_number.result
(we’ll see more details about the resource
block in a future lesson).
I initialize this Terraform configuration with terraform init
:
$ terraform init
Initializing the backend...
Initializing provider plugins...
- Finding latest version of hashicorp/random...
- Installing hashicorp/random v3.4.3...
- Installed hashicorp/random v3.4.3 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
Terraform has been successfully initialized!
Then I run the Terraform configuration with terraform apply
(remember that I can skip terraform plan
if I wish, because it is run by terraform apply
anyway):
$ terraform apply -auto-approve
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# random_integer.lucky_number will be created
+ resource "random_integer" "lucky_number" {
+ id = (known after apply)
+ max = 1000
+ min = 1
+ result = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ my_lucky_number = (known after apply)
random_integer.lucky_number: Creating...
random_integer.lucky_number: Creation complete after 0s [id=639]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
my_lucky_number = 639
The last line shows me my output. In this case my_lucky_number
turned out to be 639! If I wish to get the output value in a more convenient format, e.g. if I need to use it in my automation scripts, then I can run the terraform output
command:
$ terraform output
my_lucky_number = 639
I can also specify the name of an output with terraform output
:
$ terraform output my_lucky_number
639
The terraform output
command has one flag that could be important, it is the -raw
flag. I will illustrate its use with the following output:
output "my_static_url" {
value = "https://mattias.engineer"
}
If I run terraform output
to get the value of my_static_url
I get the following result:
$ terraform output my_static_url
"https://mattias.engineer"
The value is surrounded by quotes. This is usually not what we want! If I add the -raw
flag I get the following result (note that the -raw
flag must come before the name of the output):
$ terraform output -raw my_static_url
https://mattias.engineer
Much better!
Summary#
We have seen almost all there is to see about outputs. What is left is how to access outputs from a module in a different module. It is simple stuff, but I just did not show it now because we have not looked at modules yet. Apart from that what you have gone through in this lesson is all you will need to know about outputs for real-life and for the certification exam.
To summarize what we looked at in this lesson:
- We saw how to define an output using the
output
block - We learned that if we have an output that contains a secret we should specify
sensitive = true
when we define the output - We saw how we can define outputs as simple static values, as properties from resources, or as more complex strings built up from static and dynamic parts using string interpolation
- We saw how to access output values using
terraform output
terraform output
will list all outputs from a given configurationterraform output <name>
shows the output named<name>
terraform output -raw <name>
shows the output named<name>
in a raw format, i.e. removing quotation marks around strings
Soon it is time for me to go through modules because I keep referring to them all the time! I promise we will get there in a future lesson. ↩︎