Initialize a working directory
What terraform init sets up
The first thing you do with any new or freshly cloned Terraform configuration is run terraform init[1]: until it finishes, terraform plan and terraform apply have no backend, no modules, and no provider plugins to work with. init runs three setup steps in a fixed order and records what it installed, and this section covers what those steps install, where the files land, and why re-running init is always safe.
The three setup steps
init prepares the working directory by running these steps in order[1]:
- Initialize the backend. init reads the backend configuration from the root module and initializes the chosen backend, the place your state is stored.
- Install child modules. init searches the configuration for
moduleblocks and retrieves each module from the location in itssourceargument. - Install provider plugins. Providers ship separately from Terraform as plugins, so init finds every provider the configuration references and downloads its plugin.
Everything lands in a local .terraform directory that init creates to hold the installed plugins, modules, and backend settings. You can relocate that directory with the TF_DATA_DIR environment variable.
Safe to run again
init is safe to run multiple times[1]. Re-running it brings the directory up to date with configuration changes, installing newly added modules and providers, and it never deletes your existing configuration or state. That is why the fix for most not-installed provider or module errors is simply to run init again. init only prepares the directory: it never provisions, changes, or destroys infrastructure, so a stray init cannot harm running resources.
Providers, the lock file, and -upgrade
A plain terraform init[1] does not chase the newest provider releases, and that is deliberate: reproducible runs depend on every init selecting the same versions. Terraform reaches that goal with the dependency lock file.
The dependency lock file
After it installs providers, init records the exact provider versions it selected, along with their checksums[2], in a file named .terraform.lock.hcl in the working directory. Commit this file to version control[2] so teammates and CI select exactly the same provider versions on their next init, and so version changes show up in code review. When the file is present, a later init re-selects the recorded version even if a newer one now exists.
Forcing an upgrade
To move off the locked versions, run terraform init -upgrade[1]. It ignores the selections in the lock file, installs the newest version of each provider that the configuration's version constraints still allow, and rewrites the lock file with the new versions and checksums. Run it deliberately rather than on every init, so an upgrade is a reviewed change instead of an accident.
Where plugins come from
By default init downloads each provider into the working directory's .terraform directory, so every configuration keeps its own copy. Two settings change that source:
terraform init -plugin-dir=PATHforces init to read plugins only from the given directory, as if it were afilesystem_mirror[1] in the CLI configuration. It bypasses the registry and every other install method, which is how you install providers in an air-gapped environment.- Setting the
TF_PLUGIN_CACHE_DIR[3] environment variable, orplugin_cache_dirin the CLI config, gives Terraform a shared plugin cache so each plugin binary is downloaded once and reused across working directories.
Re-initializing the backend: migrate, reconfigure, skip
Change the backend block and your next command stops with a backend-changed error until you re-run terraform init[1]. Re-running init is the only way to apply a backend change; no terraform state subcommand does it. What happens to your existing state then comes down to one flag.
Migrate, reconfigure, or skip
terraform init -migrate-state[1] attempts to copy your existing state into the newly configured backend, prompting for confirmation when workspace states are involved. Use it when you are moving real state from one backend to another, for example from local to S3.terraform init -reconfigure[1] disregards the existing backend configuration and initializes from scratch, which explicitly prevents migration: no state is copied. Use it to point the directory at a fresh, empty backend.terraform init -backend=false[1] skips backend initialization entirely. It is intended for a directory that was already initialized for a backend, letting you reinstall plugins and modules, for example to runterraform validatein CI, without touching remote state.
The distinction that trips people up is migrate versus reconfigure: -migrate-state copies the state into the new backend, while -reconfigure throws away the old backend configuration and copies nothing. Reach for -reconfigure only when you do not want the old state carried over.
| Flag | Backend initialized | Existing state |
|---|---|---|
plain init | Yes | Prompts to migrate if the backend changed |
-migrate-state | Yes | Copied into the new backend |
-reconfigure | Yes | Not copied; old config discarded |
-backend=false | No | Untouched; backend step skipped |
Partial backend configuration
You do not have to hard-code every backend setting. terraform init -backend-config=...[1] supplies backend settings at init time, from a file or as key=value pairs, which keeps dynamic or sensitive values such as credentials out of the committed configuration.
Configuration loading and exam-pattern recognition
Before init can install anything, it has to know what the configuration declares, and that means loading every file in the directory. Terraform treats the whole module as a single document[4]: it reads and merges all .tf and .tf.json files in the working directory, and because the language is declarative, the order it reads them in has no effect on behavior. Splitting code across main.tf, variables.tf, and outputs.tf is purely a convention for readers[4]; those names are not required, and Terraform does not run files top to bottom.
Two boundaries qualify that flat merge:
- Nested directories are separate modules. A module is only the top-level files in a directory; subdirectories are not automatically included[4] and load only when a
moduleblock calls them. - Override files are applied last. Any file named
override.tf,override.tf.json, or ending in_override.tfor_override.tf.jsonis skipped on the first pass and processed last[5], merging its blocks over the matching already-defined blocks. Use them sparingly, because they hide changes from the reader.
Exam-pattern recognition
- A stem shows a run that failed because a provider or module is not installed, or a freshly cloned repository that has never been initialized. The answer is
terraform init, notplan,apply, orrefresh: init is the only command that installs dependencies. - A stem wants newer provider versions, but a plain init keeps installing the old ones. The lock file is pinning them, so the answer is
terraform init -upgrade. - A stem changes the backend and asks how to keep the existing state. The answer is to re-run
terraform init -migrate-state; distractors offerterraform state mv(which moves individual resources, not backends) or-reconfigure(which discards the old backend without copying state). - A stem claims filenames like
main.tfare required, or that files run in order. Both are false: Terraform merges all.tffiles regardless of name or order, and override files are the only ones applied last.
How each init invocation treats the backend and state
| Behavior | plain init | -migrate-state | -reconfigure | -backend=false |
|---|---|---|---|---|
| Initializes the backend | Yes | Yes | Yes | No |
| Copies existing state to the new backend | Prompts if the backend changed | Yes, copies it | No, discards old config | Not applicable |
| Installs providers and modules | Yes | Yes | Yes | Yes |
| Typical trigger | First init or new dependency | Moving state to a new backend | Re-init backend without migrating | Install plugins to validate only |
Decision tree
Sharp facts the exam loves — give these one last read before exam day.
Cheat sheet
Sharp facts the exam loves — scan these before test day.
- init performs backend, module, and provider setup
terraform init initializes a working directory by initializing the backend, downloading and installing child modules referenced in module blocks, and installing the provider plugins the configuration requires.
Trap Thinking init applies changes or refreshes state; init only prepares the working directory and never provisions infrastructure.
10 questions test this
- A small configuration has no `backend` block, so it uses Terraform's default local backend, and it requires only the `aws` provider. A developer argues that because there is no remote backend to confi
- You clone a repository whose root module both references child modules in `module` blocks and requires several providers. Because there is no `.terraform` directory yet, `terraform plan` fails immedia
- An engineer adds a new `aws` provider to the `required_providers` block of a configuration that was already initialized earlier. Running `terraform plan` now fails with a message stating that the prov
- Before the first `terraform plan`, an engineer must download the child modules referenced in `module` blocks and install the provider plugins the configuration requires. A colleague suggests running `
- A developer new to Terraform assumes the `aws` provider ships built into the Terraform CLI binary, so they expect no network access to be needed the first time they run `terraform init` on a configura
- A platform engineer has just cloned a Terraform repository whose root configuration declares several `required_providers`, references two child modules in `module` blocks, and specifies an `s3` backen
- Two engineers collaborate on a Terraform configuration through a shared Git repository, but the `.terraform` directory is excluded from version control. When the second engineer clones the repository,
- During a code review, a colleague asks what is actually stored inside the `.terraform` directory that `terraform init` created in the project root. Which answer correctly describes its contents?
- A teammate who is new to Terraform believes that running `terraform init` on a fresh configuration will begin creating the cloud resources it declares. You correct them before they run it. Which state
- An engineer clones a Terraform repository and runs `terraform init` successfully. Before running any other command, they assume init has already reconciled the Terraform state with the live remote obj
- init is the first command and creates .terraform
terraform init is the first command run against a new or cloned configuration; it creates the local .terraform directory used to store backend configuration, provider plugins, and modules.
5 questions test this
- A small configuration has no `backend` block, so it uses Terraform's default local backend, and it requires only the `aws` provider. A developer argues that because there is no remote backend to confi
- Before the first `terraform plan`, an engineer must download the child modules referenced in `module` blocks and install the provider plugins the configuration requires. A colleague suggests running `
- A platform engineer has just cloned a Terraform repository whose root configuration declares several `required_providers`, references two child modules in `module` blocks, and specifies an `s3` backen
- Two engineers collaborate on a Terraform configuration through a shared Git repository, but the `.terraform` directory is excluded from version control. When the second engineer clones the repository,
- During a code review, a colleague asks what is actually stored inside the `.terraform` directory that `terraform init` created in the project root. Which answer correctly describes its contents?
- init is safe to run multiple times
terraform init is idempotent and safe to run repeatedly; it installs newly added modules and providers but never deletes existing configuration or state.
- -upgrade ignores locked provider selections
terraform init -upgrade upgrades all previously selected providers to the newest versions allowed by the configuration's version constraints, ignoring the versions recorded in the dependency lock file.
Trap Believing a plain terraform init upgrades providers; without -upgrade it honors the versions already recorded in .terraform.lock.hcl.
8 questions test this
- Your configuration pins a provider with `version = "~> 5.0"`, the lock file records 5.10.0, and both 5.40.0 and a brand-new 6.0.0 have been published. A colleague runs `terraform init -upgrade`, expec
- Before running `terraform init -upgrade`, an engineer worries it will jump every provider to the absolute latest release on the registry, including a new major version that could break the configurati
- A platform engineer runs `terraform init -upgrade` and Terraform selects newer versions for two of the required providers. Before this command, `.terraform.lock.hcl` recorded the older versions and wa
- Your configuration constrains the aws provider with `version = "~> 5.0"`, and `.terraform.lock.hcl` currently records 5.20.0, even though 5.40.0 has since been released. A teammate runs `terraform ini
- Your team widened the version constraint on the `aws` provider in `required_providers` so a newer release is now permitted, but a plain `terraform init` still installed the version already recorded in
- Weeks after your last initialization, several of the providers required by your configuration have published newer patch releases that still satisfy your existing version constraints. You run a plain
- An engineer wants to move the team onto a newer release of the Terraform CLI itself and assumes that running `terraform init -upgrade` will download and install that newer Terraform binary. In terms o
- A configuration requires three providers, all with versions currently recorded in `.terraform.lock.hcl`. Only one of them has published a newer release within the existing constraints. An engineer run
- init writes the dependency lock file
During provider installation terraform init records the selected provider versions and checksums in .terraform.lock.hcl, which should be committed to version control so future inits select the same versions.
12 questions test this
- Before running `terraform init -upgrade`, an engineer worries it will jump every provider to the absolute latest release on the registry, including a new major version that could break the configurati
- A developer opening a pull request proposes adding `.terraform.lock.hcl` to the project's `.gitignore`, arguing it is just a machine-generated artifact. Following HashiCorp's guidance for the dependen
- Two engineers work from the same Git repository. The first runs `terraform init`, commits the resulting `.terraform.lock.hcl`, and pushes. The second engineer pulls the repo and runs `terraform init`
- A new engineer notices `.terraform.lock.hcl` sitting in the root of the working directory next to the `.tf` files, while the downloaded provider binaries live under the `.terraform` subdirectory. They
- A platform engineer runs `terraform init -upgrade` and Terraform selects newer versions for two of the required providers. Before this command, `.terraform.lock.hcl` recorded the older versions and wa
- Your configuration constrains the aws provider with `version = "~> 5.0"`, and `.terraform.lock.hcl` currently records 5.20.0, even though 5.40.0 has since been released. A teammate runs `terraform ini
- Your team widened the version constraint on the `aws` provider in `required_providers` so a newer release is now permitted, but a plain `terraform init` still installed the version already recorded in
- Weeks after your last initialization, several of the providers required by your configuration have published newer patch releases that still satisfy your existing version constraints. You run a plain
- A developer initializes a brand-new Terraform project for the first time by running `terraform init`. Provider installation completes successfully, and a `.terraform.lock.hcl` file now appears in the
- Your team relies on `.terraform.lock.hcl` to keep deployments reproducible across environments. A teammate assumes the lock file also pins the versions of the remote registry modules your configuratio
- During a code review, a teammate claims that `terraform plan` and `terraform apply` refresh `.terraform.lock.hcl` whenever providers change. You want to correct this misconception about which command
- You add a brand-new provider to the `required_providers` block of an already-initialized configuration and then run `terraform plan`, which stops with an error saying the provider is not installed. Wh
- -plugin-dir forces a local plugin source
terraform init -plugin-dir=PATH forces Terraform to read provider plugins only from the specified directory, bypassing the registry and other install methods.
- init -migrate-state moves state to a new backend
After changing the backend configuration, you re-run terraform init; the -migrate-state option copies the existing state file into the newly configured backend.
Trap Reaching for terraform state mv, push, or refresh to move to a new backend; backend migration is done by re-running terraform init.
7 questions test this
- You are reinitializing a working directory against a replacement backend, and you explicitly do NOT want Terraform to copy the current state into it — the new backend should start empty and the previo
- A working directory holds three CLI workspaces (default, staging, and prod), each with its own state, all persisted in one remote backend. The team edits the `backend` block to point at a different re
- An automated pipeline must migrate Terraform state to a newly configured backend without any human at the keyboard: the run must copy the existing state and answer 'yes' to every migration confirmatio
- An engineer is repointing a working directory from an old remote backend to a brand-new, empty one. The previous state is obsolete and must NOT be carried over — the team wants Terraform to initialize
- An engineer has changed a project's backend settings several times, and each `terraform init` keeps reconciling against the backend configuration Terraform cached during an earlier initialization. The
- To move a project's state from an old remote backend into a newly configured one, one teammate proposes running `terraform state mv` for each resource and another proposes `terraform refresh`; in test
- A colleague asks you to spell out the practical difference between `terraform init -migrate-state` and `terraform init -reconfigure` when they are pointing a working directory at a different backend.
- -reconfigure skips state migration
terraform init -reconfigure disregards any existing backend configuration and reinitializes from scratch, explicitly preventing migration of existing state to the new backend.
Trap Confusing -reconfigure with -migrate-state; -reconfigure discards the old backend without copying state, while -migrate-state copies it.
8 questions test this
- An engineer edits the `backend` block to target a new remote backend and runs `terraform init -reconfigure`, assuming the current state will be carried over. After it completes, the new backend is emp
- You are reinitializing a working directory against a replacement backend, and you explicitly do NOT want Terraform to copy the current state into it — the new backend should start empty and the previo
- A working directory holds three CLI workspaces (default, staging, and prod), each with its own state, all persisted in one remote backend. The team edits the `backend` block to point at a different re
- An automated pipeline must migrate Terraform state to a newly configured backend without any human at the keyboard: the run must copy the existing state and answer 'yes' to every migration confirmatio
- An engineer is repointing a working directory from an old remote backend to a brand-new, empty one. The previous state is obsolete and must NOT be carried over — the team wants Terraform to initialize
- An engineer has changed a project's backend settings several times, and each `terraform init` keeps reconciling against the backend configuration Terraform cached during an earlier initialization. The
- To move a project's state from an old remote backend into a newly configured one, one teammate proposes running `terraform state mv` for each resource and another proposes `terraform refresh`; in test
- A colleague asks you to spell out the practical difference between `terraform init -migrate-state` and `terraform init -reconfigure` when they are pointing a working directory at a different backend.
- -backend=false initializes without a backend
terraform init -backend=false skips backend initialization, which is useful for installing plugins and modules to validate a configuration without accessing remote state.
- Terraform merges all .tf/.tf.json files order-independently
Terraform loads and merges every .tf and .tf.json file in the working (top-level) directory into a single configuration, evaluating the whole module as one document; nested directories are treated as separate modules and are not auto-loaded. Because the language is declarative, the order in which files are loaded does not matter, so splitting code across main.tf, variables.tf, and outputs.tf is purely an organizational convention with no effect on behavior.
Trap Assuming filenames like main.tf/variables.tf/outputs.tf are required or that Terraform executes files in a top-to-bottom or filename order — it does not. The lone exception to flat merging is override files (override.tf, override.tf.json, or any *_override.tf / *_override.tf.json), which are loaded last and merged in to override matching blocks.
6 questions test this
- A base file declares `resource "aws_instance" "app"` with `instance_type = "t3.micro"` and several other arguments. A separate `app_override.tf` in the same directory declares the same resource with o
- A team keeps its canonical infrastructure in `main.tf` and wants to override one argument on a single resource for a quick experiment. A developer adds a file named exactly `override.tf`, with no pref
- A team generates part of its configuration programmatically and writes it to a file named `generated.tf.json`, keeping their hand-written resources in ordinary `.tf` files in the same directory. A col
- To tidy a growing root module, an engineer moves the networking resources into a new `./networking` subdirectory inside the same working directory, leaving the remaining `.tf` files at the top level.
- While refactoring a working directory, a platform engineer copies a `resource "aws_s3_bucket" "logs"` block from `storage.tf` into a second file, `buckets.tf`, so both files now declare that same reso
- A directory contains a base `main.tf` plus two override files, `a_override.tf` and `b_override.tf`, that set the same argument on the same resource to different values. In what order does Terraform ap