The Core Workflow: init, plan, apply, destroy

Every interaction with OpenTofu uses the same handful of commands. Here they are, in the order you use them.

tofu init

Prepares a working directory. It downloads the providers (the AWS plugin), downloads any modules, and connects to the backend (the S3 state bucket). You run it once per directory, and again whenever you add a provider or module.

In this repo: make init DIR=envs/dev runs tofu -chdir=envs/dev init -backend-config=backend.s3.tfbackend.

-chdir=DIR tells OpenTofu which directory (which "root") to operate on.

tofu plan

The dry run. It compares your code to current reality and prints exactly what it would create, change, or destroy - without doing anything. Always read the plan before applying. Symbols:

  • + create
  • ~ change in place
  • - destroy
  • -/+ destroy and recreate

In this repo: make plan DIR=envs/dev.

tofu apply

Actually makes the changes. By default it shows the plan again and asks for confirmation. In the CI pipeline, apply consumes a saved plan file so it applies exactly what was reviewed (see section 13).

In this repo, make apply exists but is labeled break-glass only - normally every environment is applied by the pipeline, not from a laptop.

tofu destroy

Tears down everything the root manages. Used for teardown; the bootstrap state bucket is deliberately protected against accidental deletion (force_destroy = false).

tofu fmt and tofu validate

  • fmt auto-formats .tf files to canonical style (indentation, alignment). make fmt runs it across the whole repo.
  • validate checks the configuration is internally valid (syntax, references, types) without contacting AWS. make validate runs a backend-less init + validate on every root, so it works with no AWS credentials - ideal as a pre-commit check.

A "root" vs a "module"

A root (or root module) is a directory you actually run tofu in - it has a backend and provider config. This repo's roots are: bootstrap/, platform/, cicd/, and envs/dev|staging|prod/. A (child) module is a directory under modules/ that roots call but you never run directly. The Makefile lists the roots explicitly:

MAKE
ROOTS := bootstrap platform cicd envs/dev envs/staging envs/prod
Adesh Tamrakar
SOFTWARE ENGINEER · VAULT

Notes, insights and random discoveries from a working engineer's vault - written for future me, published for you.