Terraform Certification (Part 12): State
Tags: hashicorp, terraform
Published: 2023-04-19 10:00:00

We are close to the end of my Certified Terraform Associate course, but we have one major concept left to go through: **Terraform state**. That is the topic of this lesson.

If you come from a background of setting up resources using infrastructure-as-code tools such as AWS CloudFormation or Azure Bicep you might be confused about the concept of _state_ in Terraform - and rightly so.

In this lesson I will go through Terraform state, what it is, how we work with it, and where we store it. This lesson covers all of section 7 of the Certified Terraform Associate exam curriculum. This part of the curriculum is outlined below:

| Part  | Content                                                       |
| :---: | ------------------------------------------------------------- |
| **7** | **Implement and maintain state**                              |
|  (a)  | Describe default `local` backend                              |
|  (b)  | Describe state locking                                        |
|  (c)  | Handle backend and cloud integration authentication methods   |
|  (d)  | Differentiate remote state back end options                   |
|  (e)  | Manage resource drift and Terraform state                     |
|  (f)  | Describe backend block and cloud integration in configuration |
|  (g)  | Understand secret management in state files                   |

## Terraform state

We have already seen that we have a _real world_, and we have our _Terraform configuration_.

How do Terraform keep track of what exists in the real world? This is where the concept of _state_ comes in. State represents what Terraform knows about the resources it has created. In an ideal situation the Terraform state should map one-to-one with the resources in the real world. This is not always the case, because the real world can change independently from the Terraform configuration. This is known as _resource drift_. We will hear more about resource drift later in this lesson.

The state is stored in a JSON file. In general we should never directly open and modify the state file. We will see an example of a state file later in this lesson, but it is not necessary for the certification exam to be familiar with how the state file looks in detail.

In the official documentation there are three[^three] reasons listed for why Terraform has a state file, I will briefly go summarize them here:

1. Terraform must be able to map your configuration to the real world in some way. It was early on decided that the state file was the most pragmatic approach. Alternatives included using provider-specific ways to _tag_ a resource in some way to be able to identify it, but this approach differs for each provider and it does not scale well.
1. Terraform must store metadata that specifies things such as the relationship (dependencies) between different resources in, or outside of, your configuration. For instance, when you delete a resource from a configuration Terraform might require information about dependencies outside of your configuration, and this information is kept in the state file.
1. Terraform stores many attributes for each resource in your configuration. When you run `terraform plan` Terraform must reach out to your providers to ask for the latest configuration for each resource, to determine if there are any changes to perform. This works well for small configurations, but less so for larger configurations. The state file can be used as a cache to improve performance when working with larger infrastructures. If `terraform plan` takes unreasonably long time you can add `-refresh=false` to the command, this will instead use the values in the state file as the source of truth.

## Backends

A _backend_ is a place where your state file is stored. If you do not explicitly specify a `backend` block in your configuration then Terraform will use the _local backend_.

The local backend consists of a local file named `terraform.tfstate`. This backend is not suited for team environments where there is more than a single developer.

What other options than the local backend are there? In fact there are a number of options. A few of the popular options are: Azure storage account (`azurerm`), Google Cloud Storage (`gcs`), and AWS S3 (`s3`).

A `backend` block in HCL is a sub-block to the `terraform` block in your configuration. An example of what this looks like:

```hcl
terraform {
  backend "azurerm" {
    resource_group_name  = "rg-terraform-backend"
    storage_account_name = "terraform"
    container_name       = "tfstate"
    key                  = "terraform.tfstate"
  }
}
```

In this example we see an example of using an Azure backend (`azurerm`). This backend stores the state file as a blob[^blob] in a storage account. To configure this backend we must provide:

- the name of an Azure resource group (`resource_group_name`)
- the name of an Azure storage account (`storage_account_name`)
- the name of a container in this storage account (`container_name`)
- the name of the blob that is the state file (`key`)

There are additional configuration options available, most notably options related to authenticating to Azure to be able to use the backend. More about authentication to a backend in a later section.

Each backend type will require different configuration options. Let's look at another example for the S3 backend in AWS:

```hcl
terraform {
  backend "s3" {
    bucket = "my-state-bucket"
    key    = "tfstate/terraform.tfstate"
    region = "eu-west-1"
  }
}
```

S3 is the blob storage service in AWS. For this backend we must configure:

- the name of an S3 bucket (`bucket`)
- the name of the blob that is the state file (`key`)
- the AWS region (`region`)

As with the Azure backend this backend has additional optional configuration options. See the documentation for the backend you want to use.

Once you have included a `backend` block and you have included the proper authentication details (read more about authentication later in this lesson) your state file will be stored at the configured location. Anyone else using the same Terraform configuration will use the same state file.

## State locking

Some backends support _state locking_.

State locking is when the state file is set in a locked mode when you run Terraform, prohibiting others from running Terraform and performing write operations to this same state file at the same time. This is important when working together with other team members with the same Terraform configuration.

You should check the documentation for the backend you are using to know if it supports state locking or not.

If your selected backend does not support state locking you might end up in a bad situation where several developers are writing to the same state file at once.

## Authenticate to a backend

If you use some other backend than the local backend then chances are that you must authenticate to the backend to be able to use it. There are usually a few options for how to authenticate to different backends, so here you must also read the documentation for your selected backend.

Let us look at the `azurerm` backend as an example. For this backend you can authenticate in the following ways:

- Using a Managed Service Identity
- Using a Service Principal with OpenID Connect
- Using a SAS token
- Using a storage account access key
- Using Azure AD authentication
- Using a Service Principal with a certificate
- Using a Service Principal with a client secret

As you can see, there are many alternatives. What is best for you depends on your situation. The important take-away from this section is: how you authenticate to your backend depends on what backend it is, and most often there are many alternatives for how to authenticate yourself.

## Resource drift

If you make an update to a resource outside of your Terraform configuration then Terraform will report this as resource drift, i.e. your state no longer corresponds to the real resource. This is generally something you want to avoid at all costs, but sometimes it does happen.

What to do when you end up in that situation? This could be a tricky situation, but one recommended approach is to run `terraform plan -refresh-only` and `terraform apply -refresh-only`. These will update the state file to match the real-world, without applying any changes to the resources.

In the certification exam I did not encounter any questions regarding this topic, but be aware that there could be questions regarding this. If so, keep the `-refresh-only` flag in mind.

## Secrets management in state files

What happens if you generate a password using the `random` provider, where is this password stored? Let us run a small example using the following Terraform configuration:

```HCL
terraform {
  required_providers {
    random = {
      source = "hashicorp/random"
    }
  }
}

resource "random_password" "db_password" {
  length  = 16
  upper   = true
  lower   = true
  special = true
  numeric = true
}
```

In this configuration I have included the `random` provider, which can be used to generate random numbers, strings, names, and passwords. I include a single `resource` block of the `random_password` type. This resource will result in a password with 16 characters.

I run through `terraform init`, `terraform plan`, and `terraform apply`. After these commands Terraform has created a state file for me. Let's take a look at the full state file `terraform.tfstate`:

```json
{
  "version": 4,
  "terraform_version": "1.3.6",
  "serial": 1,
  "lineage": "a14afc24-a787-3cee-a687-d65d226ba259",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "random_password",
      "name": "db_password",
      "provider": "provider[\"registry.terraform.io/hashicorp/random\"]",
      "instances": [
        {
          "schema_version": 3,
          "attributes": {
            "bcrypt_hash": "$2a$10$5vF3NaAMaoX4GTbzVfe82ureygwmZiBrAiGAQIH3uSOZ2.p44Xqay",
            "id": "none",
            "keepers": null,
            "length": 16,
            "lower": true,
            "min_lower": 0,
            "min_numeric": 0,
            "min_special": 0,
            "min_upper": 0,
            "number": true,
            "numeric": true,
            "override_special": null,
            "result": "7L8AAn!@I\u003e53(+6K",
            "special": true,
            "upper": true
          },
          "sensitive_attributes": []
        }
      ]
    }
  ],
  "check_results": null
}
```

In `.resources[0].instances[0].attributes.result` we can see the generated secret in plain text. This means that, in general, your state file should be considered a secret and it should be handled as such.

## Summary

In this lesson we learned everything we need to know about Terraform state for the certification exam.

In summary we looked at:

- What the Terraform state is and what it is used for.
- What a backend is and how we define one using a `backend` block in HCL.
- We learned that if we do not specify an explicit backend then Terraform will use the local backend, which is a local file named `terraform.tfstate`.
- We learned about state locking and the benefits of this when working together with a team with the same Terraform configuration.
- We learned that most backends, except for the local backend, in general requires authentication. We saw how different backends accept different authentication methods.
- We briefly covered the concept of resource drift and what we can do when we have resource drift. We saw the `-refresh-only` flag for `terraform plan` and `terraform apply`.
- We briefly covered secrets management in state files and we learned that the state file should be handled as a secret.

[^blob]: "_A binary large object (BLOB or blob) is a collection of binary data stored as a single entity._" - Description fetched from https://en.wikipedia.org/wiki/Binary_large_object at 2023-02-05.
[^three]: In fact there are four reasons, but the fourth is more a benefit of using state when working in a larger team, but is not strictly a reason why Terraform requires a state file. See https://developer.hashicorp.com/terraform/language/state/purpose for the full list of reasons and details description.