What is a Terraform Plan?

Terraform is a popular tool for managing infrastructure as code. It allows you to define and provision your resources in a declarative way, using a simple and human-readable language. But before you apply your configuration to create or update your infrastructure, you need to plan it. That's where Terraform Plan comes in and the topic of this blog post.

Video Walk-through

Our setup

Let’s take a look at our setup.

Requirements

  • A GitHub account (all the hands-on sections will utilize GitHub’s Codespaces so you won’t need to install anything on your machine)

Repository

TL;DR: You can find the repo here.

The Terraform Plan Command

Before jumping into the code example, let's understand some of the concepts around the Terraform Plan command.

Terraform Plan is a command that shows you what Terraform will do before it actually does it. It compares your current state (the existing resources) with your desired state (the configuration file) and generates a plan of action to achieve it. The plan shows you which resources will be created, modified, or destroyed, and how their attributes will change.

Purpose of Terraform Plan

Terraform Plan aims to help you review and verify your configuration before applying it. It gives you a chance to catch any errors or inconsistencies in your terraform code, and to make sure that you are not making any unwanted changes to your infrastructure. It also helps you communicate and collaborate with your team members, by showing them what you intend to do and getting their feedback.

Why is Terraform Plan important?

Planning your infrastructure changes is very important. Let’s first talk about the challenges that you may encounter when you don’t plan then discuss the importance of running plans.

Challenges without planning

Without planning you may run into the following challenges:

  • Run the risk of applying changes that are not intended or expected, which can cause errors, downtime, or security issues.
  • May not be aware of the dependencies or conflicts between your resources, which can lead to failures or inconsistencies.
  • May not be able to estimate the cost or the impact of your changes, which can affect your budget or your performance.

The Importance of Terraform Plan

Planning helps you:

  • Avoid the challenges mentioned above by showing you exactly what Terraform will do and allowing you to review it before executing it.
  • Ensure that your configuration is valid and consistent with your desired state and follows the best practices and conventions of Terraform.
  • Optimize your resources and reduce waste by showing you what resources are no longer needed and can be removed.
  • Improve your collaboration and transparency by sharing your plan with your team members and stakeholders and getting their approval before applying it.

Terraform Plan in Action

To see how Terraform Plan works in action, let's look at a few use case examples based on pulling an NGINX docker image and creating a docker container with this image.

Use Case Examples

Creation of a new resource

The process of creating a new resource is simple and we will take it one step at a time.

  1. Write configuration files that define the resources and their attributes. Below you will see all the terraform files.
    Here is the main.tf file:
terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "3.0.2"
    }
  }
}

provider "docker" {}

resource "docker_image" "nginx_image" {
  name = "${var.image_name}:${var.image_tag}"
}

resource "docker_container" "nginx_container" {
  name  = "web-server"
  image = docker_image.nginx_image.image_id
  ports {
    internal = var.internal_port
    external = var.external_port
  }
  restart = "always"
}

Then we have a variables.tf file that defines the following variables:

  1. image_name
  2. image_tag
  3. internal_port
  4. external_port
variable "image_name" {
  type        = string
  description = "The name of the Docker image"
}

variable "image_tag" {
  type        = string
  description = "The tag of the Docker image"
}

variable "internal_port" {
  type        = number
  description = "The internal port number for the container"
  default     = 80
}

variable "external_port" {
  type        = number
  description = "The external port number for the container"
  default     = 8080
}

We then have a terraform.tfvars file to assign the variable values. There are many other ways to assign these variables such as using environment variables, but here we will define them in a file.

image_name    = "nginx"
image_tag     = "1.23.3"
internal_port = 80
external_port = 8090
  1. Run terraform init command to initialize the Terraform folder.
terraform init

The output will look like this:

terraform init output
  1. Run the terraform plan command to see the execution plan and what resources will be created and how they will be configured.
terraform plan

Here is the terraform plan output that shows the proposed changes:

terraform plan output
  1. Review the plan and confirm that it matches your expectations.
  2. Run the terraform apply command to create the resources
terraform apply

When you run terraform apply, a terraform plan is created automatically with a prompt at the end to approve the plan and apply the configuration changes. Below is the output prompt to accept the plan along with the execution of the plan.

terraform apply output

You can also view the newly created NGINX container by going to the ports tab and clicking the globe icon as shown below.

view external port

And you will get the famous NGINX welcome screen

nginx welcome screen

Modification of an existing resource

Now let's say you want to modify the container's restart behavior from always restarting to restarting on-failure. Just modify the docker_container resource in the main.tf file as shown below.

resource "docker_container" "nginx_container" {
  name  = "web-server"
  image = docker_image.nginx_image.image_id
  ports {
    internal = var.internal_port
    external = var.external_port
  }
  restart = "on-failure"
}

Now run the terraform command again to see what resource will be modified and how its attribute will change. You review the plan and confirm that it matches your expectations. You run Terraform Apply to update the resource.

terraform plan

Notice how the output below shows that an update in-place will occur. This means that Terraform will not destroy any resources, it will update the resource in place. This behavior is dependent on the type of resource and provider being used along with which attribute we're changing. In our case, changing the restart behavior can be done with no destruction.

update in-place change

To demonstrate a modification that will cause the container to be destroyed and created, let's modify the external port number to use. Change the external port in the terraform.tfvars file from 8080 to 8081.

Now run terraform plan again and observe the output below:

terraform plan with destroy and create replacement

You don't need to apply this change as we're just demonstrating what a destroy and then create replacement looks like. This is a very important change that you would need to pay attention to when running the terraform plan command.

Deletion of an existing resource

Let's say we want to now delete our container but would like to keep our docker image. To do this, we can easily update our main.tf file and comment out our docker_container resource as shown below:

# resource "docker_container" "nginx_container" {
#   name  = "web-server"
#   image = docker_image.nginx_image.image_id
#   ports {
#     internal = var.internal_port
#     external = var.external_port
#   }
#   restart = "on-failure"
# }

This is telling Terraform that we want to delete this resource. Now go ahead and run the plan again.

terraform plan

Here is the execution plan output showing that we will destroy the NGINX docker container:

destroy the container plan output

We are going to go ahead and run a terraform apply to delete the container, but it's very important to pay attention to resources that are planned for destruction.

terraform apply

You can verify that the docker container is removed by running docker ps to see that there are no containers running.

Planning modes

Terraform Plan has two modes: normal mode and refresh-only mode.

  1. Normal mode is the default mode that we've been using all along. It compares the current state with the desired state and generates a plan of action to achieve it. It also refreshes the state by querying the providers for any changes that may have occurred outside of Terraform.

Refresh-only mode is a special mode that only refreshes the state without generating a plan of action. It is useful when you want to update the state without making any changes to the infrastructure. It is also a faster operation since we're skipping the normal plan phase which could take some time in large configurations.Let's try this out. First we need to recreate our infrastructure. Run the following command and accept the prompt by typing ‘yes’

terraform apply

Now go ahead and delete our NGINX docker image using the following command:

docker rmi nginx:1.23.3

Now run the following terraform command:

terraform plan -refresh-only

and the output looks like this:

terraform plan refresh only output

This is showing you that changes occurred outside of Terraform. That is correct since we deleted the docker image outside of Terraform. Now since this is a terraform plan command, it will not refresh the state file. Take a look at your state file called terraform.tfstate. This is an example of what it would look like right now:

{
  "version": 4,
  "terraform_version": "1.4.6",
  "serial": 14,
  "lineage": "233d2971-7895-7cf7-a361-148f8ca4418d",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "docker_image",
      "name": "nginx_image",
      "provider": "provider[\"registry.terraform.io/kreuzwerker/docker\"]",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "build": [],
            "force_remove": null,
            "id": "sha256:ac232364af842735579e922641ae2f67d5b8ea97df33a207c5ea05f60c63a92dnginx:1.23.3",
            "image_id": "sha256:ac232364af842735579e922641ae2f67d5b8ea97df33a207c5ea05f60c63a92d",
            "keep_locally": null,
            "name": "nginx:1.23.3",
            "platform": null,
            "pull_triggers": null,
            "repo_digest": "nginx@sha256:f4e3b6489888647ce1834b601c6c06b9f8c03dee6e097e13ed3e28c01ea3ac8c",
            "triggers": null
          },
          "sensitive_attributes": [],
          "private": "bnVsbA=="
        }
      ]
    }
  ],
  "check_results": []
}

Notice how we still have our docker image. Now let's run the following command to refresh this state file and remove the docker image from it.

terraform apply -refresh-only

answer 'yes' to the prompt and observe the state file now:

{
  "version": 4,
  "terraform_version": "1.4.6",
  "serial": 15,
  "lineage": "233d2971-7895-7cf7-a361-148f8ca4418d",
  "outputs": {},
  "resources": [],
  "check_results": []
}

We've successfully removed the docker image from our state file. Using the -refresh-only flag with the terraform plan and terraform apply commands is a much safer option than the older method of using the terraform refresh command.

Planning options

Terraform Plan has several options that allow you to customize how it works. In this section, we will discuss the most popular ones, you can get a list of all the options using the terraform plan -h command.

  1. The -out flag. This flag allows you to save the output of Terraform plan to a file, which you can then use as an input for Terraform apply. This ensures that Terraform apply will execute exactly the same actions as Terraform plan, without any possibility of drift or interference. This is especially useful if you have a long-running or complex plan that you want to review carefully before applying, or if you want to automate your Terraform workflow with scripts or CI/CD tools. Try this:
terraform plan -out tfplan

Notice the output:

terraform plan with out flag

And now you run terraform apply while referencing this tfplan file as shown:

terraform apply tfplan
  1. The -target flag. This flag allows you to specify one or more resources that you want Terraform to focus on when planning and applying. This can be useful if you want to test or debug a specific resource without affecting the rest of your infrastructure, or if you want to make a quick change without waiting for a full plan and apply. However, be careful when using this option, as it may cause dependencies or side effects that are not captured by the plan. We will discuss this flag in more detail later under the Resource Targeting section.
  2. The -replace flag. This flag forces the replacement of a particular resource instance using its resource address. If the plan would've normally produced an update or no-op action for this instance, Terraform will plan to replace it instead. You can use this option multiple times to replace more than one object. In the past, Terraform used the terraform taint command to do the same, but it's now recommended to use the -replace flag with terraform plan and terraform apply. Let's give it a try.
terraform plan -replace docker_container.nginx_container

This will effectively replace our docker NGINX container. So it will destroy and create. Here is the summary output of this command:

using the replace flag with terraform plan

To go ahead and apply the change, you will need to run:

terraform apply -replace docker_container.nginx_container
  1. The -var and -var-file flags. These flags allow you to pass variables to Terraform plan, either as individual key-value pairs or as files containing multiple variables. Variables are useful for parameterizing your Terraform configuration and making it more reusable and flexible. So far, we've been using the variables.tf and the terraform.tfvars files to declare and assign variable values, respectively. Now comment out everything in the terraform.tfvars file to like this:
# image_name    = "nginx"
# image_tag     = "1.23.3"
# internal_port = 80
# external_port = 8081

Now if you run terraform plan, you will be prompted to supply values for both the image_name and the image_tag since they don't have default values specified in the variables.tf file as shown below:

prompt for variable values

To eliminate the need to answer these prompts, we can use the -var flag to provide the needed variables via the command line like this:

terraform plan -var image_name=nginx -var image_tag=1.23.3

This should work fine and you should get a No changes to your infrastructure message.We could also use a variables file that perhaps is located in a different directory than our current one. Remember that Terraform looks for all its files in the current directory you're running Terraform from. Now check the folder called a_random_directory and the file called my_variables_file.txt. Notice how we gave this file the .txt extension to show you that we can include non-terraform-specific files to the terraform plan command. You'll see that we simply defined the two needed variables there:

image_name    = "nginx"
image_tag     = "1.23.3"

Now let's run terraform plan again:

terraform plan -var-file a_random_directory/my_variables_file.txt

This should work fine and you should get a No changes to your infrastructure message.

  1. The -destroy flag. This creates a plan to destroy all objects currently managed by this Terraform configuration. It's similar to running terraform destroy, however, it's useful when used in CI/CD pipelines. Now uncomment the variables in the terraform.tfvars file and run the following execution plan command:
terraform plan -destroy
terraform plan with destroy flag

To apply the changes, go ahead and run the following command:

terraform apply -destroy

Resource Targeting

Resource targeting in Terraform is a feature that allows you to apply changes to only a subset of your infrastructure, instead of applying the entire plan at once. This can be useful for troubleshooting errors, recovering from network failures, or testing out new configurations without affecting the rest of your resources.

We already mentioned how to target certain resources using the -target flag when you run terraform plan or terraform apply. You can provide one or more target options that contain references to resources, modules, or collections of resources in your configuration. For example:

terraform plan -target aws_instance.web -target module.network

This command will generate a plan that only includes the aws_instance.web resource and all the resources in the module.network module. Terraform will ignore any other resources or modules that are not explicitly targeted.

Resource targeting can be a powerful tool for managing your infrastructure, but it should not be part of your regular workflow. It can cause inconsistencies in your state file and lead to unexpected dependencies or conflicts between resources. It is recommended to use resource targeting only as a last resort when you need to fix a specific problem or test a specific change. For most cases, you should apply your entire plan as defined by your configuration files.

Let's give it a try with our docker example.

First, let's rebuild our infrastructure:

terraform apply

type 'yes' for the prompt.

Now let's change the image_tag from "1.23.3" to "1.23.1" in the terraform.tfvars file.

Run terraform plan again and notice that both the docker_container.nginx_container and the docker_image.nginx_image resources are planned to be destroyed and recreated.

terraform plan replacing container and image

To target only the docker image to be recreated let's use our -target flag:

terraform plan -target docker_image.nginx_image

Notice now that only the image will be updated. Also, notice the warning message about resource targeting being in effect.

terraform plan target image

You can go ahead and apply the changes with the following command:

terraform apply -target docker_image.nginx_image

Now notice the error message when we try to apply:

error message when using targeting

This shows the dependency that this container has on the current image tag. That's why the resource targeting capability needs to be used with care.

Best Practices

To make the most of Terraform Plan and Terraform in general, here are some best practices to follow:

The Practice Details
Plan before Apply Always run Terraform Plan before Terraform Apply, and review the plan carefully
Use Visualization Software This helps you understand the proposed infrastructure changes.
Examples:
env0’s pretty plan
Rover
Use a Version Control System (VCS) This helps you manage your configuration files and commit your changes before running terraform plan
Always run speculative Terraform plans Execute Terraform Plan within your version control on every relevant Pull Request, to better review your changes before merging the code. env0 can help you with automatic https://docs.env0.com/docs/plan-on-pull-request
Terraform plan to output file Use the terraform plan command to output to a file and include that file in your CI/CD pipeline and as part of the peer review process. Only use this file with terraform apply command after it has been reviewed.
Use Terraform Automation and COllaboration Software (TACOS) Tools such as env0 help store your state file remotely and securely, in addition to advanced features such as remote execution, collaboration, and governance.

Conclusion

Terraform Plan is a powerful and useful command that lets you preview the changes that Terraform will make to your infrastructure before applying them. It helps you validate your configuration, avoid errors and unwanted changes, and collaborate with your team. In this blog post, we learned about the purpose and importance of Terraform Plan, and how to use it in different scenarios. We also saw how to save and apply a plan file, and how to use some of the options and flags that Terraform Plan supports. By using Terraform Plan, you can make your infrastructure as code more reliable, predictable, and transparent.

What is a Terraform Plan?

Terraform is a popular tool for managing infrastructure as code. It allows you to define and provision your resources in a declarative way, using a simple and human-readable language. But before you apply your configuration to create or update your infrastructure, you need to plan it. That's where Terraform Plan comes in and the topic of this blog post.

Video Walk-through

Our setup

Let’s take a look at our setup.

Requirements

  • A GitHub account (all the hands-on sections will utilize GitHub’s Codespaces so you won’t need to install anything on your machine)

Repository

TL;DR: You can find the repo here.

The Terraform Plan Command

Before jumping into the code example, let's understand some of the concepts around the Terraform Plan command.

Terraform Plan is a command that shows you what Terraform will do before it actually does it. It compares your current state (the existing resources) with your desired state (the configuration file) and generates a plan of action to achieve it. The plan shows you which resources will be created, modified, or destroyed, and how their attributes will change.

Purpose of Terraform Plan

Terraform Plan aims to help you review and verify your configuration before applying it. It gives you a chance to catch any errors or inconsistencies in your terraform code, and to make sure that you are not making any unwanted changes to your infrastructure. It also helps you communicate and collaborate with your team members, by showing them what you intend to do and getting their feedback.

Why is Terraform Plan important?

Planning your infrastructure changes is very important. Let’s first talk about the challenges that you may encounter when you don’t plan then discuss the importance of running plans.

Challenges without planning

Without planning you may run into the following challenges:

  • Run the risk of applying changes that are not intended or expected, which can cause errors, downtime, or security issues.
  • May not be aware of the dependencies or conflicts between your resources, which can lead to failures or inconsistencies.
  • May not be able to estimate the cost or the impact of your changes, which can affect your budget or your performance.

The Importance of Terraform Plan

Planning helps you:

  • Avoid the challenges mentioned above by showing you exactly what Terraform will do and allowing you to review it before executing it.
  • Ensure that your configuration is valid and consistent with your desired state and follows the best practices and conventions of Terraform.
  • Optimize your resources and reduce waste by showing you what resources are no longer needed and can be removed.
  • Improve your collaboration and transparency by sharing your plan with your team members and stakeholders and getting their approval before applying it.

Terraform Plan in Action

To see how Terraform Plan works in action, let's look at a few use case examples based on pulling an NGINX docker image and creating a docker container with this image.

Use Case Examples

Creation of a new resource

The process of creating a new resource is simple and we will take it one step at a time.

  1. Write configuration files that define the resources and their attributes. Below you will see all the terraform files.
    Here is the main.tf file:
terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "3.0.2"
    }
  }
}

provider "docker" {}

resource "docker_image" "nginx_image" {
  name = "${var.image_name}:${var.image_tag}"
}

resource "docker_container" "nginx_container" {
  name  = "web-server"
  image = docker_image.nginx_image.image_id
  ports {
    internal = var.internal_port
    external = var.external_port
  }
  restart = "always"
}

Then we have a variables.tf file that defines the following variables:

  1. image_name
  2. image_tag
  3. internal_port
  4. external_port
variable "image_name" {
  type        = string
  description = "The name of the Docker image"
}

variable "image_tag" {
  type        = string
  description = "The tag of the Docker image"
}

variable "internal_port" {
  type        = number
  description = "The internal port number for the container"
  default     = 80
}

variable "external_port" {
  type        = number
  description = "The external port number for the container"
  default     = 8080
}

We then have a terraform.tfvars file to assign the variable values. There are many other ways to assign these variables such as using environment variables, but here we will define them in a file.

image_name    = "nginx"
image_tag     = "1.23.3"
internal_port = 80
external_port = 8090
  1. Run terraform init command to initialize the Terraform folder.
terraform init

The output will look like this:

terraform init output
  1. Run the terraform plan command to see the execution plan and what resources will be created and how they will be configured.
terraform plan

Here is the terraform plan output that shows the proposed changes:

terraform plan output
  1. Review the plan and confirm that it matches your expectations.
  2. Run the terraform apply command to create the resources
terraform apply

When you run terraform apply, a terraform plan is created automatically with a prompt at the end to approve the plan and apply the configuration changes. Below is the output prompt to accept the plan along with the execution of the plan.

terraform apply output

You can also view the newly created NGINX container by going to the ports tab and clicking the globe icon as shown below.

view external port

And you will get the famous NGINX welcome screen

nginx welcome screen

Modification of an existing resource

Now let's say you want to modify the container's restart behavior from always restarting to restarting on-failure. Just modify the docker_container resource in the main.tf file as shown below.

resource "docker_container" "nginx_container" {
  name  = "web-server"
  image = docker_image.nginx_image.image_id
  ports {
    internal = var.internal_port
    external = var.external_port
  }
  restart = "on-failure"
}

Now run the terraform command again to see what resource will be modified and how its attribute will change. You review the plan and confirm that it matches your expectations. You run Terraform Apply to update the resource.

terraform plan

Notice how the output below shows that an update in-place will occur. This means that Terraform will not destroy any resources, it will update the resource in place. This behavior is dependent on the type of resource and provider being used along with which attribute we're changing. In our case, changing the restart behavior can be done with no destruction.

update in-place change

To demonstrate a modification that will cause the container to be destroyed and created, let's modify the external port number to use. Change the external port in the terraform.tfvars file from 8080 to 8081.

Now run terraform plan again and observe the output below:

terraform plan with destroy and create replacement

You don't need to apply this change as we're just demonstrating what a destroy and then create replacement looks like. This is a very important change that you would need to pay attention to when running the terraform plan command.

Deletion of an existing resource

Let's say we want to now delete our container but would like to keep our docker image. To do this, we can easily update our main.tf file and comment out our docker_container resource as shown below:

# resource "docker_container" "nginx_container" {
#   name  = "web-server"
#   image = docker_image.nginx_image.image_id
#   ports {
#     internal = var.internal_port
#     external = var.external_port
#   }
#   restart = "on-failure"
# }

This is telling Terraform that we want to delete this resource. Now go ahead and run the plan again.

terraform plan

Here is the execution plan output showing that we will destroy the NGINX docker container:

destroy the container plan output

We are going to go ahead and run a terraform apply to delete the container, but it's very important to pay attention to resources that are planned for destruction.

terraform apply

You can verify that the docker container is removed by running docker ps to see that there are no containers running.

Planning modes

Terraform Plan has two modes: normal mode and refresh-only mode.

  1. Normal mode is the default mode that we've been using all along. It compares the current state with the desired state and generates a plan of action to achieve it. It also refreshes the state by querying the providers for any changes that may have occurred outside of Terraform.

Refresh-only mode is a special mode that only refreshes the state without generating a plan of action. It is useful when you want to update the state without making any changes to the infrastructure. It is also a faster operation since we're skipping the normal plan phase which could take some time in large configurations.Let's try this out. First we need to recreate our infrastructure. Run the following command and accept the prompt by typing ‘yes’

terraform apply

Now go ahead and delete our NGINX docker image using the following command:

docker rmi nginx:1.23.3

Now run the following terraform command:

terraform plan -refresh-only

and the output looks like this:

terraform plan refresh only output

This is showing you that changes occurred outside of Terraform. That is correct since we deleted the docker image outside of Terraform. Now since this is a terraform plan command, it will not refresh the state file. Take a look at your state file called terraform.tfstate. This is an example of what it would look like right now:

{
  "version": 4,
  "terraform_version": "1.4.6",
  "serial": 14,
  "lineage": "233d2971-7895-7cf7-a361-148f8ca4418d",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "docker_image",
      "name": "nginx_image",
      "provider": "provider[\"registry.terraform.io/kreuzwerker/docker\"]",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "build": [],
            "force_remove": null,
            "id": "sha256:ac232364af842735579e922641ae2f67d5b8ea97df33a207c5ea05f60c63a92dnginx:1.23.3",
            "image_id": "sha256:ac232364af842735579e922641ae2f67d5b8ea97df33a207c5ea05f60c63a92d",
            "keep_locally": null,
            "name": "nginx:1.23.3",
            "platform": null,
            "pull_triggers": null,
            "repo_digest": "nginx@sha256:f4e3b6489888647ce1834b601c6c06b9f8c03dee6e097e13ed3e28c01ea3ac8c",
            "triggers": null
          },
          "sensitive_attributes": [],
          "private": "bnVsbA=="
        }
      ]
    }
  ],
  "check_results": []
}

Notice how we still have our docker image. Now let's run the following command to refresh this state file and remove the docker image from it.

terraform apply -refresh-only

answer 'yes' to the prompt and observe the state file now:

{
  "version": 4,
  "terraform_version": "1.4.6",
  "serial": 15,
  "lineage": "233d2971-7895-7cf7-a361-148f8ca4418d",
  "outputs": {},
  "resources": [],
  "check_results": []
}

We've successfully removed the docker image from our state file. Using the -refresh-only flag with the terraform plan and terraform apply commands is a much safer option than the older method of using the terraform refresh command.

Planning options

Terraform Plan has several options that allow you to customize how it works. In this section, we will discuss the most popular ones, you can get a list of all the options using the terraform plan -h command.

  1. The -out flag. This flag allows you to save the output of Terraform plan to a file, which you can then use as an input for Terraform apply. This ensures that Terraform apply will execute exactly the same actions as Terraform plan, without any possibility of drift or interference. This is especially useful if you have a long-running or complex plan that you want to review carefully before applying, or if you want to automate your Terraform workflow with scripts or CI/CD tools. Try this:
terraform plan -out tfplan

Notice the output:

terraform plan with out flag

And now you run terraform apply while referencing this tfplan file as shown:

terraform apply tfplan
  1. The -target flag. This flag allows you to specify one or more resources that you want Terraform to focus on when planning and applying. This can be useful if you want to test or debug a specific resource without affecting the rest of your infrastructure, or if you want to make a quick change without waiting for a full plan and apply. However, be careful when using this option, as it may cause dependencies or side effects that are not captured by the plan. We will discuss this flag in more detail later under the Resource Targeting section.
  2. The -replace flag. This flag forces the replacement of a particular resource instance using its resource address. If the plan would've normally produced an update or no-op action for this instance, Terraform will plan to replace it instead. You can use this option multiple times to replace more than one object. In the past, Terraform used the terraform taint command to do the same, but it's now recommended to use the -replace flag with terraform plan and terraform apply. Let's give it a try.
terraform plan -replace docker_container.nginx_container

This will effectively replace our docker NGINX container. So it will destroy and create. Here is the summary output of this command:

using the replace flag with terraform plan

To go ahead and apply the change, you will need to run:

terraform apply -replace docker_container.nginx_container
  1. The -var and -var-file flags. These flags allow you to pass variables to Terraform plan, either as individual key-value pairs or as files containing multiple variables. Variables are useful for parameterizing your Terraform configuration and making it more reusable and flexible. So far, we've been using the variables.tf and the terraform.tfvars files to declare and assign variable values, respectively. Now comment out everything in the terraform.tfvars file to like this:
# image_name    = "nginx"
# image_tag     = "1.23.3"
# internal_port = 80
# external_port = 8081

Now if you run terraform plan, you will be prompted to supply values for both the image_name and the image_tag since they don't have default values specified in the variables.tf file as shown below:

prompt for variable values

To eliminate the need to answer these prompts, we can use the -var flag to provide the needed variables via the command line like this:

terraform plan -var image_name=nginx -var image_tag=1.23.3

This should work fine and you should get a No changes to your infrastructure message.We could also use a variables file that perhaps is located in a different directory than our current one. Remember that Terraform looks for all its files in the current directory you're running Terraform from. Now check the folder called a_random_directory and the file called my_variables_file.txt. Notice how we gave this file the .txt extension to show you that we can include non-terraform-specific files to the terraform plan command. You'll see that we simply defined the two needed variables there:

image_name    = "nginx"
image_tag     = "1.23.3"

Now let's run terraform plan again:

terraform plan -var-file a_random_directory/my_variables_file.txt

This should work fine and you should get a No changes to your infrastructure message.

  1. The -destroy flag. This creates a plan to destroy all objects currently managed by this Terraform configuration. It's similar to running terraform destroy, however, it's useful when used in CI/CD pipelines. Now uncomment the variables in the terraform.tfvars file and run the following execution plan command:
terraform plan -destroy
terraform plan with destroy flag

To apply the changes, go ahead and run the following command:

terraform apply -destroy

Resource Targeting

Resource targeting in Terraform is a feature that allows you to apply changes to only a subset of your infrastructure, instead of applying the entire plan at once. This can be useful for troubleshooting errors, recovering from network failures, or testing out new configurations without affecting the rest of your resources.

We already mentioned how to target certain resources using the -target flag when you run terraform plan or terraform apply. You can provide one or more target options that contain references to resources, modules, or collections of resources in your configuration. For example:

terraform plan -target aws_instance.web -target module.network

This command will generate a plan that only includes the aws_instance.web resource and all the resources in the module.network module. Terraform will ignore any other resources or modules that are not explicitly targeted.

Resource targeting can be a powerful tool for managing your infrastructure, but it should not be part of your regular workflow. It can cause inconsistencies in your state file and lead to unexpected dependencies or conflicts between resources. It is recommended to use resource targeting only as a last resort when you need to fix a specific problem or test a specific change. For most cases, you should apply your entire plan as defined by your configuration files.

Let's give it a try with our docker example.

First, let's rebuild our infrastructure:

terraform apply

type 'yes' for the prompt.

Now let's change the image_tag from "1.23.3" to "1.23.1" in the terraform.tfvars file.

Run terraform plan again and notice that both the docker_container.nginx_container and the docker_image.nginx_image resources are planned to be destroyed and recreated.

terraform plan replacing container and image

To target only the docker image to be recreated let's use our -target flag:

terraform plan -target docker_image.nginx_image

Notice now that only the image will be updated. Also, notice the warning message about resource targeting being in effect.

terraform plan target image

You can go ahead and apply the changes with the following command:

terraform apply -target docker_image.nginx_image

Now notice the error message when we try to apply:

error message when using targeting

This shows the dependency that this container has on the current image tag. That's why the resource targeting capability needs to be used with care.

Best Practices

To make the most of Terraform Plan and Terraform in general, here are some best practices to follow:

The Practice Details
Plan before Apply Always run Terraform Plan before Terraform Apply, and review the plan carefully
Use Visualization Software This helps you understand the proposed infrastructure changes.
Examples:
env0’s pretty plan
Rover
Use a Version Control System (VCS) This helps you manage your configuration files and commit your changes before running terraform plan
Always run speculative Terraform plans Execute Terraform Plan within your version control on every relevant Pull Request, to better review your changes before merging the code. env0 can help you with automatic https://docs.env0.com/docs/plan-on-pull-request
Terraform plan to output file Use the terraform plan command to output to a file and include that file in your CI/CD pipeline and as part of the peer review process. Only use this file with terraform apply command after it has been reviewed.
Use Terraform Automation and COllaboration Software (TACOS) Tools such as env0 help store your state file remotely and securely, in addition to advanced features such as remote execution, collaboration, and governance.

Conclusion

Terraform Plan is a powerful and useful command that lets you preview the changes that Terraform will make to your infrastructure before applying them. It helps you validate your configuration, avoid errors and unwanted changes, and collaborate with your team. In this blog post, we learned about the purpose and importance of Terraform Plan, and how to use it in different scenarios. We also saw how to save and apply a plan file, and how to use some of the options and flags that Terraform Plan supports. By using Terraform Plan, you can make your infrastructure as code more reliable, predictable, and transparent.

Logo Podcast
With special guest
Andrew Brown

Schedule a technical demo. See env0 in action.

CTA Illustration