Why the block structure matters
- Clear intent: Each block type has an explicit role (for example,
providervsresource), which makes configurations easier to read and reason about. - Modularity: Blocks compose naturally — use the right block types for discrete concerns and group them into modules.
- Reusability: Consistent block patterns enable shareable modules, predictable behavior, and cleaner team collaboration.
- Predictability: Meta-arguments and nested/dynamic blocks let you express common patterns (scaling, dependencies, lifecycle) declaratively.
Block anatomy (high level)
A Terraform block usually has three parts:- Block type — e.g.,
resource,provider,module,variable. - Optional labels — resource blocks often include a type and a name:
resource "<TYPE>" "<NAME>". - Block body — arguments and nested blocks inside
{ ... }.
count, for_each, depends_on, and lifecycle. Use nested blocks and dynamic blocks when you need to generate block content programmatically from data structures.
Quick reference: common block types
| Block type | Purpose | Example |
|---|---|---|
| Provider | Connects Terraform to an external platform and configures credentials, region, etc. | provider "aws" { region = "us-west-2" } |
| Resource | Declares infrastructure objects Terraform manages (VMs, networks, storage, DNS, etc.). | resource "aws_instance" "web" { ami = "ami-123" instance_type = "t3.micro" } |
| Data | Reads information about existing resources or external sources without creating them. | data "aws_ami" "ubuntu" { most_recent = true } |
| Variable | Declares input variables to parameterize configurations. | variable "instance_type" { type = string; default = "t3.micro" } |
| Output | Exposes values from the applied configuration for other systems or users. | output "instance_ip" { value = aws_instance.web.public_ip } |
| Terraform | Configures Terraform itself (required_providers, backend settings, etc.). | terraform { required_providers { aws = { source = "hashicorp/aws"; version = "~> 4.0" } }; backend "s3" { bucket = "my-terraform-state" } } |
| Module | Groups related resources and references reusable configurations via source. | module "network" { source = "./modules/network"; cidr = "10.0.0.0/16" } |
Examples and common patterns
Provider block (configure a cloud provider):Meta-arguments and advanced nesting
Common meta-arguments:count— create multiple instances of a block.for_each— iterate across maps/sets to create multiple resources with unique keys.depends_on— explicitly express ordering dependencies.lifecycle— fine-tune creation/update/delete behavior.
for_each and lifecycle:
Importing existing resources into state
Terraform does not have an “import block.” The import workflow is:- Add a matching
resourceblock to your configuration that reflects the existing external object. - Run:
terraform import <resource_address> <external_id> - Run
terraform planand update your configuration fields to match the imported resource attributes.
Scope and next steps
This overview covers the most commonly used block types and patterns. Terraform also includes other constructs such aslocals, provisioners, provider-specific nested blocks, and advanced patterns for module composition. Explore individual reference pages and tutorials for in-depth examples and best practices.
This lesson provided a conceptual overview of Terraform’s block structure and the primary block types you’ll use. For hands-on examples, syntax rules, and advanced patterns for each block type, consult the official Terraform language documentation and provider-specific guides.
Links and references
- Terraform language docs: https://developer.hashicorp.com/terraform/language
- Terraform CLI import docs: https://developer.hashicorp.com/terraform/cli/import
- Module registry and examples: https://registry.terraform.io/