Sign in
← Infrastructure as Code
13 min · medium · Lesson 2 of 5

Terraform: your first resource

Providers, resources, plan and apply, the state file. How Terraform brings the cloud in line with the description.

Providers, resources, plan and apply, the state file. How Terraform brings infrastructure in line with the description.

Why

Terraform takes a text description of infrastructure and creates it in the cloud. You write what should exist, Terraform compares that to what's already there, and applies only the difference. It's the same declarative approach as in the previous lesson, now in practice.

The description is written in the HCL language in .tf files. Terraform reads all .tf files in one folder as a single configuration, so you can split the code across files however is convenient.

Alongside Terraform there's an open fork, OpenTofu, with the same language and commands. It appeared because of a license change. On August 10, 2023, HashiCorp moved Terraform from the open MPL to the Business Source License (BSL), a license with restrictions on commercial use. Part of the community and the companies building products around Terraform published the OpenTF manifesto. HashiCorp didn't respond, and on August 25, 2023 they forked it. In September that year the project was renamed OpenTofu and handed to the Linux Foundation, and on January 10, 2024 the first stable release, 1.6.0, came out.

The basics match between them: .tf files, init/plan/apply, providers, state. New features since 2024 differ between the two projects. Everything in the rest of this lesson and in the labs works in both, except that OpenTofu's binary is called tofu.

Provider and resource

Two main entities: the provider and the resource.

A provider is a plugin for a specific platform (Yandex Cloud, AWS, Hetzner). It knows how to talk to that platform's API. There are providers for local things too: hashicorp/local creates files on disk, and this lesson's labs work with it.

A resource is a specific object: a VM, a network, a bucket, a file.

A fragment for Yandex Cloud (a real VM also requires boot_disk and network_interface blocks, omitted here):

terraform {
  required_providers {
    yandex = {
      source  = "yandex-cloud/yandex"
      version = ">= 0.100"
    }
  }
}

provider "yandex" {
  zone = "ru-central1-a"
}

resource "yandex_compute_instance" "web" {
  name = "web-01"
  resources {
    cores  = 2
    memory = 4
  }
  # boot_disk { ... } and network_interface { ... } are required
}

source tells Terraform where to download the provider from, version constrains its version. Without version, Terraform takes the latest one, and the same code can behave differently six months later.

The resource "type" "name" block describes one object. type (yandex_compute_instance) sets the provider. name (web) is a local reference within the code: other resources reach this object at the address yandex_compute_instance.web, and the object is recorded in state under the same address. The object's actual name in the cloud is set by the name argument, which is a different thing.

Workflow: init, plan, apply

terraform init     # download providers, prepare the working directory
terraform plan     # show what Terraform is about to do
terraform apply    # show the plan again and apply it after yes

init creates two things in the folder. .terraform/ holds the downloaded providers and isn't committed to git. .terraform.lock.hcl pins the exact provider versions chosen. This file is committed, so the whole team and CI use the same provider.

terraform plan is the main command for safety. It changes nothing in the infrastructure, only shows the difference between the code and reality. Here's what a plan looks like for a single file through the hashicorp/local provider:

Terraform will perform the following actions:

  # local_file.index will be created
  + resource "local_file" "index" {
      + content              = <<-EOT
            <h1>Hello from Terraform</h1>
        EOT
      ...
      + filename             = "./site/index.html"
      + id                   = (known after apply)
    }

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

Marks in front of a resource:

The last one is the trickiest. Some arguments can't be changed on a live object, and then Terraform recreates it entirely. Next to such an argument plan writes # forces replacement:

  # local_file.index must be replaced
-/+ resource "local_file" "index" {
      ~ content              = <<-EOT # forces replacement
          - <h1>Hello from Terraform</h1>
          + <h1>Hello, IaC</h1>
        EOT
    ...
Plan: 1 to add, 0 to change, 1 to destroy.

For a file that's harmless. For a VM with data on disk it's data loss. That's why plan is always read before apply, and the first thing to check is destroy in the last line.

terraform destroy deletes everything recorded in this working directory's state. Good for temporary environments.

State: Terraform's memory

To know what it has already created, Terraform keeps state, by default a terraform.tfstate file in the working directory. For every address from the code (local_file.index) it records the real object and its attributes. State is how Terraform knows what to change and what to leave alone. You can see which addresses are in it with terraform state list.

State is critical and dangerous at the same time:

Losing state means losing the link between the code and the real resources. The objects in the cloud stay where they are, but Terraform no longer knows about them and will try to create them again. That's why state is kept safe and backed up.

Common mistakes

What you'll need for the tasks

The labs work without a cloud and without Docker: the resources in them are files created by the hashicorp/local provider (the local_file resource, the filename and content arguments). You need Terraform 1.4 or newer, or OpenTofu.

You'll find useful:

If init can't download the provider (a download error or a 403 from Russia), set up the Yandex Cloud provider mirror following their instructions: Yandex Cloud: Terraform provider mirror.

In upcoming lessons we'll cover GitOps, where code like this gets applied automatically from git.

Done reading?

Mark the lesson as read to move your track progress forward.

Go through it with an AI agent

The agent explains anything unclear in plain words, gives you 2 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.

Get access for $49

Tell the agent: use MCP izi, give me lesson iac-basics-02-terraform-basics