Useful Terraform Tools

Share This Post

Share on facebook
Share on linkedin
Share on twitter
Share on reddit

Introduction

Terraform has certainly come a long way since its humble beginnings in 2014. Coming from a modest little infrastructure tool to the powerhouse it is today. Unfortunately, one of the issues with Terraform is how difficult troubleshooting problems can be. Due to the fact that it’s an abstraction over the APIs for the infrastructure it controls, it can be difficult to get to the bottom of why an issue is occurring. Along with your standard CLI commands you probably use frequently such as terraform validate, terraform plan, and terraform apply, Terraform has a lot of great CLI tools built-in to help troubleshoot your issues, get to the bottom of your errors, and clean up your code!

Terraform Fmt

One simple, but great, tool that Terraform has to help kick off your troubleshooting steps is terraform fmt. Although not necessarily a troubleshooting tool in itself, its ability to automatically clean up your code can make finding those pesky missing braces and other nested resource issues quickly and efficiently. Let’s take a look at how it works.

Let’s say you have some Terraform code, and feel free to follow along here as I’ll be utilizing the same examples throughout the article (you just need Docker installed), that looks like this:

terraform {
  required_providers {
    docker = {
      source = "kreuzwerker/docker"
    }
  }
}

provider "docker" {}

resource "docker_image" "container_image" {
  name = "grafana/grafana"
        provisioner "local-exec" {
    command = "echo ${self.latest} > imageid.txt"
}
}

That looks pretty rough, doesn’t it? There are bad indentations, braces are out of line, etc. Well, it doesn’t have to look so bad! If you’re following along, create a new working directory, run terraform init within the directory, and add this code “as-is” to your main.tf and run a terraform fmt. Boom, see how much cleaner that is? That would make things significantly easier to troubleshoot! Also, if you have nested directories for modules and whatnot, you can run:

terraform fmt -recursive

to fix all files within. Cool, huh? Anyway, let’s move on to something more interesting!

Terraform Output

terraform output is another overlooked tool that can be very useful. terraform output simply redisplays the outputs you’ve configured in the CLI, but it does get a little more interesting than that. By using the –json flag and a json parsing utility called “jq”, you can filter the outputs to get exactly what you need. This is also what’s required if you need to see sensitive output values. Let’s take a look!

Go ahead and add an output to the main.tf file:

output "image_id" {
  value     = {(docker_image.container_image.name) = (docker_image.container_image.latest)}
}

Save the file and run another great command:

terraform refresh

Once you’ve done that, you can see that it respects the new output. Now run:

 terraform output 

and you can once again see the output as you did before. Now, let’s say we want to access just the value and strip the rest of the metadata about the output. First, install jq with:

 sudo apt install jq 

(If on Debian/Ubuntu, otherwise, check https://stedolan.github.io/jq/download/ for other installation methods.)

Once jq is installed, run this command to see just the output:

terraform output -json | jq '."image_id"."value"'

As you can see, this shows just the value of the output which is a key:value pair. Let’s access the just the image_id using jq:

terraform output -json | jq '."image_id"."value"."grafana/grafana"'

Nice! So, as you can see, we now just have the value displayed. Although it’s a little trivial for such a small deployment, this technique can be very useful for large deployments. Jq can also be used to access sensitive outputs if you were to add sensitive = true to the output.

Terraform Show

terraform show is a great tool to help you stay out of the state file. Since modifying state manually is not something that should ever be done, having other utilities at your disposal to view the state is critical to managing complicated deployments. terraform show can also be utilized with jq to parse the state and find the information you need. Let’s take a look!

First, let’s take a look at what terraform show returns. Run terraform show and take a look at the output. Now, append -json to the command and pipe it through jq like this:

terraform show -json | jq
{
  "format_version": "0.1",
  "terraform_version": "0.14.8",
  "values": {
...
    "root_module": {
      "resources": [
        
...
          "values": {
...
            "latest": "sha256:c9e576dccd689553c5fadc84d30bcc2502d3367497c96d19f2a081faf313ff99",
            "name": "grafana/grafana",
            "output": null,
            "pull_trigger": null,
            "pull_triggers": null
        }
      ]
    }
  }
}

So now we can use jq to parse (The output has been truncated as it’s pretty long.) Let’s get that id once again like so:

terraform show -json | jq '.values.root_module.resources[0].values.latest'

Perfect! Now we can see the id once again!

Terminal output from: terraform show -json | jq '.values.root_module.resources[0].values.latest'
terraform show output

Terraform State

The terraform state command sounds a lot like terraform show, but the key difference is the terraform state command requires extra arguments to manage and see individual components of your state rather than just displaying the entire state file. So let’s add some another resource here before we continue. Let’s create a container from our image so we can see two different resources instead of just one:

resource "docker_container" "app_container" {
    name = "grafana"
    image = docker_image.container_image.latest
}

This will create a container from our Docker image. So now that we’ve done this, let’s take a quick look at two of the important Terraform state commands:

<strong>terraform state list</strong> is a great tool for quickly seeing what resources you have deployed and how to access them. For instance, if you have to reference a resource in an output, you can use terraform state list to quickly see the paths of your resources and how to get to them. Let’s take a look:

terraform state list
terraform state list

As you can see, you can see the path to the image resource and the container resource we’ve created. We can then use this in the next command.

<strong>terraform state show</strong> <strong><resource name></strong> will allow you to see information just about that resource. So if we were to use the resource path from terraform state list and then run

terraform state show docker_image.container_image

we can see the specific attributes of that resource without having to filter out all other resources.

terraform state show

This can be very helpful in getting just the information you need faster, especially when dealing with a very large state. Unfortunately, terraform state show will not output to json, so grep can be a great bet to get the information you need.

terraform state show docker_image.container_image | grep latest
terraform state show

Terraform Graph

Now things start to get fun! Let’s say you have a “cycle error”. A cycle error is when resource A has a dependency on resource B and resource B has a dependency on resource A. Obviously, this would cause neither to be able to be created since they are dependent on each other. Similar to a “chicken and egg” scenario. This is typically pretty easy to find in smaller deployments, but can get very difficult in larger ones. That’s where the terraform graph command comes into play! terraform graph is somewhat useful as a CLI tool, but it’s extremely useful as a visual tool. Let’s take a look.

First, let’s create a cycle that we can diagnose. We’ll do something very simple and just introduce a cycle manually by forcing the docker_image resource to depend on the docker_container resource. Obviously, this is an issue because the container needs the image to be created, so if the image requires the container, it will not be able to be deployed:

resource "docker_image" "container_image" {
  name = "grafana/grafana"
  provisioner "local-exec" {
    command = "echo ${self.latest} > imageid.txt"
  }
  depends_on = [docker_container.app_container]
}

Now, run terraform plan and let’s see what happens:

As you can see, there’s a nasty cycle error, so let’s use terraform graph to see how we would diagnose this. Again, pretty easy with a deployment this small, but if you’re deploying 10s to 100s of resources, this can become quite a pain!

First, install Graphviz:

sudo apt install graphviz

If you are using something other than Ubuntu or Debian, the docs can be found here: https://graphviz.org/download/

Then run:

terraform graph -draw-cycles | dot -Tpdf > graph.pdf

Once you have done that, go ahead and open the graph.pdf file that is created in the directory you’re in:

Cool! Although the image is formatted a little “wonky” (something reps at Hashicorp have also lamented), it’s still a very useful tool. As you can see, the cycle is clearly visible as are all other dependencies, which helps us resolve the cycle as well as visualize any dependencies that could cause issues in the future. So go ahead and remove that manual dependency from the image resource and run a terraform apply.

Terraform Console


terraform console

Once you’ve done that, you should have just a > indicating that you are in the console. Now, we’ll start off by just viewing all of the information about the image. Do that by typing:

docker_image.container_image
Terraform Console

Once you’ve done that, we’ll want to access just the name. Do that with:

docker_image.container_image.name 
docker_image name

As you can see, it returns grafana/grafana. We want just the second portion of that, even though they’re exactly the same in this case, that isn’t always true. So, to name our container “grafana” automatically, we’ll use split and run this in the console to test it out:

split("/", docker_image.container_image.name)[1]
Terraform Split function

Perfect! This gets us exactly what we’re looking for! So let’s add that for the name argument in the container resource:

resource "docker_container" "app_container" {
    name = split("/", docker_image.container_image.name)[1]

    image = docker_image.container_image.latest
    
}

As you can see, terraform console is very powerful when experimenting with new functions, trying to access information, and more. Unfortunately, its functionality with modules can be a little quirky, but it’s still a really great resource, nonetheless.

Conclusion

As you can see, there are some great tools at your disposal to help you spend less time in Stackoverflow and more time in Terraform! If you’d like to dive deeper, sign up for my More than Certified in Terraform course and you’ll have over 12 hours of Terraform deployments to dive into and perfect your troubleshooting skills! So that’s all for this article, don’t forget to Terraform Apply Yourself!

Resources

Terraform Installation

Docker Installation

Terraform fmt Docs

Terraform Ouput Docs

Terraform Show Docs

Terraform State List Docs

Terraform State Show Docs

Terraform Graph Docs

Terraform Console Docs

https://graphviz.org/

My More than Certified in Terraform Course

Final Code

Below is the final code for the deployment. I have commented out the artificial dependency we created and also added ports in order for you to access the Grafana container and play with it!

terraform {
  required_providers {
    docker = {
      source = "kreuzwerker/docker"
    }
  }
}

provider "docker" {}

resource "docker_image" "container_image" {
  name = "grafana/grafana"
  provisioner "local-exec" {
    command = "echo ${self.latest} > imageid.txt"
  }
  #depends_on = [docker_container.app_container]
}

resource "docker_container" "app_container" {
  name  = split("/", docker_image.container_image.name)[1]
  image = docker_image.container_image.latest
  ports {
    internal = 3000
    external = 3000
  }
}

output "image_id" {
  value = { (docker_image.container_image.name) = (docker_image.container_image.latest) }
}

Be the First to Learn!

Get exclusive access to new courses, educational labs, deployment strategies, interviews and more with the MTC Course Preview Program!

More To Explore

Uncategorized

5 Qs for a Manager

Scott Mabe: Engineering and Development Manager/ Punk Music Connoisseur Hi Scott! Thanks for taking these questions. What’s your story? Scott: My name is Scott Mabe.

Uncategorized

More Consistent Terraform Runs with Docker

For more great Terraform content, check out my course! Introduction Like most software, one of the most aggravating things about Terraform is its versioning. It