Terraform Associate Certification: HashiCorp Certified

Use the Terraform CLI

Basic Terraform Commands

In this lesson, we will revisit some of the most commonly used Terraform commands. These commands help ensure your configurations are valid, well-formatted, and in sync with your real-world infrastructure. Read on to learn how to validate, format, view the state, and manage resources with Terraform.

Terraform Validate

The terraform validate command checks whether your configuration syntax is correct and internally consistent. When your configuration is valid, you'll receive a successful validation message. For example, a valid configuration like the one below:

resource "local_file" "pet" {
  filename = "/root/pets.txt"
  content  = "We love pets!"
}

Running the command produces:

$ terraform validate
Success! The configuration is valid.

If an error exists in your configuration file, Terraform pinpoints the problematic line and offers suggestions for a fix. For instance, using an unsupported argument such as file_permissions instead of the correct file_permission generates an error message:

resource "local_file" "pet" {
  filename = "/root/pets.txt"
  content  = "We love pets!"
}
$ terraform validate
Error: Unsupported argument

  on main.tf line 4, in resource "local_file" "pet":
  4:  file_permissions = "0777"

An argument named "file_permissions" is not expected here. Did you mean "file_permission"?

Note

Always review the error messages carefully—Terraform’s hints help you quickly identify and fix configuration mistakes.

Terraform fmt

The terraform fmt command scans all configuration files in the current directory and reformats them into a canonical format. This improves readability and consistency across your files. When you run this command, any files that are modified will be displayed in the output.

Terraform Show

The terraform show command displays the current state of your managed infrastructure. This includes a detailed view of resources and their attributes. To facilitate further processing, you can append the --json flag, which outputs the state in JSON format for easier integration with other tools.

Terraform Providers

The terraform providers command lists all providers declared in your configuration. For example, if your configuration uses only AWS resources, the command produces an output similar to:

resource "aws_instance" "db" {
  ami           = var.ami
  instance_type = var.instance_type
}
$ terraform providers
Providers required by configuration:
└── provider[registry.terraform.io/hashicorp/aws]

Providers required by state:
provider[registry.terraform.io/hashicorp/aws]

Terraform Output

The terraform output command retrieves the output variables defined in your configuration. You can display either all outputs or a specific one by providing the output name. Consider the example below:

resource "local_file" "pet" {
  filename        = "/root/pets.txt"
  content         = "We love pets!"
  file_permission = "0777"
}

resource "random_pet" "cat" {
  length    = 2
  separator = "-"
}

output "content" {
  value       = local_file.pet.content
  sensitive   = false
  description = "Print the content of the file"
}

output "pet-name" {
  value       = random_pet.cat.id
  sensitive   = false
  description = "Print the name of the pet"
}

When you run:

$ terraform output
content  = We love pets!
pet-name = huge-owl

You can also specify an output by appending its name to the command.

Terraform Refresh

Terraform automatically refreshes the state during commands like terraform plan and terraform apply to capture any real-world changes. The terraform refresh command explicitly syncs the state file with the actual infrastructure. For example, if a resource has been manually updated, refreshing the state will capture the latest changes:

resource "local_file" "pet" {
  filename        = "/root/pets.txt"
  content         = "We love pets!"
  file_permission = "0777"
}

resource "random_pet" "cat" {
  length    = 2
  separator = "-"
}
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

random_pet.cat: Refreshing state... [id-huge-owl]
local_file.pet: Refreshing state... [id=cba595b7d9f94ba1107a46f3f731912d95fb3d2c]
--------------------------------------------------------------------------
No changes. Infrastructure is up-to-date.

$ terraform apply -refresh-only
random_pet.cat: Refreshing state... [id-huge-owl]
local_file.pet: Refreshing state... [id=cba595b7d9f94ba1107a46f3f731912d95fb3d2c]

This command updates the state file without altering your actual infrastructure, helping you determine the next steps for your deployment.

Terraform Graph

The terraform graph command generates a visual representation of resource dependencies in your configuration or execution plan. The output is produced in DOT format and can be visualized with Graphviz. For users who prefer a visual perspective, this helps in understanding complex dependency relationships within your infrastructure.

$ terraform graph
digraph {
  compound = "true"
  newrank = "true"
  subgraph "root" {
    "[root] aws_instance.cerberus (expand)" [label = "aws_instance.cerberus", shape = "box"]
    "[root] provider[\"registry.terraform.io/hashicorp/aws\"]" [label = "provider[\"registry.terraform.io/hashicorp/aws\"]", shape = "diamond"]
    "[root] var.ami" [label = "var.ami", shape = "note"]
    "[root] var.instance_type" [label = "var.instance_type", shape = "note"]
    "[root] var.region" [label = "var.region", shape = "note"]
    "[root] aws_instance.cerberus (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/aws\"]"
    "[root] aws_instance.cerberus (expand)" -> "[root] var.ami"
    "[root] aws_instance.cerberus (expand)" -> "[root] var.instance_type"
    "[root] meta.count-boundary (EachMode fixup)" -> "[root] aws_instance.cerberus (expand)"
    "[root] provider[\"registry.terraform.io/hashicorp/aws\"]" -> "[root] var.region"
    "[root] root" -> "[root] meta.count-boundary (EachMode fixup)"
    "[root] root" -> "[root] provider[\"registry.terraform.io/hashicorp/aws\"]"
  }
}

You can generate and inspect the DOT output with Graphviz to gain a deeper understanding of your infrastructure's architecture.

Terraform State Commands

Terraform includes several commands to manage the state file—allowing you to list, view, move, pull, and remove state entries. It is important to use these commands instead of manually modifying the state file.

List Resources

Use the terraform state list command to display all resources recorded in the state file. The output lists resource addresses in the format resource_type.resource_name. For example:

$ terraform state list
aws_dynamodb_table.cars
aws_s3_bucket.finance-202922

You can also filter resources by specifying a pattern:

$ terraform state list aws_s3_bucket.cerberus-finance
aws_s3_bucket.cerberus-finance

Show Resource Details

The terraform state show command displays attributes of a single resource from the state file. For example, to see details of an S3 bucket:

$ terraform state show aws_s3_bucket.cerberus-finance
resource "aws_s3_bucket" "terraform-state" {
  acl                         = "private"
  arn                         = "arn:aws:s3:::cerberus-finance"
  bucket                      = "cerberus-finance"
  bucket_domain_name          = "cerberus-finance.s3.amazonaws.com"
  bucket_regional_domain_name = "cerberus-finance.s3.us-west-1.amazonaws.com"
  force_destroy               = false
  hosted_zone_id              = "Z2F5ABCDE1ACD"
  id                          = "cerberus-finance"
  region                      = "us-west-1"
  request_payer               = "BucketOwner"
  tags = {
    "Description" = "Bucket to store Finance and Payroll Information"
  }
}
versioning {
  enabled    = false
  mfa_delete = false
}

Move Resources

The terraform state mv command is used to rename or move a resource within the state file. This does not automatically update your configuration files, so you must adjust them manually.

For example, suppose you have the following configuration for a DynamoDB table:

resource "aws_dynamodb_table" "state-locking" {
  name         = "state-locking"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"
  attribute {
    name = "LockID"
    type = "S"
  }
}

And your state file includes:

{
  "resources": [
    {
      "mode": "managed",
      "type": "aws_dynamodb_table",
      "name": "state-locking",
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]"
    }
  ]
}

To rename this resource from state-locking to state-locking-db, execute:

$ terraform state mv aws_dynamodb_table.state-locking aws_dynamodb_table.state-locking-db

After moving the resource in the state, update your configuration file accordingly:

resource "aws_dynamodb_table" "state-locking-db" {
  name         = "state-locking"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"
  attribute {
    name = "LockID"
    type = "S"
  }
}

A subsequent terraform apply will confirm that no changes are pending:

$ terraform apply
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Pulling Remote State

If you are using remote state, you may need to download the state file locally. The terraform state pull command retrieves the remote state, which can be processed with tools like jq for JSON querying:

$ ls
main.tf  provider.tf
$ terraform state pull
{
  "version": 4,
  "terraform_version": "0.13.0",
  "serial": 0,
  "lineage": "b6e2cf0e-ef8d-3c59-1e11-c6520dcd745c",
  "resources": [
    {
      "mode": "managed",
      "type": "aws_dynamodb_table",
      "name": "state-locking-db",
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      "instances": [
        {
          "schema_version": 1,
          "attributes": {
            ...
          }
        }
      ]
    }
  ]
}

You can query specific details using jq:

$ terraform state pull | jq '.resources[] | select(.name == "state-locking-db") | .instances[].attributes.hash_key'
"LockID"

Removing Resources from State

The terraform state rm command allows you to remove one or more resources from the state file. This is useful when you no longer want Terraform to manage a resource. Remember, removing a resource from the state does not delete the actual infrastructure.

$ terraform state rm aws_s3_bucket.finance-2020922
Acquiring state lock. This may take a few moments...
Removed aws_s3_bucket.finance-2020922
Successfully removed 1 resource instance(s).
Releasing state lock. This may take a few moments...

After removal, ensure you also delete the corresponding resource block from your configuration files.

Pushing State Changes

The terraform state push command updates the remote state by pushing your local state file. Use this command with extreme caution, as it can disrupt your configuration if misused. If the local state differs significantly from the remote state, you may encounter an error message like this:

$ terraform state push ./terraform.tfstate
$ terraform state push ./randomstate/terraform.tfstate
Failed to write state: cannot import state with lineage "1dc19ee8-2b7f-d87a-4786-4be724b24988" over unrelated state with lineage "6d167ba6-5171-a624-6bad-2e6bfec62c28"

Although the --force option exists, it is recommended only as a last resort.


That concludes our lesson on basic Terraform commands. By understanding these commands, you will be better equipped to validate your configurations, maintain a clean state, and manage your infrastructure efficiently.

For additional resources and more detailed explanations, consider visiting the following links:

Watch Video

Watch video content

Previous
Dependency Lock File