Skip to main content

Outputs

·1541 words·8 mins
Terraform - This article is part of a series.
Part 7: This Article

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.

pRreospoeurrtpcyreopReerstoyuprTrceoerprearftoOyrumtOpuCutpotprnOuofutpitegpruutrtyationOutputTVhaeluOeutVsaildueeWoVralldueValueUser

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:

PartContent
8Read, 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 or false, default is false) that specifies if this is a sensitive value, like a password. If true 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 configuration
    • terraform 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

  1. 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. ↩︎

Mattias Fjellström
Author
Mattias Fjellström
Cloud architect consultant and an HashiCorp Ambassador
Terraform - This article is part of a series.
Part 7: This Article