Terraform CLI
Published: 2021-01-13 08:18:58


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](https://dev.to/letsdotech/introduction-to-terraform-44h1).

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.

## Directories

By now it should already be clear that the CLI interface for Terraform is `terraform`. Every command related to Terraform CLI starts with `terraform` command.

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.

```
terraform init
```


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.

## Infrastructure lifecycle

Some of the most important and most used Terraform CLI commands are `plan`, `apply`, and `destroy` which manage the planning, creation, modification, and deletion of cloud infrastructure.

### Plan

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.

```
terraform plan
```


### Apply

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 apply
```


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.

### Destroy

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:

```
terraform destroy
```


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.

## Formatting code

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.

### console

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.

Optionally you can pass in a path to state files to refer to values and experiment with expressions to derive a correct one. This is similar to the javascript console which is available in the latest web browsers like Google Chrome or Mozilla Firefox.

### fmt

Terraform has its own style convention — refer to it [here](https://www.terraform.io/docs/configuration/style.html). 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
```


Running `terraform fmt` rewrites the configuration files after the code is adjusted to follow conventions.

### validate

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 validate
```


## Inspecting infrastructure

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.

### show

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


### state list and state show

`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.

### graph

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


## Authentication

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.

### Login

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:

1. A request for API token using your browser

2. A request to store the token in `/home/<username>/.terraform.d/credentials.tfrc.json` file

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.

### Logout

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](https://github.com/sumeetninawe/tf-blog/commit/b1440528ad94740308c1adebcb02c72f34e93d67).

## Tainting

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 `terraform plan`.

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 – `“status”: “tainted”`.

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 `instance_id_1`.

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

## Renaming and Removal

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` and `rm`.

`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_1` and `demo_vm_2`. We have a requirement to change the name of `demo_vm_1` to `special_vm`.

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” `demo_vm_1`.

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 `aws_instance.demo_vm_1` to `aws_instance.special_vm`.

Run `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 `mv` and `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](https://www.terraform.io/docs/cli-index.html). 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](https://letsdotech.dev/2021/01/08/terraform-cli-part-1/) on January 8, 2021.*