Terraform Certification (Part 7): Outputs
Tags: hashicorp, terraform
Published: 2023-04-02 10:00:00

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.

![terraform outputs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9kceney7c89rqpo5nn2c.png)

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 modules[^modules], or when using the `terraform output` command.

The general format of the `output` block is this:

```hcl
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](https://developer.hashicorp.com/terraform/language/values/outputs#depends_on-explicit-output-dependencies) 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](https://developer.hashicorp.com/terraform/language/values/outputs#custom-condition-checks).

An output containing a static string looks like this:

```hcl
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:

```hcl
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]:

```hcl
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:

```hcl
// 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`: 

```console
$ 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):

```console
$ 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:

```console
$ terraform output

my_lucky_number = 639
```

I can also specify the name of an output with `terraform output`:

```console
$ 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:

```hcl
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:

```console
$ 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):

```console
$ 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

[^modules]: 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.