Till now we have dealt quite a lot with Terraform CLI directly and indirectly in our previous posts. But Terraform CLI was never the focus of those introductory discussions. If you missed the introduction, please feel free to read the same here.
The introductory post also contains information about the workflow (
init - plan - apply - destroy). In all the examples till now we have used this workflow from CLI. It is safe to say we have used a CLI based workflow. In this post, we take a moment to understand the significance of Terraform CLI.
By now it should already be clear that the CLI interface for Terraform is
terraform. Every command related to Terraform CLI starts with
A Terraform project is essentially a set of
.tf files. All the IaC should be written into these files and saved in a particular directory. This forms the root directory of any Terraform project. It can also contain sub-directories. Terraform automatically interprets these configuration files as part of the project. However, there are other files and sub-directories which are created by Terraform to maintain states and downloaded plugins.
Terraform never works directly with configuration files (
.tf). To successfully apply the configuration Terraform works with plugins which it needs to download before
apply can happen. This is where an initialization command (below) needs to be executed into the same directory where configuration files are placed.
This command should be run every time a new provider is introduced in the configuration. By running this command, Terraform identifies the providers required by the configuration along with their versions and downloads the appropriate plugin from the repository. These plugins are downloaded in a directory .terraform created by Terraform in the same root directory.
Note: Remember to specify
.terraform directory into
.gitignore file to avoid unnecessary transportation of modules.
There is no harm in reinitializing the repository every time. By doing this it makes sure all the required plugins are downloaded and available for use. It does not start a new download for the same.
Some of the most important and most used Terraform CLI commands are
destroy which manage the planning, creation, modification, and deletion of cloud infrastructure.
Once the written configuration is ready (in case of an update or create) to be deployed — and the root directory initialized, the next action is to run
terraform plan command. Running
terraform plan into the root directory of Terraform project evaluates and validates the configuration provided in configuration files. It makes sure the correct syntax is used, appropriate plugins are installed, the state is not corrupted, checks the actual deployment and finds differences, lists out dependencies, etc.
Simply navigate to the root directory and run the below command. If successful, it would lay down the plan listing all the target resources which will be created or updated. In the end, it would beautifully tell us how many resources are planned for creation, modification, and deletion.
Once the configuration is validated successfully using
terraform plan, it is time to put that plan into action. This is done by running the below command:
Terraform works on the given configuration in the backend. Terraform internally uses the access credentials set up for the cloud providers to consume their APIs for the creation, modification, and destruction of the resources.
**Note: **Having successfully run the plan command, doesn’t mean there won’t be any errors during the apply phase.
Perhaps, one of the most important commands during the learning phase, if you want to avoid huge bills. 🙂
After the configuration is applied (created, modified, destroy), appropriate changes are reflected in the Terraform state file.
terraform destroy reads the state file to understand which resources currently exist and deletes the same. All you need to do is navigate to the root directory and run:
These are basic resource lifecycle management CLI commands but they are the most important when working with Terraform. As we go through more details of Terraform’s state management, modules, and backend — the significance of these commands would arise.
There are certain Terraform CLI commands which are very useful while writing the configuration itself. Let us take a look at some important ones which you can start using right away.
If you ever find yourself using complex expressions and functions, and wonder if this is the right syntax, or would it return the expected value at a certain point in the configuration? Well,
terraform console can help you do a quick check. Run
terraform console and it would open an interactive session where you can print and try out expression values.
Terraform has its own style convention — refer to it here. But you don’t really have to worry about it because we can make sure all the conventions are followed by simply executing the below command in the root Terraform directory.
terraform fmt rewrites the configuration files after the code is adjusted to follow conventions.
I know we talked about validations when we discussed the
terraform plan. However,
terraform validate is another kind of validation where it takes care of syntax errors. It has nothing to do with the verification of remote states or resources. It is a simple validation command to check the syntax of Terraform configuration. Run this command in the root module as below, if successful, be sure about the syntax.
Terraform state contains a lot of useful structure information, which can be queried to understand current situations with cloud resource deployment. This part describes a few commands which help us in this regard.
Before we discuss the actual commands, do take a look at any existing
terraform.tfstate file. Do note that it is just a JSON file that has the information of the currently applied configuration.
terraform show simply prints the current state on the console. By default, it prints the information in the form of formatted HCL, but if you want to get a JSON output, that is possible as well by running the below command. JSON output can prove to be more useful when we have to pass the information to other interfaces.
terraform show -json
terraform show gives us the verbose output, in the sense that it prints everything that's present in the state file. However, if you need specific details about the state, running
terraform state list will present you with the resource titles of the created resources.
terraform state show helps in getting the details of a particular resource.
Terraform CLI also has the ability to generate output in the form of a graph. Simply running
terraform graph in the root directory will help you with a digraph. However, if you want a graphical representation you need to install GraphViz (sudo apt install graphviz on Linux).
terraform graph | dot -Tsvg > graph.svg
Terraform CLI is also used in conjunction with Terraform Cloud. Terraform Cloud is used to maintain workspaces, states, private modules and to enforce access control on the infrastructure being managed. These are topics for later, but for now, just assume that we have to deal with Terraform Cloud in the future so that we can proceed with the first CLI commands related to authentication.
Authentication between Terraform Cloud and CLI is token-based. You can log in to your Terraform Cloud by mentioning the
hostname while executing the below command. If you attempt to login without providing a hostname, it is assumed that you are looking to log in to app.terraform.io.
terraform login [hostname]
Running the above
login command in the terminal window, Terraform CLI asks for confirmation about 2 things:
A request for API token using your browser
A request to store the token in
By typing in
yes, you confirm the same and the browser window opens up and asks you to log in to app.terraform.io. You will be presented with a token to be copied and pasted into the terminal window. That is it - you are successfully logged into Terraform Cloud using Terraform CLI.
To log out of Terraform Cloud from Terraform CLI, all you need to do is run
terraform logout from the terminal window.
For the next section, let’s refer to the example code here.
Imagine a scenario, where the resources are created but they have some unexpected settings or configurations which are not favorable to the overall scheme of things. In such cases, the obvious choice is to recreate that particular resource instead of recreating the entire stack.
To fulfill this situation, Terraform performs something called “tainting” of the resource.
terraform taint command helps to manually mark resources as tainted.
Tainting resources happens in the Terraform state file. Terraform does not perform any operation on the actual resources themselves. As a result of this, the next time you run the
terraform plan, it would indicate that one resource will be destroyed and another will be created in its place. Terraform plans this change for all the tainted resources.
Let’s say other resources depend on a certain attribute of the tainted resource. In that case, Terraform plans rebuilding of those resources as well, if required. Meaning if the configuration change is possible without rebuilding, then those resources will not be rebuilt.
Tainting of the resource is done manually by running the command as below.
terraform taint <resource identifier>
In our example, we are creating 2 resources by the names “
demo_vm_1” and “
demo_vm_2”. Initialize the root directory and run the
As expected, it gives us an output saying, 2 resources to add, and in the output, it is going to provide us with instance id assigned by AWS. Apply this configuration to create 2 EC2 instances and note the output. In my case, I was assigned the below IDs for 2 different instances.
Apply complete! Resources: 2 added, 0 changed, 0 destroyed. Outputs: instance_id_1 = "i-02506cc9073afe093" instance_id_2 = "i-059e81b2cbc09f400"
Let us assume, we understand that instance 2 was not created properly and we want to mark it as tainted. Run the below command to mark instance_id_2 as tainted.
terraform taint aws_instance.instance_id_2
Terraform confirms the action by outputting below:
Resource instance aws_instance.demo_vm_2 has been marked as tainted.
At this point, if we run the
terraform plan, it would give an output something like below. It plans to delete the tainted resource, recreates it, and then output the new ID of the recreated EC2 instance.
Plan: 1 to add, 0 to change, 1 to destroy. Changes to Outputs: ~ instance_id_2 = "i-059e81b2cbc09f400" -> (known after apply) ------------------------------------------------------------------------ Note: You didn't specify an "-out" parameter to save this plan, so Terraform can't guarantee that exactly these actions will be performed if "terraform apply" is subsequently run.
Also, observe the state file at this point. In the resources section,
demo_vm_2 is marked with an additional property –
Let us go ahead and run
terraform apply. As expected, it prints the output with 2 instance IDs. Since only one resource was rebuilt. Notice that the ID assigned by AWS is different for
instance_id_2 but the same for
Apply complete! Resources: 1 added, 0 changed, 1 destroyed. Outputs: instance_id_1 = "i-02506cc9073afe093" instance_id_2 = "i-092ea5f280dc558c9"
That’s about the
taint command. Of course, we can un-taint the resources by running below command and everything will be back to normal.
terraform untaint aws_instance.demo_vm_2
Terraform trusts and relies completely on the state file. The state file is a representation of real-world deployment. Terraform “manages” real-world infrastructure with the help of states. Sometimes, managing resources requires renaming and removing items from the state.
Terraform achieves this using commands which are very similar to file system management commands —
mv is used to move items from one state to another, or it can also be used to simply rename the items and update the state file.
rm is used to remove a certain item from Terraform state management.
These commands work purely from the state file perspective and no changes take place to the real infrastructure. Let us use the same example and try to rename a resource. Currently, we have created 2 VMs whose identifiers are
demo_vm_2. We have a requirement to change the name of
But before we go ahead and use
terraform mv command, take a note of actual EC2 instances in your AWS management console as well as the Terraform state file. In the state file, note the name attributes of the same.
Run the below command to satisfy our requirement:
terraform state mv aws_instance.demo_vm_1 aws_instance.special_vm
Below output is generated. Note that, nothing has changed with actual resources on AWS. Take a look at the state file again, and notice the
demo_vm_1 does not exist in it, instead,
special_vm exists with the same attributes.
Move "aws_instance.demo_vm_1" to "aws_instance.special_vm" Successfully moved 1 object(s).
We have only made this change in the state file and NOT in the configuration. If we run
terraform plan at this point, it treats this as a change in the configuration which triggers deletion of “
special_vm”, and triggers the creation of “new”
But this is not true since all we wanted to do was to rename the resource. This is one of the limitations as of now and we experienced the same behavior when we tried to
import the pre-existing resources in to Terraform state for management. Here, to align things correctly, do the changes to the configuration – change
terraform plan again and check the same this time. No changes should be required.
Similarly, we can also
remove the configuration from Terraform management by running the
rm command as below.
terraform state rm aws_instance.special_vm
Running the above command removes
special_vm from the state file. Having done this, Terraform no longer associates the existence of configuration code written for
special_vm in our
main.tf file with anything in the state file. Thus, running the
terraform plan proposes a plan to create another VM.
Here it is up to us to keep the configuration for
special_vm, or completely get rid of it. To completely get rid of it, remove the corresponding configuration from
.tf files, and run the plan again.
Make sure to check the output of the
terraform plan after this and also observe the state files.
Please note that
rm commands are applicable not just for resources but also for modules, providers, etc.
That completes the introduction of basic and important commands. Of course, this post is not meant to list all the available commands on Terraform CLI documentation. Above are the most used commands and if you are looking forward to being a Terraform developer, you ought to know them. We would learn about
modules in the next post.
Originally published at http://letsdotech.dev on January 8, 2021.