Table of Contents
Why Terraform Matters
Infrastructure as Code lets you describe servers, networks, and services in files instead of clicking in web consoles. Terraform is one of the most widely used tools for this. It focuses on creating and managing infrastructure across many providers such as AWS, Azure, GCP, and others.
Terraform is declarative. You write what you want the final infrastructure to look like, and Terraform figures out how to create, change, or remove real resources to match that description. This is different from writing shell scripts that describe step by step how to build everything.
In this chapter you will focus on the core ideas that make Terraform work. Details about providers, resources, and modules are covered later, so here you concentrate on what is specific to Terraform itself and how its workflow and language are organized.
The Terraform Workflow
Terraform follows a very regular workflow. Almost every session with Terraform, from a tiny test to a real production change, uses the same core commands.
You start by writing or editing configuration files, usually with the .tf extension. These files are plain text, normally checked into version control.
Before Terraform can talk to any cloud, you run terraform init in the directory with your configuration. This downloads the required plugins for the providers you will be using and prepares the local working directory. You only need to run it again when you add or change providers or modules.
Once initialization is done, you check what Terraform intends to do with terraform plan. This command reads your configuration and compares it to what already exists in the target environment, then prints an execution plan. The plan explains which resources will be created, changed, or destroyed. It does not modify real infrastructure.
If the plan output looks correct, you apply it with terraform apply. Terraform will prompt you to confirm, then it will perform the actions needed to make reality match the configuration. As it runs, it prints progress messages and, when finished, shows which resources were added, changed, or destroyed.
If you later remove a resource from the configuration or no longer need an entire stack, you can use terraform destroy. This command is similar to apply, but its focus is to remove all managed resources. In practice, many teams prefer to remove resources from configuration and let terraform apply handle the deletions to keep everything driven by code.
Terraform never changes infrastructure silently. You should always review a terraform plan before running terraform apply, especially in shared or production environments.
The Terraform Execution Model
Terraform relies on a few key concepts to manage infrastructure safely and predictably. The most important ones are desired state, the state file, and the dependency graph.
The desired state is whatever you write in your .tf files. This is the target picture of how your infrastructure should look. It includes which resources exist, which arguments they have, and how they relate to each other.
Terraform keeps track of the real world through a state file, usually called terraform.tfstate. This file records what resources Terraform believes exist and their attributes. When you change configuration, Terraform compares desired state from the files with the current state in the state file and with the actual provider. The result is the execution plan.
Terraform automatically builds a dependency graph between resources. If one resource uses the output or identifier of another, Terraform knows that the second depends on the first. It will create resources in the right order and also destroy or update them in an order that respects those relationships. In most configurations you do not specify the order explicitly. Terraform infers it from references in the configuration.
Because the state file is central, it must be treated carefully. In single user experiments it can live locally. In team environments it is usually stored remotely in a shared backend so that everyone works from the same state.
Never manually edit the Terraform state file unless you fully understand the risks. Corrupted or inconsistent state can lead to unexpected changes or deletions in real infrastructure.
Terraform Configuration Language Basics
Terraform uses its own configuration language, commonly called HCL (HashiCorp Configuration Language). This language is built around blocks, arguments, and expressions.
A block defines a logical object. Common block types are terraform, provider, resource, variable, and output. Each block has a type, optional labels, and a body enclosed in curly braces.
Inside a block you set arguments. Each argument assigns a value to a named property. Values can be simple like strings or numbers, or they can be more complex structures like lists and maps.
Expressions let you reference other values or compute derived values. They can include function calls, arithmetic, conditionals, and resource references. References usually use the format block_type.block_name.attribute, and Terraform uses these references to determine dependencies.
The HCL syntax is designed to be human readable. Strings use quotes, lists use square brackets, and maps use curly braces with key value pairs. When you run terraform fmt, Terraform automatically formats your files to a standard style, which keeps configuration easier to read and review.
Core Terraform Blocks
Every practical Terraform configuration includes a few core block types that define global behavior and basic structure. These are different from provider specific resource definitions and from higher level modules.
The terraform block controls Terraform itself. In it you can specify the required Terraform version and the external providers along with their required versions. You can also define where the state is stored by configuring a backend here, though some backends use a separate configuration.
Providers represent external platforms and APIs, such as cloud vendors. A provider block configures authentication and default settings for a specific provider. Terraform downloads provider plugins during initialization based on what is declared.
Variables are defined with variable blocks. These declare input values that can be set from the command line, environment variables, or separate files. Inside the block you can specify type constraints, default values, and documentation text. This keeps your configuration flexible and reusable without hardcoding environment specific details.
Outputs are defined with output blocks. They publish specific values from the configuration that you may want to see after an apply or feed into other systems. Outputs often include IP addresses, URLs, or identifiers that are needed by users or automation.
Terraform State and Backends
The Terraform state file is how Terraform remembers what it manages. By default it lives locally in the directory where you run Terraform. For personal use or testing this is usually enough.
In shared environments the state must be accessible to all team members and protected from concurrent writes. Terraform solves this with backends. A backend defines where state is stored and how it is accessed. Common backends include remote storage on object stores or specialized state services.
When you switch from a local backend to a remote one, Terraform can migrate the existing state. After migration, commands like plan and apply automatically read and write from the configured backend. Some backends also provide state locking to prevent two people or systems from running conflicting operations at the same time.
From the perspective of Terraform usage, the backend is mostly invisible after configuration. You still run the same commands in the same directory, but state operations go through the backend instead of the local file.
Plans, Changes, and Idempotence
Terraform emphasizes predictable changes. Each terraform plan explains the actions in a structured way, often grouped by resource. It shows resources that will be added, changed in place, or destroyed. It also indicates when a change will cause a resource to be replaced entirely rather than updated.
Terraform strives for idempotence. This means that if you apply the same configuration multiple times with no changes, the later runs should see no changes required. As long as providers behave consistently and external changes are not made behind Terraform’s back, repeated applies keep the system stable.
When the real environment is changed outside of Terraform, such as manually editing a cloud resource in the provider console, Terraform will detect the drift the next time you run a plan. Depending on your configuration, Terraform may then plan to overwrite those manual changes or adapt its state to match. It is a good practice to keep Terraform as the single source of truth for managed resources and avoid manual adjustments.
Working Safely with Terraform
Using Terraform in a safe and maintainable way requires some discipline. Configuration files should be stored in version control, with meaningful commit messages so that changes to infrastructure are auditable and reviewable.
Running terraform fmt keeps configuration consistent, and terraform validate can check for many syntax and structural issues before you ever talk to a provider. These commands are quick checks that can be integrated into automated pipelines.
In team environments it is common to have a review process for Terraform changes. A typical pattern is to have automated systems run terraform plan and post the result for human review, then after approval have an automated terraform apply. This strengthens the link between code review and actual infrastructure changes.
Always treat Terraform configuration and state with the same care as application source code. Unreviewed changes or careless handling of state can disrupt running systems or cause data loss.
Summary of Fundamental Ideas
Terraform is a tool that turns infrastructure into code, using a declarative language and a predictable workflow. You write configuration files in HCL, initialize providers, create plans, and apply them to bring real resources into alignment with what you have described.
The state file and backend system let Terraform remember what it manages, while the dependency graph and planning phase ensure that changes are applied in the right order and with clear visibility. With these fundamentals in place, you can later build more structured and reusable Terraform setups using providers, modules, and more advanced patterns covered in subsequent chapters.