Beginner’s Guide to Terraform apply

Andrei Dumitrescu
Andrei Dumitrescu
hero image

terraform apply is one of the most fundamental and important commands you'll ever use in Terraform.

You literally can’t turn your code into infrastructure without it.

However, there are a few things about terraform apply that aren’t obvious until you’ve used it for a while, and those details can make the difference between smooth deployments and frustrating surprises.

So in this guide I’ll break down how it works and how to use it, while also avoiding common mistakes.

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 Terraform apply and how does it work?

At some point in every Terraform project, you have to push the button that makes your code real, and that button is terraform apply. It’s the step where all those lines you wrote in .tf files finally show up as actual infrastructure you can use.

The thing is, it sounds like terraform apply just charges ahead and builds everything, but it’s a lot smarter (and safer) than that. 

Here’s what really happens when you run it:

  • Terraform starts off by checking your configuration files (what you want), the state file (what it already knows about), and your cloud account (what’s actually out there)

  • Next, it figures out the differences from what's live (in the state file) vs what you want to happen (in your config file). This way it can see what’s missing, what’s out of date, and what doesn’t belong anymore. With that, it puts together a plan. Even if you never ran terraform plan`` on its own, you still get that preview here

  • Then comes the safety check. Before taking the action Terraform stops and shows you the plan in plain English. Nothing happens until you type yes. If you decide not to, it just exits politely, no harm done

  • Only once you’ve given the green light does it go ahead and execute what is in your config file. (Creating what you asked for, fixing what’s drifted, and cleaning up anything you’ve removed from your code)

  • Finally, when it’s finished, it updates the state file so it remembers exactly what it’s now managing, and can check against it the next time you change your code

This 5 step process helps you keep everything running smoothly, assuming you’re using it correctly…

How to use Terraform apply

There are a few different run options available when using terraform apply. To help you see how it works, let's walk through it step by step with a basic example. 

We’ll go through the full cycle: 

  • Writing the code

  • Setting up Terraform for the first time

  • Applying changes

  • And then seeing them live

So let’s have a scenario.

Imagine that you’ve just joined a team, and your first job is to create an AWS storage bucket for project files. 

Sure, it’s a fairly simple task and you could click around in the AWS console to set it up, but then it only exists once and you have no history, and no easy way to share the setup with teammates. 

If you set it up with Terraform instead, you can write it in code, save it in Git if you like, and then anyone can re-create the same thing in seconds.

Here’s how we would go about this.

Step #1. Create a project folder and write your configuration

Because we’re assuming this is your very first time working with Terrafrom, you need to start by making a new folder for this project. 

This folder will be your “home base” for whenever you use Terraform, and you’ll keep all your .tf files here, so you can use them and also come back to them anytime you want to update the project later.

Inside that folder, create a file called main.tf and add this code:

provider "aws" {
  region = "us-east-1"
}

resource "aws_s3_bucket" "project_files" {
  bucket = "my-first-terraform-bucket-1234"
}

Here’s what’s going on:

  • The provider block tells Terraform we’re working with AWS in the us-east-1 region

  • The resource block defines the S3 bucket you want. Bucket names must be globally unique, so swap out the example name for something personal, like 'my-first-terraform-bucket-1234' or whatever you want to call it

Step #2. Initialize Terraform

The very first time you set up a new project folder, you also need to run:

terraform init

This helps Terraform to “stock the toolbox” for your project and download plugins etc.

In this case, we want it to download the AWS provider plugin (so Terraform knows how to talk to AWS), as well as creates a hidden .terraform folder inside your project, so that everything’s ready to go.

Sidenote:

You don’t need to run this every time you change your code. Just once for the project unless you later add new providers (say Azure or Google Cloud) or upgrade Terraform and need fresh plugins.

Step #3. Apply the configuration

Now it’s time to actually make your bucket, so in the same project folder, run:

terraform apply

Remember though that Terraform doesn’t charge ahead blindly. First, it figures out what needs to happen and then shows you the plan. 

So, you’ll see something like this:

Terraform will perform the following actions:

  # aws_s3_bucket.project_files will be created
  + resource "aws_s3_bucket" "project_files" {
      + bucket = "my-first-terraform-bucket-2025"
      + id     = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.
As you can see, it’s broken down into two main parts. The action to be taken, and then a final checklist plan.

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

This is Terraform saying: 

“I see you want to add one new thing (the bucket). Nothing that already exists is changing. Nothing that already exists is being destroyed.”

Then it pauses with a safety check:

Do you want to perform these actions?
  Only 'yes' will be accepted to approve

  • If you type `yes`, Terraform will go ahead and create the bucket in AWS

  • If you type `no`, it stops and nothing happens. Nothing is created, changed, or destroyed

In our case we want this to run so we type yes.

Then once it’s finished, Terraform also updates its state file so it remembers exactly what it just created. That’s how it knows what’s already there next time you run it.

Easy right!?

But what about if we wanted to update or change anything? 

Well that’s super simple also.

For example

If your team later decides the bucket should have versioning enabled, you’d simply edit your main.tf like so:

resource "aws_s3_bucket" "project_files" {
  bucket = "my-first-terraform-bucket-1234"

  versioning {
    enabled = true
  }
}

Then simply run terraform apply again. (No terraform init or new folder creation required).

This time Terraform will notice the bucket already exists, compare it with your updated code, and just turn on versioning instead of recreating the bucket from scratch.

And that’s the magic of Terraform because it keeps track of your infrastructure, so you can make changes safely and predictably, instead of starting from zero each time.

We can also do a few other things with it, so let’s take a look at some of them.

The most common apply flags and when to use them

Sometimes you’ll want to adjust how terraform apply behaves, especially when you’re automating Terraform or dealing with special cases, and that’s where flags come in.

Here are the most useful ones, along with when you’d actually use them.

Terraform apply -auto-approve

This flag removes the safety check from our code so we don’t need to type yes for it to run.

So why would we want this?

Well, in a CI/CD pipeline, no one is there to type “yes”, so if you don’t skip the prompt, the run will just hang forever. Obviously we don’t want this to happen but at the same time, we still want this to run safely.

So how do we solve this?

The trick is to move our safety checks up to earlier in the process, by making sure we thoroughly check the plan ourselves. This way we know it's safe to run and it won’t hang in the pipeline.

This is kind of a good habit to have, even if you’re not using -auto-approve.

Why?

Simply because it builds a habit of deeper checking .i.e. that's all you're focused  on at that point in time.

For example

When you jump straight to apply, you’re already in ‘go’ mode. The plan scrolls by, but because the next step is an approval prompt, it’s easy to skim and type “yes” without noticing something risky. 

But running plan on its own forces you into a test mode mindset instead. Nothing is applied yet, so your only job is to review carefully, and that pause makes you much more likely to catch issues like a resource being destroyed and recreated instead of simply updated.

Think of how many times you’ve written an email and were sure there were no typos before you hit send. Then you read it back later and they are super obvious. 

This is the same kind of thing, only this typo might break your infrastructure, which is why it's best to take a step back and check.

Anyways, rant over and back to the other flags!

terraform apply -refresh-only

One of the main features of apply is it keeps track of your state file. This way it can then see the current configuration and then see what needs to be changed.

However, sometimes people make changes to apps outside of Terraform. If we don’t update our state file for these changes, we’ll overwrite them  by accident when we do any future changes.

So what we can do is use terraform apply -refresh-only to instead take a look at what's currently out there in our infrastructure, and use that to update the current saved state file.

terraform apply -target

Normally, Terraform applies changes to everything in your project at once as that’s the safest way to keep things consistent. 

However, sometimes you only want to apply changes to a single resource and that's exactly what terraform apply -target allows us to do.

For example

Imagine you’re debugging and only want to update the S3 bucket you just edited. Instead of waiting for Terraform to plan the entire project, you can run:

terraform apply -target=aws_s3_bucket.project_files

This tells Terraform:

“Just update this one thing, and leave the rest alone.”

It’s handy for quick fixes during development, but you don’t want to overuse it. 

Why?

Well if you apply only parts of your project too often, you can create drift where some resources don’t line up with your code anymore. It’s useful in special cases, but not an everyday habit.

terraform apply -parallelism

Terraform usually makes changes in parallel, which speeds things up. But some providers (or tricky dependencies) don’t like being hit with too many changes at once.

This flag allows you to slow things down.

For example

terraform apply -parallelism=1

That forces Terraform to apply resources one at a time. It’s slower, but sometimes necessary when a provider’s API struggles under load.

terraform apply -force-unlock

By default, Terraform locks the state file while it’s running. This prevents two processes from making changes at the same time, which could corrupt the state.

Sometimes a run crashes or gets interrupted, leaving behind a stale lock. If you see an error like:

Error acquiring the state lock

That usually means Terraform thinks something is still running when it’s not. 

We can unlock it by running:

terraform force-unlock <LOCK_ID>

This clears the lock without disabling Terraform’s protection. It’s the right way to recover from a crash and get back to a clean state.

Time to try apply for yourself

So as you can see, there’s more to terraform apply than you might think!

Hopefully this guide has helped you understand the inner workings and options you have available when using it. That being said, knowing how it works is nowhere near as good as trying it out for yourself.

So why not give it a go? 

You can start with small, low-stakes projects and create a simple resource, review the plan, confirm the changes, and see how Terraform updates state along the way. 

Then once you’re comfortable with the basics, you’ll know when to reach for flags or automation, and you’ll trust Terraform to handle bigger, production-level changes just as reliably.

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!)