Beginner’s Guide to Terraform CLI Commands

Andrei Dumitrescu
Andrei Dumitrescu
hero image

Terraform can manage your whole infrastructure, but the problem of course is that none of it works without the CLI:

  • But what is the CLI?

  • What does it do?

  • And how can you use it more effectively?

Don’t worry because I’ll cover all this and more, as well as the most important Terraform CLI commands, show you how they work, and help you avoid the mistakes that trip people up early on.

Let’s get into it…

Sidenote:  If you want to dive deeper into learning and using Terraform, check out my DevOps Bootcamp for Terraform

I cover everything from the fundamentals all the way to provisioning real-world cloud infrastructure on AWS.

This means you can go from total beginner to being able to get hired as a DevOps Engineer or System Administrator, so it’s a great resource for any skill level.

With that out of the way, let’s get into this guide…

What is the Terraform CLI?

Otherwise known as the ‘Command Line Interface’, the Terraform CLI is how you interact with Terraform so that you can control all the other tools on your network remotely.

Think of it as the master control system. 

You type commands into a terminal, and Terraform goes out and tells those other tools what to do. So whether you're deploying to AWS, Azure, or any other provider, you're not logging into those platforms directly. You're using Terraform to do it for you, and the CLI is how you give it instructions.

So how does it work?

It’s fairly simple. You write your infrastructure setup in .tf files, then run commands to apply those changes. The CLI reads your code, compares it to what’s already deployed, and updates your infrastructure to match.

The issue though is that the CLI doesn’t come with a sleek design or a helpful user interface. It’s just a terminal. You write the command, hit enter, and Terraform does the work. (Kind of how you might type a request into ChatGPT).

The good news is that to make your life easier, Terraform comes with a set of built-in commands. The trick is just knowing how to use them, so let’s break them down, one by one.

Top 14 Terraform CLI commands explained

Rather than cover every possible command out there, these are the ones you’ll come back to over and over again, whether you’re spinning up a brand new project or making small changes to an existing one.

And to make it even easier, I’ll break them down in the order you’d normally use them.

terraform init

The first command you should learn is terraform init,

So let me explain

When you start a new Terraform project, you’ll have a folder with your .tf files in it. But at that point, Terraform doesn’t know what providers you’re using, whether you’re pulling in any modules, or how your project should be set up. 

That’s what init is for.

When you run it, Terraform will look at the code in your folder, figure out which cloud provider plugins it needs (like AWS or Azure), and download them in the background. It also sets up a hidden .terraform folder with everything it needs to manage your project.

So how do you use it?

Just open your terminal, navigate to your project folder, and type:

terraform init

You’ll then see some output in the terminal that looks something like:

Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching ">= 4.0.0"...
- Installing hashicorp/aws v5.17.0...
Terraform has been successfully initialized!

Once you see that last line, you’re good to go. 

Simple right?

Just remember, though:

  • You only need to run this command once when setting up a project, or if you change providers, add modules, or mess with the backend configuration

  • But if you skip this step and try to run another command first, Terraform will just throw an error. It won’t be able to load the plugins or access the backend until init is complete.

So always start with terraform init

terraform fmt

Once your project is initialized, the next command worth knowing is terraform fmt, because it cleans up your code and formats your .tf files so they follow a standard style. That means consistent indentation, spacing, and ordering, kind of like how Prettier works for JavaScript or black works for Python.

This might seem like a small thing, but it really helps when you’re working in a team or coming back to your code later. (It also helps prevent silly mistakes caused by inconsistent formatting).

How do you use it?

Just run:

terraform fmt

By default, it’ll look at all the .tf files in your current folder and clean them up. You won’t always notice a huge change, but behind the scenes it makes your files easier to read, and easier for Terraform to parse.

You can also pass flags if you want it to be more specific.

For example

terraform fmt -recursive tells it to go through all subfolders too (useful if you’re working with modules or nested directories).

While terraform fmt -check checks if the files are formatted correctly, and is handy for CI pipelines.

When should you use this?

It’s safe to run any time, and is a great habit to run it before committing your code. It won’t ever change how your config works, it just makes things cleaner and more predictable.

Think of it as tidying up your workspace before you actually get to work.

terraform validate

After formatting your files, the next useful command is terraform validate

This one checks whether your configuration is technically correct by looking at your .tf files and checks that the syntax is valid and the configuration makes sense. If there’s a missing bracket, a typo in a resource block, or a misused argument, validate will catch it.

However, it’s important to remember that it won’t tell you if you’ve made a mistake with your AWS configuration or anything like that. 

Its entire goal is to tell you if your Terraform code doesn’t follow the rules.

How do you use it?

Just type:

terraform validate

And you’ll get a message like:

Success! The configuration is valid.

Easy right?

But what if something’s wrong? Well then it might look something like this:

╷
│ Error: Unsupported block type
│
│   on main.tf line 12, in resource "aws_s3_bucket" "example":
│   12:   unknown_block {
│
│ Blocks of type "unknown_block" are not expected here.
╵

When should you run it?

Usually after fmt, and definitely before planning or applying any changes. It’s a quick way to catch basic issues before Terraform starts trying to do anything with your config.

Small tip:

If you're working with multiple files and modules, validate will check the whole project. This is a good thing because even a small mistake in one file can stop you in your tracks. It’s worth running often, especially if something feels off and you’re not sure why.

terraform plan

terraform plan gives you a preview of what Terraform is about to do, before it does it, which is super handy.

It compares what’s in your .tf files with what’s currently deployed in your cloud provider, then tells you exactly what changes it wants to make.

For example

It might show that it's going to create a new resource, update an existing one, or delete something that’s no longer in your config. But the key is, it doesn’t actually do anything yet. It just shows you the plan.

How do you use it?

Just type:

terraform plan

Then you’ll get an output that looks something like this:

Terraform will perform the following actions:

# aws_s3_bucket.example will be created
+ resource "aws_s3_bucket" "example" {
    + bucket = "my-example-bucket"
    + tags   = {
        + "Environment" = "dev"
      }
  }

Plan: 1 to add, 0 to change, 0 to destroy.

That line at the bottom is what you want to pay attention to, as this is what Terraform thinks needs to happen.

Why is this important?

Because once you run terraform apply, there’s no going back!

So plan is your chance to catch mistakes, double-check everything, and make sure you’re not about to destroy something by accident.

It’s also really helpful when working on a team because you can share a plan with someone else to confirm what’s about to change before applying it.

Optional tip: If you want to save the plan and apply it later, you can use the ``-out`` flag:

terraform plan -out=tfplan

This creates a binary file you can feed into terraform apply.

Speaking of which, let’s take a look at that now.

terraform apply

So, once you’ve reviewed your plan and everything looks good, it’s time to run terraform apply. This is the command that turns your .tf files into real infrastructure.

It takes your configuration, compares it to the current state of your cloud environment (just like plan), and then makes whatever changes are needed to match your code. That means creating resources, updating them, or destroying ones that are no longer defined.

How do you use it?

You can just run:

terraform apply

Terraform will run a plan first, show you what it’s about to do, and ask you to confirm:

Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.

Enter a value:

If you type yes, it goes ahead and applies the changes.

Or, if you’ve already run terraform plan -out=tfplan, you can apply that saved plan directly:

terraform apply tfplan

This skips the preview step and just applies the exact plan you saved earlier.

Want to skip the confirmation prompt?

If you're running Terraform in an automated script or CI/CD pipeline, you can add the -auto-approve flag to skip the manual confirmation:

terraform apply -auto-approve

WARNING! Only use ``-auto-approve`` if you’re absolutely sure you want the changes to go through, because it removes the last safety net.

terraform destroy

As the name suggests, this one does the opposite of apply, in that it tears everything down.

terraform destroy looks at the state file to see what infrastructure Terraform has created, and then deletes all of it. 

So why would you want this?

Well, it’s useful when you want to clean up a test environment, reset everything from scratch, or remove unused resources without having to delete them manually in your cloud provider’s dashboard.

How do you use it?

Just run:

terraform destroy

Terraform will then generate a plan (just like it does with apply) and show you everything it’s about to delete.

You’ll then see output like:

Terraform will perform the following actions:

# aws_instance.web will be destroyed
- resource "aws_instance" "web" {
    ...
  }

Plan: 0 to add, 0 to change, 1 to destroy.

Then it will ask you to confirm:

Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure.
Only 'yes' will be accepted to confirm.

Enter a value:

Type yes, and it will proceed.

And just like with apply, you can also skip the confirmation with:

terraform destroy -auto-approve

But again, only do this if you’re 100% sure as you’re removing the safety steps!

When should you use it?

Mostly in dev or testing environments where you want to quickly clean up everything you created. You probably won’t use it often in production unless you’re decommissioning something entirely.

One thing to remember though is that Terraform will only destroy resources it knows about i.e. the ones in your state file. So if something was changed manually or deployed outside of Terraform, it might not be touched at all.

terraform show

Once you’ve applied changes with Terraform, you might want to take a look at what actually exists:

  • What resources were created

  • What values they have

  • And what Terraform is tracking

terraform show allows you to read the current state file and displays all the information Terraform has about your deployed infrastructure. It’s essentially a snapshot of what’s out there, as far as Terraform is concerned.

This includes resource types, names, settings, and even outputs like IP addresses, ARNs, or tags.

How do you use it?

Just run:

terraform show

You’ll get a detailed dump of the state file, formatted in a way that’s easier to read than opening the raw .tfstate file

The output might look something like:

# aws_instance.web
resource "aws_instance" "web" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"
  tags = {
    Name = "web-server"
  }
}

Top tip: 

If your state file is large, or the output is really long, then you can pipe it into less for easier reading:

terraform show | less

When should you use it?

Whenever you want to verify what’s currently deployed, or troubleshoot something that doesn’t look quite right. It’s also helpful when:

  • You’re not sure if a change actually went through

  • You want to check the current value of a resource

  • You’re working with someone else’s project and need to see what’s been created

It’s read-only, so it’s safe to use any time.

terraform output

Once Terraform finishes applying your config, it can pass back useful values such as public IPs, database URLs, or resource IDs. The terraform output command is how you access those values directly.

It prints out the values you've defined using output blocks in your configuration. These are usually the key details you want to reference after a deployment, either manually or from another tool.

For example

Maybe you want to see the DNS name of a load balancer, or the IP of a virtual machine. Rather than digging through the full state file, you can just run terraform output to see exactly what you need.

How do you use it?

To see all outputs at once, just type:

terraform output

You’ll get something like:

web_server_ip = "34.217.19.84"
app_url       = "https://my-app.example.com"

Or, if you want to see a specific output value by name, use:

terraform output web_server_ip

Handy right?

Want machine-readable output?

If you’re using the result in automation or CI pipelines, you can use the JSON flag:

terraform output -json

This returns all output values as a structured JSON object, which makes it easy to parse.

When should you use it?

Any time you want to retrieve a value from the current state without re-running a full apply or reading the .tfstate file directly.

This command is especially useful when:

  • You’re manually verifying a deployment

  • You need to grab a value to plug into another script or tool

  • You’re sharing outputs across teams or environments

terraform state

When something gets out of sync or breaks in a weird way, the terraform state command gives you a set of tools to inspect and manipulate what Terraform is tracking behind the scenes.

These then let you interact directly with the state file where Terraform keeps track of every resource it manages. You can list resources, show details, move things, or remove them entirely from state (without deleting them in the real world).

However, be warned that this is a powerful set of tools, and you should use it carefully. It’s meant for edge cases and troubleshooting and not everyday workflows.

So let’s break these down.

terraform state list

Shows all the resources Terraform is currently managing. This is helpful when you’re not sure what’s in state or want to check a resource’s exact name.

terraform state list

terraform state show <resource>

Shows the full details of a specific resource in the state. You’ll need the resource address from the list command, like:

terraform state show aws_instance.web

terraform state rm <resource>

Removes a resource from the state file without destroying it in your cloud provider. Useful if you created something manually or want Terraform to stop tracking it.

terraform state rm aws_instance.web

terraform state mv <old> <new>

Renames or moves a resource in the state. Handy when you refactor your code and want to keep the same resource without destroying it.

terraform state mv aws_instance.web aws_instance.app

When should you use it?

Only when you really need to. These commands are mostly for advanced use cases, like:

  • Cleaning up after a failed deployment

  • Fixing state drift when something was changed outside of Terraform

  • Restructuring your code without breaking resources

Just a heads-up: 

Any mistakes with the state can cause Terraform to lose track of your infrastructure, so always double-check what you’re doing and consider backing up your state file before making manual changes!

terraform refresh

Most of the time, Terraform’s state file stays in sync with your real infrastructure. But if something changes outside of Terraform, (like if someone manually tweaks a resource in the cloud), the state file won’t automatically reflect that.

That’s where terraform refresh comes in.

This command updates the state file to reflect the actual state of the infrastructure. It talks to your cloud provider, checks each resource, and rewrites the state file to match what’s really there.

To be clear, it doesn’t change your infrastructure, it only updates the local or remote .tfstate file.

How do you use it?

Just run:

terraform refresh

Terraform will then scan through all managed resources and adjust the state accordingly. You won’t get a fancy output like you do with plan or apply , but the state file will now reflect the truth.

When should you use it?

  • If someone manually changes a resource outside of Terraform

  • If a resource was updated through another tool (like the AWS console)

  • If you’re seeing weird differences between plan output and what’s actually live

Warning!

Be careful using this as it can overwrite things you haven’t saved yet. So if you’ve made changes in your .tf files but haven’t applied them, running refresh may update the state file in ways you weren’t expecting, and now your plan might look different.

So use it when things feel “off,” but make sure you’re not in the middle of a partial deployment.

terraform taint

Most of the time, Terraform tries to be smart by only replacing resources if it knows something has changed.  However, sometimes a resource is technically fine on paper, yet broken or misbehaving in reality. That’s when terraform taint comes in.

This command marks a resource for replacement during the next terraform apply , even if Terraform thinks nothing is wrong. It tells Terraform, “Forget the diff, I want you to rebuild this no matter what.”

To be clear though, it doesn’t destroy the resource immediately. It just flags it so the next apply will delete and recreate it.

How do you use it?

You need the resource address, which you can get from terraform state list or your config. 

Then run:

terraform taint aws_instance.web

You’ll get a message like:

Resource instance aws_instance.web has been marked as tainted.

Now when you run terraform plan, it will show that the resource is being replaced:

-/+ aws_instance.web (tainted)

And then when you run terraform apply , it will destroy and recreate that resource.

When should you use it?

  • When a resource is broken in the real world, but Terraform doesn’t detect it

  • When you want to force a rebuild, like restarting a VM or reinitializing a database

  • When testing resource replacement behavior

Heads-up:

Use this carefully, because Tainting causes a full destroy-and-recreate cycle, which can lead to downtime depending on the resource, so always make sure you understand what you’re replacing before you hit apply.

Time to give these a try!

Whether you're starting a new project or troubleshooting an existing one, these CLI commands are the foundation of everything you’ll do with Terraform.

However, the best way to learn anything is to use it, so it’s time to try them out!

Go ahead and set up a test folder, run through the flow, and see it in action. You don’t have to get everything perfect, but just getting comfortable with the CLI will take you a long way.

P.S.

Don’t forget, if you want to dive deeper into learning and using Terraform, check out my DevOps Bootcamp for Terraform

I cover everything from the fundamentals all the way to provisioning real-world cloud infrastructure on AWS and getting hired!

Once you join, you'll have the opportunity to from me, other students and working professionals.

Not only that, but as a member, you’ll also get access to every course in the Zero To Mastery library!

Best articles. Best resources. Only for ZTM subscribers.

If you enjoyed Andrei's post and want to get more like it in the future, subscribe below. By joining over 300,000 ZTM email subscribers, you'll receive exclusive ZTM posts, opportunities, and offers.

No spam ever, unsubscribe anytime

More Terraform content

If you enjoyed this post, check out other articles I've written on Terraform and DevOps:

More from Zero To Mastery

53 Terraform Interview Questions and Answers preview
53 Terraform Interview Questions and Answers
18 min read

About to sit for a DevOps technical interview with Terraform? Learn the most common questions and answers that you need to know to ace your interview!

Beginner’s Guide to Dynamic Blocks in Terraform (With Code Examples) preview
Beginner’s Guide to Dynamic Blocks in Terraform (With Code Examples)
11 min read

Learn how to use Terraform dynamic blocks to automate repetitive tasks, streamline infrastructure, and scale efficiently in this step-by-step guide.

Beginner's Guide To Terraform's Try Function preview
Beginner's Guide To Terraform's Try Function
12 min read

Tired of Terraform errors? Learn how the try function simplifies edge cases and ensures your setups work flawlessly. (With code examples!)