The Resource block is the smallest unit in Terraform. It declares actual cloud entities (e.g., VPCs, instances, buckets) and is the fundamental building block that plan and apply operate on.
This article is grounded in the behaviors documented in the official docs and lays out the angles commonly tested on the exam, alongside the pitfalls that trip people up in the field.
A Resource block is defined by combining a resource type provided by a provider with a unique local name. Within a single module, you cannot have duplicates of the same type and name. You declare the desired state by supplying attributes (arguments).
Types are defined by the provider and conventionally carry a provider prefix (e.g., aws_, azurerm_, google_). The local name accepts alphanumerics and underscores and is used in subsequent references.
| Block Type | Purpose | Typical Example |
|---|---|---|
| resource | Create, update, and delete actual infrastructure | resource "aws_s3_bucket" "logs" { ... } |
| data | Read existing information (no creation) | data "aws_iam_policy_document" "example" { ... } |
| module | Invoke a reusable configuration | module "vpc" { source = "..." } |
| locals | Reuse expression results as local variables | locals { common_tags = { ... } } |
| output | Export values outside the module | output "bucket_name" { value = aws_s3_bucket.logs.id } |
A minimal Resource block example
resource "aws_s3_bucket" "logs" {
bucket = "nicheelab-logs-${var.env}"
tags = {
Project = "NicheeLab"
Env = var.env
}
}
output "bucket_id" {
value = aws_s3_bucket.logs.id
}Terraform automatically builds a DAG (directed acyclic graph) from reference relationships, then uses it for parallel execution and ordering. Using attributes from another resource creates an implicit dependency.
Use depends_on only when you cannot reference but still need ordering. Excessive depends_on reduces parallelism, so first consider whether a reference is enough.
Resource dependency DAG illustration
Implicit and explicit dependency examples
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "app" {
vpc_id = aws_vpc.main.id # 参照により暗黙依存
cidr_block = "10.0.1.0/24"
availability_zone = var.az
}
resource "null_resource" "post_config" {
# ファイル生成など外部要件で順序を保証したいときのみ
depends_on = [aws_subnet.app]
triggers = {
stamp = timestamp()
}
}Use count with an integer for simple repetition, and for_each with a keyed collection when individual identification matters. provider lets you select specific credentials or regions via an alias.
lifecycle controls creation and replacement behavior, but overusing it because it is convenient makes diffs hard to resolve. Set it only when you have a clear reason.
Examples of count, for_each, and provider aliases
provider "aws" {
region = var.region
}
provider "aws" {
alias = "dr"
region = var.dr_region
}
# count: 単純なN個
resource "aws_iam_user" "ops" {
count = var.ops_user_count
name = "ops-${count.index}"
}
# for_each: 名前で個別管理
resource "aws_s3_bucket" "team" {
for_each = toset(var.team_names)
bucket = "nicheelab-${each.key}-bucket"
provider = aws # 明示省略可
}
# DR側へ明示的に作成
resource "aws_s3_bucket" "team_dr" {
for_each = toset(var.team_names)
bucket = "nicheelab-${each.key}-bucket-dr"
provider = aws.dr
}
# 参照例
output "team_buckets" {
value = { for k, v in aws_s3_bucket.team : k => v.id }
}Arguments are settable inputs, while attributes include outputs and read-only values that Terraform learns after creation. Attributes shown as computed in plan are finalized only after apply.
Reference another resource's attribute as type.name.attr, and access composite attributes via dot notation or indexing. Design carefully to avoid circular references by not casually folding output attributes into input values.
Examples of attribute references and outputs
resource "aws_s3_bucket" "logs" {
bucket = "nicheelab-logs-${var.env}"
tags = var.common_tags
}
resource "aws_s3_bucket_policy" "logs" {
bucket = aws_s3_bucket.logs.id # 属性参照により依存
policy = data.aws_iam_policy_document.allow_put.json
}
data "aws_iam_policy_document" "allow_put" {
statement {
actions = ["s3:PutObject"]
resources = ["${aws_s3_bucket.logs.arn}/*"]
principals { type = "AWS" identifiers = [var.app_role_arn] }
}
}
output "logs_bucket_arn" {
value = aws_s3_bucket.logs.arn
}Attribute changes that require replacement normally follow destroy → create. With create_before_destroy, the new resource is created first and the old one is deleted after cutover. Watch out for failures on resources with name collisions or other constraints.
prevent_destroy is effective against accidental deletion, but it also blocks planned destroys, so share the intent across the team and temporarily remove it when you need to apply changes. ignore_changes can suppress manual modifications, but over long-term operations it tends to become a source of drift.
Typical lifecycle patterns
resource "aws_lb" "public" {
name = "nicheelab-pub"
internal = false
load_balancer_type = "application"
lifecycle {
create_before_destroy = true
}
}
resource "aws_s3_bucket" "archive" {
bucket = "nicheelab-archive-${var.env}"
lifecycle {
prevent_destroy = true
ignore_changes = [tags] # タグを手動変更する運用の暫定対応
}
}Prioritize future references and searchability when naming, so that type.name hints at the role. Consolidate tags and common settings in locals and reuse them across resources.
Common exam topics include understanding implicit dependencies, choosing between count and for_each, the meaning of each lifecycle setting, and the difference between data and resource. Aim for the level where you can read code and explain its behavior.
A small example of locals and reuse
locals {
common_tags = {
Project = "NicheeLab"
Owner = var.owner
}
}
resource "aws_s3_bucket" "app" {
bucket = "nicheelab-app-${var.env}"
tags = local.common_tags
}
output "app_bucket" {
value = aws_s3_bucket.app.id
}Associate
問題 1
In a module, a subnet ID is passed as an argument to other resources, and the plan shows the dependent resources being created after the subnet. In this situation, what is the most appropriate choice regarding whether to add depends_on?
正解: A
If you reference another resource's attributes, an implicit dependency is set automatically. You do not need to add depends_on for ordering. Use depends_on only when you cannot reference but still need to control order.
Are there naming conventions for resource names?
HCL allows alphanumerics and underscores, and the name must be unique within the module. In practice, combining prefixes or suffixes that indicate role and environment (e.g., web, db, prod, dev) makes maintenance easier.
What is the difference between resource and data?
resource handles the creation, update, and deletion of actual infrastructure. data only reads existing information and does not create anything. Both build implicit dependencies through references in the same way.
When should I use depends_on?
Only when you cannot reference another resource's attributes directly but still need ordering guarantees. Examples include external provisioning (such as null_resource provisioners) or resources that must explicitly wait on ordering. Overusing it reduces parallelism.
Practice with certification-focused question sets
無料で問題を解いてみるNicheeLab Editorial Team
NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.
HCL Syntax: Terraform's Configuration Language (2026)
HCL2 fundamentals for Terraform — blocks, attributes, expres...
Terraform Authoring & Operations Pro: Complete Guide (2026)
Tactics for the Terraform Pro exam — module authoring, works...
Terraform Providers: Plugin Management Fundamentals (2026)
Provider mechanics — required_providers, versions, mirrors, ...
Terraform Resource Blocks: Declarative Infra Units (2026)
Resource block fundamentals — addresses, references, common ...
Terraform Data Sources: Read-Only External Data (2026)
Data source basics — declaration, refresh behavior, dependen...