# Terraform Terraform is an infrastructure as code tool that enables users to define and provision datacenter infrastructure using a declarative configuration language. It manages infrastructure lifecycle through a planning and execution workflow, allowing teams to safely version, share, and reuse infrastructure definitions. Terraform interacts with cloud providers and services through a plugin-based provider system, translating high-level configurations into API calls that create, modify, or destroy infrastructure resources. The core engine builds a dependency graph of resources, parallelizes operations where possible, and maintains state to track the real-world infrastructure it manages. Terraform's architecture separates the CLI interface from backend operations, provider plugins, and state management, enabling both local and remote execution modes. The system supports workspaces for environment isolation, modules for code reuse, and comprehensive validation and testing capabilities through its command-line interface. ## CLI Commands ### terraform init - Initialize a Terraform working directory Initializes a Terraform working directory by downloading providers, setting up backends, and preparing modules. This command must be run before any other Terraform operations. ```bash # Basic initialization terraform init # Initialize and upgrade provider versions terraform init -upgrade # Initialize with backend configuration terraform init -backend-config="bucket=my-state-bucket" # Example output: # Initializing the backend... # Initializing provider plugins... # - Finding latest version of hashicorp/aws... # - Installing hashicorp/aws v5.1.0... # Terraform has been successfully initialized! ``` ### terraform plan - Generate and show execution plan Creates an execution plan showing what actions Terraform will take to reach the desired state defined in configuration files. Does not modify real infrastructure. ```bash # Generate a plan terraform plan # Save plan to file for later apply terraform plan -out=tfplan # Plan with specific variable values terraform plan -var="region=us-west-2" -var="instance_type=t2.micro" # Plan to destroy resources terraform plan -destroy # Example output: # Terraform will perform the following actions: # # aws_instance.example will be created # + resource "aws_instance" "example" { # + ami = "ami-0c55b159cbfafe1f0" # + instance_type = "t2.micro" # } # Plan: 1 to add, 0 to change, 0 to destroy. ``` ### terraform apply - Apply infrastructure changes Executes the actions proposed in a Terraform plan to create, update, or destroy infrastructure to match the configuration. ```bash # Apply with interactive approval terraform apply # Auto-approve without confirmation terraform apply -auto-approve # Apply a saved plan file terraform apply tfplan # Apply with variable overrides terraform apply -var="environment=production" # Example workflow: terraform plan -out=tfplan terraform apply tfplan # Example output: # aws_instance.example: Creating... # aws_instance.example: Creation complete after 30s [id=i-0abc123def456] # Apply complete! Resources: 1 added, 0 changed, 0 destroyed. ``` ### terraform destroy - Destroy managed infrastructure Destroys all resources managed by the Terraform configuration. Useful for cleaning up test environments or decommissioning infrastructure. ```bash # Destroy with confirmation prompt terraform destroy # Auto-approve destruction terraform destroy -auto-approve # Destroy specific resource only terraform destroy -target=aws_instance.example # Example output: # aws_instance.example: Destroying... [id=i-0abc123def456] # aws_instance.example: Destruction complete after 45s # Destroy complete! Resources: 1 destroyed. ``` ### terraform validate - Validate configuration syntax Validates Terraform configuration files for syntax errors and internal consistency without accessing remote services. ```bash # Validate current directory configuration terraform validate # Validate with JSON output terraform validate -json # Example successful validation: # Success! The configuration is valid. # Example with errors: # Error: Invalid resource name # on main.tf line 5: # 5: resource "aws_instance" "my-instance" { # A name must start with a letter or underscore and may contain only letters, digits, underscores, and dashes. ``` ### terraform show - Display state or plan details Displays human-readable output from a state file or plan file. Useful for inspecting current infrastructure state or reviewing saved plans. ```bash # Show current state terraform show # Show specific plan file terraform show tfplan # Output state in JSON format terraform show -json > state.json # Example output: # # aws_instance.example: # resource "aws_instance" "example" { # ami = "ami-0c55b159cbfafe1f0" # id = "i-0abc123def456" # instance_type = "t2.micro" # public_ip = "54.183.22.100" # } ``` ### terraform output - Extract output values from state Retrieves output values defined in the configuration from the state file. Useful for extracting information for other tools or scripts. ```bash # Show all outputs terraform output # Get specific output value terraform output instance_ip # Output in JSON format terraform output -json # Use output in scripts INSTANCE_IP=$(terraform output -raw instance_ip) echo "Server IP: $INSTANCE_IP" # Example output definition in configuration: # output "instance_ip" { # value = aws_instance.example.public_ip # } ``` ### terraform console - Interactive expression evaluation Launches an interactive console for evaluating Terraform expressions in the context of the current configuration and state. ```bash # Start console terraform console # Example session: $ terraform console > aws_instance.example.public_ip "54.183.22.100" > var.region "us-west-2" > length(aws_instance.example.tags) 3 > exit ``` ### terraform fmt - Format configuration files Rewrites Terraform configuration files to a canonical format and style. Ensures consistent formatting across team members. ```bash # Format current directory terraform fmt # Format recursively terraform fmt -recursive # Check if files need formatting terraform fmt -check # Show differences terraform fmt -diff # Example output: # main.tf # variables.tf ``` ### terraform graph - Generate dependency graph Generates a visual representation of the dependency graph of resources in the configuration. ```bash # Generate graph in DOT format terraform graph > graph.dot # Convert to image with Graphviz terraform graph | dot -Tpng > graph.png # Generate graph for specific plan terraform graph -type=plan > plan-graph.dot ``` ### terraform state - Advanced state management Provides advanced state manipulation commands for moving, removing, and inspecting resources in the state file. ```bash # List all resources in state terraform state list # Show specific resource details terraform state show aws_instance.example # Remove resource from state (doesn't destroy) terraform state rm aws_instance.example # Move resource to new address terraform state mv aws_instance.example aws_instance.web_server # Example list output: # aws_instance.example # aws_security_group.allow_ssh # aws_vpc.main ``` ### terraform workspace - Manage multiple environments Creates and manages workspaces (isolated state environments) for managing multiple infrastructure deployments. ```bash # List workspaces terraform workspace list # Create new workspace terraform workspace new production # Switch to workspace terraform workspace select staging # Show current workspace terraform workspace show # Example workflow: terraform workspace new development terraform apply # Creates resources in development workspace terraform workspace new production terraform apply # Creates separate resources in production workspace ``` ### terraform import - Import existing infrastructure Imports existing infrastructure resources into Terraform state, allowing Terraform to manage resources created outside of Terraform. ```bash # Import AWS instance terraform import aws_instance.example i-0abc123def456 # Import with module terraform import module.network.aws_vpc.main vpc-0123456789abcdef # Example configuration needed before import: # resource "aws_instance" "example" { # # Configuration will be populated after import # } # After import, run terraform plan to see differences ``` ### terraform taint - Mark resource for recreation Marks a resource for recreation on the next apply. Useful when a resource is in a bad state or needs to be replaced. ```bash # Taint a resource terraform taint aws_instance.example # Next apply will destroy and recreate: terraform apply # Example output: # aws_instance.example: Refreshing state... [id=i-0abc123def456] # aws_instance.example must be replaced # -/+ resource "aws_instance" "example" { # ~ id = "i-0abc123def456" -> (known after apply) # } ``` ### terraform providers - Show provider information Displays information about providers required by the configuration. ```bash # List required providers terraform providers # Show provider schemas terraform providers schema -json > schemas.json # Lock provider versions terraform providers lock # Example output: # Providers required by configuration: # . # ├── provider[registry.terraform.io/hashicorp/aws] ~> 5.0 # └── provider[registry.terraform.io/hashicorp/random] 3.5.1 ``` ## Configuration Syntax ### Resource Declaration - Define infrastructure resources Resources are the most important element in Terraform. Each resource block describes one or more infrastructure objects. ```hcl # Basic resource declaration resource "aws_instance" "web" { ami = "ami-0c55b159cbfafe1f0" instance_type = "t2.micro" tags = { Name = "WebServer" Environment = "production" } } # Resource with count meta-argument resource "aws_instance" "servers" { count = 3 ami = "ami-0c55b159cbfafe1f0" instance_type = "t2.micro" tags = { Name = "server-${count.index}" } } # Resource with for_each meta-argument resource "aws_s3_bucket" "buckets" { for_each = toset(["logs", "data", "backups"]) bucket = "my-app-${each.key}" tags = { Purpose = each.key } } # Resource with lifecycle rules resource "aws_instance" "example" { ami = "ami-0c55b159cbfafe1f0" instance_type = "t2.micro" lifecycle { create_before_destroy = true prevent_destroy = true ignore_changes = [tags] } } # Resource with dependencies resource "aws_instance" "app" { ami = "ami-0c55b159cbfafe1f0" instance_type = "t2.micro" depends_on = [aws_db_instance.database] } ``` ### Variable Declaration - Define input variables Variables allow parameterization of Terraform configurations, making them reusable across different environments. ```hcl # Simple variable variable "region" { type = string default = "us-west-2" description = "AWS region for resources" } # Variable with validation variable "instance_type" { type = string default = "t2.micro" validation { condition = contains(["t2.micro", "t2.small", "t2.medium"], var.instance_type) error_message = "Instance type must be t2.micro, t2.small, or t2.medium." } } # Complex type variable variable "vpc_config" { type = object({ cidr_block = string azs = list(string) private_subnets = list(string) public_subnets = list(string) }) default = { cidr_block = "10.0.0.0/16" azs = ["us-west-2a", "us-west-2b"] private_subnets = ["10.0.1.0/24", "10.0.2.0/24"] public_subnets = ["10.0.101.0/24", "10.0.102.0/24"] } } # Using variables in terraform.tfvars file: # region = "us-east-1" # instance_type = "t2.small" # vpc_config = { # cidr_block = "10.1.0.0/16" # azs = ["us-east-1a", "us-east-1b"] # private_subnets = ["10.1.1.0/24", "10.1.2.0/24"] # public_subnets = ["10.1.101.0/24", "10.1.102.0/24"] # } # Reference variables in resources: # ami = var.ami_id # instance_type = var.instance_type # cidr_block = var.vpc_config.cidr_block ``` ### Output Values - Export resource attributes Outputs extract values from resources and make them available for external use or for other Terraform configurations. ```hcl # Simple output output "instance_ip" { value = aws_instance.web.public_ip description = "The public IP of the web server" } # Output with sensitive data output "database_password" { value = aws_db_instance.main.password sensitive = true } # Complex output output "instance_details" { value = { id = aws_instance.web.id public_ip = aws_instance.web.public_ip private_ip = aws_instance.web.private_ip arn = aws_instance.web.arn } description = "Complete instance information" } # Output with depends_on output "vpc_ready" { value = "VPC and subnets are ready" depends_on = [ aws_vpc.main, aws_subnet.private, aws_subnet.public ] } # Accessing outputs from other modules: # module.network.instance_ip # Accessing from command line: # terraform output instance_ip # terraform output -json instance_details ``` ### Module Declaration - Reusable infrastructure components Modules package Terraform configurations for reuse, enabling composition and abstraction of infrastructure patterns. ```hcl # Using a module from local path module "vpc" { source = "./modules/vpc" cidr_block = "10.0.0.0/16" region = var.region tags = { Environment = "production" ManagedBy = "terraform" } } # Using a module from Terraform Registry module "s3_bucket" { source = "terraform-aws-modules/s3-bucket/aws" version = "3.15.0" bucket = "my-application-bucket" acl = "private" versioning = { enabled = true } } # Using module outputs resource "aws_instance" "app" { ami = "ami-0c55b159cbfafe1f0" instance_type = "t2.micro" subnet_id = module.vpc.private_subnet_ids[0] tags = { Name = "App Server" } } # Module with count module "web_servers" { count = 3 source = "./modules/web-server" name = "web-${count.index}" subnet = module.vpc.public_subnet_ids[count.index] } # Module with for_each module "environments" { for_each = toset(["dev", "staging", "prod"]) source = "./modules/environment" env_name = each.key vpc_cidr = "10.${index(["dev", "staging", "prod"], each.key)}.0.0/16" } ``` ### Provider Configuration - Configure provider plugins Provider blocks configure the named provider, specifying credentials and other settings required to interact with APIs. ```hcl # AWS provider configuration provider "aws" { region = "us-west-2" default_tags { tags = { ManagedBy = "Terraform" Project = "MyApp" } } } # Multiple provider configurations (aliases) provider "aws" { alias = "east" region = "us-east-1" } provider "aws" { alias = "west" region = "us-west-2" } # Using aliased providers in resources resource "aws_instance" "east_server" { provider = aws.east ami = "ami-0c55b159cbfafe1f0" instance_type = "t2.micro" } resource "aws_instance" "west_server" { provider = aws.west ami = "ami-0123456789abcdef" instance_type = "t2.micro" } # Provider version constraints in terraform block terraform { required_version = ">= 1.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } random = { source = "hashicorp/random" version = "3.5.1" } } } ``` ### Backend Configuration - Configure state storage Backend blocks configure where Terraform stores state data. Backends enable collaboration and state locking. ```hcl # S3 backend configuration terraform { backend "s3" { bucket = "my-terraform-state" key = "production/terraform.tfstate" region = "us-west-2" encrypt = true dynamodb_table = "terraform-locks" } } # Initialize with backend # terraform init # Migrating from local to remote: # terraform init -migrate-state # Azure backend terraform { backend "azurerm" { resource_group_name = "terraform-rg" storage_account_name = "tfstate" container_name = "tfstate" key = "prod.terraform.tfstate" } } # Consul backend terraform { backend "consul" { address = "consul.example.com:8500" scheme = "https" path = "terraform/production" } } # Local backend (default) terraform { backend "local" { path = "terraform.tfstate" } } # Backend configuration from CLI # terraform init -backend-config="bucket=my-other-bucket" \ # -backend-config="key=my-state" ``` ### Data Sources - Query external data Data sources allow Terraform to fetch information defined outside of Terraform or computed by a separate Terraform configuration. ```hcl # Query existing AWS resources data "aws_ami" "ubuntu" { most_recent = true owners = ["099720109477"] # Canonical filter { name = "name" values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] } } # Use data source in resource resource "aws_instance" "web" { ami = data.aws_ami.ubuntu.id instance_type = "t2.micro" } # Query availability zones data "aws_availability_zones" "available" { state = "available" } # Query VPC data "aws_vpc" "default" { default = true } # Query remote state data "terraform_remote_state" "network" { backend = "s3" config = { bucket = "terraform-state" key = "network/terraform.tfstate" region = "us-west-2" } } # Use remote state outputs resource "aws_instance" "app" { ami = data.aws_ami.ubuntu.id instance_type = "t2.micro" subnet_id = data.terraform_remote_state.network.outputs.subnet_id availability_zone = data.aws_availability_zones.available.names[0] } ``` ## Core API Interfaces ### Context - Core execution engine Context is the central coordination point for all Terraform operations. It manages provider plugins, evaluates configurations, and executes graph-based operations. ```go import ( "github.com/hashicorp/terraform/internal/terraform" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/states" ) // Create a new context for plan/apply operations opts := &terraform.ContextOpts{ Meta: &terraform.ContextMeta{ Env: "production", OriginalWorkingDir: "/path/to/config", }, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): providerFactory, }, Parallelism: 10, UIInput: uiInput, } ctx, diags := terraform.NewContext(opts) if diags.HasErrors() { log.Fatal(diags.Err()) } // Validate configuration validateDiags := ctx.Validate(config, nil) // Generate plan plan, planDiags := ctx.Plan(config, state, &terraform.PlanOpts{ Mode: plans.NormalMode, SetVariables: terraform.InputValues{ "region": &terraform.InputValue{ Value: cty.StringVal("us-west-2"), }, }, }) // Apply changes newState, applyDiags := ctx.Apply(plan, config, nil) ``` ### Backend Interface - State and operations abstraction Backend interface provides abstraction for state storage and remote operations, enabling multiple state storage backends and execution environments. ```go import ( "github.com/hashicorp/terraform/internal/backend" "github.com/hashicorp/terraform/internal/states/statemgr" "github.com/zclconf/go-cty/cty" ) // Initialize a backend type CustomBackend struct { // Implementation fields } // Implement required backend methods func (b *CustomBackend) ConfigSchema() *configschema.Block { return &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "path": { Type: cty.String, Required: true, }, }, } } func (b *CustomBackend) Configure(config cty.Value) tfdiags.Diagnostics { var diags tfdiags.Diagnostics path := config.GetAttr("path").AsString() // Initialize backend with configuration return diags } func (b *CustomBackend) StateMgr(workspace string) (statemgr.Full, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics // Return state manager for the workspace mgr := &statemgr.Filesystem{ Path: fmt.Sprintf("%s/%s.tfstate", b.path, workspace), } return mgr, diags } func (b *CustomBackend) Workspaces() ([]string, tfdiags.Diagnostics) { // Return list of available workspaces return []string{"default", "staging", "production"}, nil } func (b *CustomBackend) DeleteWorkspace(name string, force bool) tfdiags.Diagnostics { // Delete workspace state return nil } ``` ### Provider Interface - Resource management Provider interface defines the contract between Terraform core and provider plugins for managing infrastructure resources. ```go import ( "github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/zclconf/go-cty/cty" ) type CustomProvider struct { configured bool client *ApiClient } // Get provider schema func (p *CustomProvider) GetProviderSchema() providers.GetProviderSchemaResponse { return providers.GetProviderSchemaResponse{ Provider: providers.Schema{ Block: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "api_token": {Type: cty.String, Required: true}, "endpoint": {Type: cty.String, Optional: true}, }, }, }, ResourceTypes: map[string]providers.Schema{ "custom_server": { Version: 1, Block: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "name": {Type: cty.String, Required: true}, "size": {Type: cty.String, Optional: true}, "region": {Type: cty.String, Required: true}, "id": {Type: cty.String, Computed: true}, }, }, }, }, } } // Configure provider func (p *CustomProvider) ConfigureProvider(req providers.ConfigureProviderRequest) providers.ConfigureProviderResponse { var resp providers.ConfigureProviderResponse token := req.Config.GetAttr("api_token").AsString() p.client = NewApiClient(token) p.configured = true return resp } // Apply resource changes func (p *CustomProvider) ApplyResourceChange(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { var resp providers.ApplyResourceChangeResponse if req.PlannedState.IsNull() { // Delete resource id := req.PriorState.GetAttr("id").AsString() err := p.client.Delete(id) if err != nil { resp.Diagnostics = resp.Diagnostics.Append(err) } return resp } // Create or update resource name := req.PlannedState.GetAttr("name").AsString() size := req.PlannedState.GetAttr("size").AsString() region := req.PlannedState.GetAttr("region").AsString() server, err := p.client.CreateServer(name, size, region) if err != nil { resp.Diagnostics = resp.Diagnostics.Append(err) return resp } // Build new state with computed values resp.NewState = cty.ObjectVal(map[string]cty.Value{ "name": cty.StringVal(name), "size": cty.StringVal(size), "region": cty.StringVal(region), "id": cty.StringVal(server.ID), }) return resp } // Read resource current state func (p *CustomProvider) ReadResource(req providers.ReadResourceRequest) providers.ReadResourceResponse { var resp providers.ReadResourceResponse id := req.PriorState.GetAttr("id").AsString() server, err := p.client.GetServer(id) if err != nil { resp.Diagnostics = resp.Diagnostics.Append(err) return resp } resp.NewState = cty.ObjectVal(map[string]cty.Value{ "name": cty.StringVal(server.Name), "size": cty.StringVal(server.Size), "region": cty.StringVal(server.Region), "id": cty.StringVal(server.ID), }) return resp } ``` ### State Management - Infrastructure state tracking State package provides data structures and operations for tracking infrastructure state across Terraform operations. ```go import ( "github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/addrs" "github.com/zclconf/go-cty/cty" ) // Create new state state := states.NewState() // Access state through sync wrapper for thread safety syncState := state.SyncWrapper() // Add module state moduleAddr := addrs.RootModuleInstance module := state.EnsureModule(moduleAddr) // Add resource to state resourceAddr := addrs.Resource{ Mode: addrs.ManagedResourceMode, Type: "aws_instance", Name: "web", }.Absolute(moduleAddr) providerAddr := addrs.AbsProviderConfig{ Provider: addrs.NewDefaultProvider("aws"), Module: moduleAddr.Module(), } syncState.SetResourceInstanceCurrent( resourceAddr.Instance(addrs.NoKey), &states.ResourceInstanceObjectSrc{ Status: states.ObjectReady, AttrsJSON: []byte(`{ "id": "i-1234567890abcdef", "ami": "ami-0c55b159cbfafe1f0", "instance_type": "t2.micro" }`), SchemaVersion: 1, }, providerAddr, ) // Set output values syncState.SetOutputValue( addrs.OutputValue{Name: "instance_id"}.Absolute(moduleAddr), cty.StringVal("i-1234567890abcdef"), false, // not sensitive ) // Query state instance := state.ResourceInstance(resourceAddr.Instance(addrs.NoKey)) if instance != nil { obj := instance.Current // Access state data id := obj.AttrsJSON // JSON representation } // Check if state is empty if state.Empty() { log.Println("No resources managed") } // Iterate modules for _, mod := range state.Modules { log.Printf("Module: %s", mod.Addr) for _, rs := range mod.Resources { log.Printf(" Resource: %s", rs.Addr) } } ``` ## Summary and Integration Terraform serves as a comprehensive infrastructure as code solution, bridging the gap between declarative configuration and imperative infrastructure provisioning. Its primary use cases include multi-cloud infrastructure deployment, immutable infrastructure patterns, disaster recovery automation, and environment parity across development, staging, and production. Teams use Terraform to standardize infrastructure definitions, enforce policies through validation, and maintain audit trails of infrastructure changes through version control integration. The integration patterns revolve around composability through modules, provider extensibility through the plugin system, and state management through backends. Terraform integrates with CI/CD pipelines for automated infrastructure deployment, with configuration management tools like Ansible for post-provisioning setup, and with monitoring systems through provider-specific resources. The CLI commands provide both human-interactive workflows (plan/apply cycles) and automation-friendly interfaces (JSON output, exit codes, saved plans) that enable Terraform to function as both a standalone tool and a component in larger infrastructure automation ecosystems.