Terraform has established itself as the definitive standard for Infrastructure as Code, bridging the gap between declarative configuration and actual cloud resource provisioning. For engineers, DevOps specialists, and systems administrators, proficiency with the terraform init, terraform plan, and terraform apply commands is not merely beneficial; it is fundamental to working with the platform effectively. These three commands form the pillars of every standard Terraform workflow, governing how a local working directory is prepared, how changes are previewed, and how those changes are ultimately executed against remote infrastructure. Understanding the precise mechanics of each stage, including how state is managed, how plans are generated, and how dependencies are resolved, allows practitioners to deploy infrastructure with precision, minimize the risk of configuration drift, and automate complex cloud environments. This article provides a deep technical analysis of these core commands, examining their internal processes, command-line flags, safety mechanisms, and their integration into production-grade workflows.
The Foundation: terraform init
The terraform init command serves as the critical entry point for any Terraform operation. It initializes a Terraform working directory, preparing it for the execution of subsequent commands such as terraform plan and terraform apply. Without a properly initialized workspace, Terraform cannot function, as it lacks the necessary plugins, modules, and backend configuration to operate. When a user executes this command, Terraform performs a series of essential preparation steps to ensure the local environment is aligned with the configuration files present in the directory.
The process of initialization involves three primary actions. First, it handles backend initialization. Terraform supports various state backends, ranging from local file storage to remote cloud services like S3, GCS, or Azure Blob Storage. The init command configures the backend specified in the configuration files, ensuring that Terraform knows where to read and write its state data. Second, it performs child module installation. If the configuration references remote modules from the Terraform Registry or other module sources, init downloads these modules into the local directory, making their resources and variables available for use. Third, it executes plugin installation. This is arguably the most complex part of the initialization process, as it involves downloading and unpacking provider plugins. These providers act as the bridge between Terraform's core engine and specific cloud providers (such as AWS, Azure, or GCP).
Terraform creates a hidden directory named .terraform within the working directory to store these downloaded components. Additionally, it generates a lock file, typically named .terraform.lock.hcl. This lock file is crucial for version pinning. It records the exact versions of the providers and modules used during the initialization. This ensures consistency across different environments, such as a developer's laptop, a build server, and a production CI/CD pipeline. By locking versions, teams can prevent unexpected behavior caused by new provider releases introducing breaking changes or different behaviors.
When to Run terraform init
While init appears to be a simple setup step, it must be run under specific circumstances to maintain workflow integrity. It is mandatory when setting up a new project for the first time. However, it is also required when the configuration changes in specific ways. If a developer adds a new provider to the configuration, or references a new module, init must be re-run to download the new dependencies. Similarly, if the backend configuration is modified—such as switching from a local backend to a remote S3 bucket—init is required to reconfigure the state storage location.
Another common scenario is after pulling changes from a version control system. If a team member updates the provider versions in their code and commits these changes, other team members who pull this code must run init to update their local environment to match the new requirements. Failure to do so can result in errors where Terraform complains about missing providers or mismatched versions. The command also supports various flags to customize its behavior, such as -backend-config to pass dynamic parameters to the backend, or -upgrade to check for newer versions of providers. However, in most standard workflows, running terraform init without flags is sufficient to ensure the directory is ready for execution.
The Preview Phase: terraform plan
Once the working directory is initialized, the next step is to understand what Terraform intends to do. The terraform plan command creates an execution plan showing what Terraform will do. It acts as a dry run, analyzing the desired state defined in the configuration files against the actual state of the infrastructure. This command is indispensable for verifying changes before any modification is made to the real-world environment. It allows users to preview the impact of their configuration changes, identify potential errors, and ensure that the proposed actions align with their intentions.
The execution of terraform plan involves a multi-step analysis process. First, Terraform reads the current state from the configured backend. This state file contains metadata about all resources Terraform is managing, including their attributes, dependencies, and unique identifiers. Second, it reads the configuration files defined in the working directory. These files define the desired state of the infrastructure, specifying resources, parameters, and relationships. Third, Terraform refreshes the resource state from the cloud provider's APIs. This refresh is critical because the local state file may become outdated if someone or something modified the infrastructure outside of Terraform. By querying the actual cloud resources, Terraform updates its understanding of the current reality.
Finally, Terraform calculates the diff between the desired state (configuration) and the actual state (refreshed from APIs). Based on this diff, it generates a plan that lists the specific operations required to reconcile the two states. The output of this command provides a clear, human-readable summary of the changes. Each line indicates a specific action for a resource, using symbolic notation to denote the type of operation.
Interpreting Plan Output
The output of terraform plan is structured to be easily interpretable. Users should pay close attention to the symbols prefixed to each resource change.
+indicates that a new resource will be created.~indicates that an existing resource will be updated in-place.-indicates that a resource will be destroyed.-/+indicates that a resource must be replaced, which involves destroying the existing resource and then creating a new one. This often happens when a critical attribute, such as the instance type of an AWS EC2 instance or the region of a resource, is changed.
For example, a plan might show + aws_instance.web_server will be created and ~ aws_instance.web_server will be updated. The + symbol means Terraform will add these resources. Always reviewing the plan carefully is a best practice to avoid unexpected changes, such as unintended deletions or replacements. This step is particularly crucial in shared environments where multiple developers are working on the same infrastructure. A mistake in one developer's configuration could lead to the destruction of resources used by another service. The terraform plan command allows this risk to be mitigated before the apply stage.
Execution and Automation: terraform apply
The terraform apply command executes the operations proposed in a Terraform plan. It is the command that actually makes changes to the infrastructure. It can operate in two distinct modes, depending on whether a saved plan file is provided as an argument.
Mode 1: Without a Saved Plan File
When terraform apply is run without passing a saved plan file, Terraform automatically creates a new execution plan as if the user had just run terraform plan. It then prompts the user to approve that plan. Once approved, it performs the indicated operations. This is the standard interactive workflow for most manual deployments.
The command-line syntax is:
bash
terraform apply [options] [plan file]
In this mode, users can use all of the planning modes and planning options available in terraform plan to customize how Terraform creates the plan. This includes flags such as -var and -var-file to specify input variables, or -target to limit the scope of changes. The process is interactive by default. Terraform displays the plan and asks:
```text
Do you want to perform these actions?
Only 'yes' will be accepted to approve.
Enter a value:
```
The user must explicitly type yes to proceed. This safety mechanism prevents accidental execution of commands if a user simply hits enter or if a script is run without proper safeguards.
Mode 2: With a Saved Plan File
In automation scenarios, such as CI/CD pipelines, it is common to separate the planning and applying phases. A user or a script can run terraform plan with the -out flag to save the plan to a file (e.g., terraform plan -out=main.plan). Later, terraform apply can be run with this specific file as an argument:
bash
terraform apply main.plan
When a saved plan file is passed, Terraform performs the operations contained in that plan without prompting for confirmation. The act of passing the plan file is interpreted by Terraform as the approval to execute. This two-step workflow is recommended for automation because it ensures that the plan being executed is the exact one that was reviewed and approved. It also allows for a time gap between planning and applying, which can be useful if manual approval of the plan is required before deployment.
It is important to note that when using a saved plan file, you cannot specify any additional planning modes or options. Options like -destroy or -var only affect Terraform's decisions about which actions to take during the planning phase. Since the plan file contains the final results of those decisions, passing options to apply when using a saved plan is not permitted. To inspect a saved plan file before applying it, users can use the terraform show command followed by the plan file name.
The -auto-approve Flag
For non-interactive sessions or when running terraform apply without a saved plan file in a script, the -auto-approve flag is essential. This option instructs Terraform to apply the plan without asking for confirmation. It skips the interactive approval prompt.
bash
terraform apply -auto-approve
While this flag is useful for automation, it comes with significant risks. If -auto-approve is used, it is strongly recommended to ensure that no one can change the infrastructure outside of the Terraform workflow. If manual changes are made directly in the cloud console or via another tool, Terraform may attempt to revert them or perform unexpected operations to align the actual state with the desired state. This can lead to unpredictable changes and configuration drift. Therefore, strict access controls and process discipline are required when using this flag.
State Management and Data Integrity
Central to the functionality of init, plan, and apply is the Terraform state file. State data files store information about infrastructure and configuration, mapping real-world resources to user-defined settings. Whenever terraform init, terraform plan, or terraform apply is run, the saved state is referenced and updated with the latest resources and configuration at the end of the execution process.
The state file is stored in JSON format. This format allows the state to be version-controlled and shared among team members if necessary. However, because the state file contains sensitive information, such as resource IDs and potentially secrets, it is generally not recommended to commit the local state file to version control. Instead, remote backends are preferred for production environments.
State files facilitate change tracking, performance optimization for large infrastructures, and resource management. By maintaining an accurate map of resources, Terraform can efficiently determine which resources need to be created, updated, or destroyed. It also uses the state to establish dependencies between resources. For example, Terraform knows that a security group must be created before an instance that references it can be launched.
Despite the convenience of the state file, it is not recommended to manipulate it manually. Manually editing the JSON state file is dangerous and can lead to an inconsistent state where Terraform's understanding of the infrastructure diverges from reality. The best practice when adding new resources is to make the corresponding changes to the configuration files and follow the standard workflow: init, plan, and apply. There are special cases where manipulating the state file is necessary, such as importing existing infrastructure that was not created by Terraform. This is done using the terraform import command, which updates the state file to track the existing resource. However, for most daily operations, adhering to the configuration-driven workflow is the safest and most reliable approach.
Comparison of Core Commands
The following table summarizes the key differences between init, plan, and apply to provide a quick reference for engineers.
| Feature | terraform init | terraform plan | terraform apply |
|---|---|---|---|
| Primary Purpose | Initialize working directory, install plugins/modules | Preview changes, calculate diff | Execute changes, provision resources |
| State Interaction | Configures backend, does not modify resource state | Reads state, refreshes from cloud APIs, does not write final state | Reads state, writes new state after execution |
| Cloud API Calls | Minimal (version checks) | Yes (refreshes resource state) | Yes (creates/updates/deletes resources) |
| Output | Success/Failure message, lock file | Human-readable plan with symbols | Execution log, outputs, updated state |
| Destructive Potential | Low (setup only) | None (read-only) | High (creates, updates, destroys) |
| Key Flags | -backend-config, -upgrade |
-out, -destroy, -target |
-auto-approve, -var, -replace |
| Usage in CI/CD | Required for new environments | Used to generate plan files for review | Used to execute approved plans |
Command-Line Options and Flags
Both plan and apply support a variety of flags to customize their behavior. Understanding these flags is critical for advanced use cases.
Planning Modes and Options
When running terraform apply without a saved plan file, it supports all planning modes and options available in terraform plan.
-destroy: Creates a plan to destroy all remote objects managed by the state file. This is the command used to tear down an entire environment.-refresh-only: Creates a plan to update Terraform state and root module output values without making any infrastructure changes. This is useful for syncing the state file after manual changes.-target: Limits the scope of the plan to specific resource instances. This allows for partial updates in large infrastructures.-replace: Specifies which resource instances Terraform should replace.-varand-var-file: Used to specify input variables. These allow dynamic configuration of resources based on deployment environments.
Execution Options
The following options change how the apply command executes and reports on the operation.
-auto-approve: Skips interactive approval of the plan before applying. As noted, this option is ignored when a previously saved plan file is passed, as the file passing itself implies approval.-compact-warnings: Shows any warning messages in a compact form, including only summary messages, unless the warnings are accompanied by at least one error. This is useful for reducing log noise in automated systems.-input=false: Disables all of Terraform's interactive prompts. This is mandatory for automated scripts that cannot accept user input. If a prompt is required and-input=falseis set, the command will fail rather than waiting indefinitely.
Production Workflows and Best Practices
In a production environment, the workflow is rarely just a simple sequence of three commands on a single machine. It is typically integrated into a CI/CD pipeline. A robust production workflow often involves:
- Code Commit and Validation: When code is pushed to a repository, the CI system runs
terraform fmtto check formatting andterraform validateto check for syntax errors. - Initialization and Planning: The CI system runs
terraform initto set up the backend and providers. It then runsterraform planwith the-outflag to save the plan to a file. This plan file is stored and can be reviewed by team leads. - Approval: In highly regulated environments, the plan file is reviewed and approved manually or via an automated policy engine before proceeding.
- Application: Once approved, the CI system runs
terraform applywith the saved plan file. This ensures that the exact changes that were reviewed are the ones that are executed.
This separation of concerns ensures that no changes are made to production infrastructure without proper review and approval. It also provides an audit trail, as the plan file records exactly what was proposed and executed.
Conclusion
The terraform init, terraform plan, and terraform apply commands constitute the core of Terraform's functionality. init prepares the environment by installing dependencies and configuring the state backend. plan provides a safe, read-only preview of the changes, allowing for verification and error detection. apply executes the changes, modifying the real-world infrastructure and updating the state file to reflect the new reality. Mastery of these commands, including their flags, safety mechanisms, and interaction with the state file, is essential for any engineer working with Infrastructure as Code. By adhering to the best practices of using saved plan files in automation, locking provider versions, and reviewing plans carefully, teams can achieve high confidence in their deployments. The ability to predict, verify, and execute infrastructure changes with precision is what makes Terraform a powerful tool for modern cloud architecture. As infrastructure complexity grows, these core commands remain the reliable foundation upon which scalable and secure cloud environments are built.