Terraform 入門まとめ: 基本文法から AWS 実例、data / locals / output / ファイル分割まで一気に整理
Terraform を学び始めると、最初は次のような疑問が連続で出てきます。
-
resource "aws_instance" "web"の"web"は何か -
local_fileは何か -
dataとresourceは何が違うのか -
localsは何に使うのか -
.tfファイル間でimportは必要なのか -
main.tfには何を書くべきか -
outputは何のためにあるのか
この記事では、そうした疑問を Terraform の基本構造から AWS 実例までつなげて まとめます。
Terraform 初学者が「全体像」をつかめるように、できるだけ実例ベースで整理しています。
Terraform とは
Terraform は、インフラをコードで定義して作成・管理するための IaC ツールです。
AWS、GCP、Azure などのクラウドや、SaaS、ローカル環境など、さまざまな対象をコードで管理できます。
Terraform の設定は主に HCL(HashiCorp Configuration Language) という記法で書きます。
Terraform のコードは、ざっくりいうと次の部品で構成されます。
terraformprovidervariablelocalsdataresourceoutput
まずはそれぞれを順番に見ていきます。
1. Terraform の基本構文
Terraform の設定は ブロック の集まりです。
たとえば最小構成はこんな形です。
terraform {
required_providers {
local = {
source = "hashicorp/local"
version = "~> 2.5"
}
}
}
provider "local" {}
resource "local_file" "example" {
filename = "hello.txt"
content = "Hello Terraform"
}
このコードでは、terraform apply を実行するとローカルに hello.txt が作成されます。
1-1. ブロックの基本形
Terraform のブロックはだいたい次の形で書きます。
ブロック種別 "リソース種別" "Terraform 内でのリソース名" {
引数名 = 値
}
たとえば:
resource "local_file" "example" {
filename = "hello.txt"
}
これは
- ブロック種別:
resource - リソース種別:
local_file - リソース名:
example
です。
1-2. 値の書き方
Terraform では次のような値が使えます。
文字列・数値・真偽値
name = "web-server"
port = 8080
enabled = true
リスト
ports = [80, 443, 8080]
マップ
tags = {
Environment = "dev"
Owner = "team-a"
}
複数行文字列
content = <<EOF
line1
line2
line3
EOF
2. local_file とは何か
Terraform の学習で最初によく出てくるのが local_file です。
resource "local_file" "example" {
filename = "hello.txt"
content = "Hello Terraform"
}
local_file は、local プロバイダが提供するリソース型 です。
AWS の EC2 のようなクラウドリソースではなく、Terraform を実行しているマシン上にローカルファイルを作るためのリソース です。
つまりこのコードは、
-
hello.txtというファイルを作る - 中身に
Hello Terraformを書く
という意味になります。
2-1. 何に使うのか
local_file は主に次のような用途で使われます。
- Terraform の学習
- 設定ファイルの生成
- 確認用ファイルの出力
- テンプレートから
.envや JSON を作る
例
resource "local_file" "env_file" {
filename = ".env"
content = <<EOF
APP_NAME=myapp
APP_ENV=local
APP_DEBUG=true
EOF
}
3. ラベルはなぜ必要なのか
Terraform で最初に混乱しやすいのがこれです。
resource "local_file" "example" {
filename = "hello.txt"
content = "Hello Terraform"
}
この "example" は何かというと、Terraform 内でそのリソースを識別するための名前 です。
3-1. ラベルの役割
Terraform はリソースを 型 + 名前 で管理します。
この場合は:
- 型:
local_file - 名前:
example
です。
つまり Terraform はこのリソースを local_file.example として扱います。
参照例
output "file_name" {
value = local_file.example.filename
}
ここでの local_file.example.filename は、
-
local_file= リソース型 -
example= ラベル -
filename= 属性
という意味です。
3-2. ラベルはなくてもいいのか
resource では 必要です。なくせません。
これは OK です。
resource "local_file" "example" {
filename = "hello.txt"
}
これは NG です。
resource "local_file" {
filename = "hello.txt"
}
resource ブロックは構文上、基本的に次の形が必要です。
resource "リソースタイプ" "ローカル名" {
...
}
3-3. ブロックによってラベル数は違う
ラベルなし
terraform {
required_version = ">= 1.0"
}
locals {
app_name = "demo"
}
1つラベルあり
variable "app_name" {
type = string
}
output "result" {
value = "ok"
}
2つラベルあり
resource "local_file" "example" {
filename = "a.txt"
}
4. AWS の実例で Terraform を見る
ここからは AWS で見ていきます。
最小の EC2 作成例です。
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
provider "aws" {
region = "ap-northeast-1"
}
resource "aws_instance" "web" {
ami = "ami-xxxxxxxxxxxxxxxxx"
instance_type = "t3.micro"
tags = {
Name = "terraform-web"
}
}
4-1. これを分解すると
terraform {}
Terraform 本体の設定です。
ここでは AWS プロバイダを使う宣言をしています。
provider "aws" {}
どのクラウドを使うか、その接続設定を書きます。
この例では東京リージョンを指定しています。
resource "aws_instance" "web" {}
AWS の EC2 インスタンスを作る定義です。
-
aws_instance= EC2 インスタンスというリソース型 -
web= Terraform 内での名前
つまり aws_instance.web として参照できます。
5. variable で可変部分を外出しする
AMI ID やインスタンスタイプを毎回固定値で書くと使い回しにくいので、variable を使います。
variable "aws_region" {
type = string
default = "ap-northeast-1"
}
variable "ami_id" {
type = string
}
variable "instance_type" {
type = string
default = "t3.micro"
}
provider "aws" {
region = var.aws_region
}
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = var.instance_type
tags = {
Name = "terraform-web"
}
}
5-1. ポイント
-
variable "aws_region"で入力変数を定義 - 参照は
var.aws_region -
var.ami_idのように使う
イメージ
-
variable= 外から渡してもらう入力 -
output= 外へ返す出力
variable は関数でいう「引数」のようなものです。
6. data ブロックとは何か
Terraform でかなり重要なのが data です。
一言でいうと:
-
resource= 作る -
data= 既にあるものを読む
です。
6-1. resource との違い
resource
resource "aws_instance" "web" {
ami = "ami-xxxxxxxx"
instance_type = "t3.micro"
}
これは EC2 を 新しく作る です。
data
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-*-x86_64"]
}
}
これは AWS に既にある AMI を 検索して読む です。
6-2. AWS でよくある使い方
- 既存の VPC を使う
- 既存の Subnet を使う
- 最新の Amazon Linux AMI を使う
- 既存の Route53 Hosted Zone を使う
6-3. AMI を取得して EC2 を作る例
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-*-x86_64"]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = "t3.micro"
tags = {
Name = "terraform-web"
}
}
ここでの
data.aws_ami.amazon_linux.id
は、
dataaws_amiamazon_linuxid
という順で、取得した AMI の ID を参照 しています。
7. locals とは何か
locals は、自分の Terraform 設定の中で使う中間値や共通値をまとめる仕組み です。
7-1. data との違い
-
data= 外から情報を取ってくる -
locals= 中で使う値を整える
locals の例
locals {
project_name = "sample-app"
env = "dev"
instance_name = "${local.project_name}-${local.env}-web"
common_tags = {
Project = local.project_name
Env = local.env
}
}
これは外部に問い合わせていません。
単に Terraform 内部で値を作っているだけです。
7-2. data と locals を並べると
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-*-x86_64"]
}
}
locals {
project_name = "sample-app"
env = "dev"
instance_name = "${local.project_name}-${local.env}-web"
}
-
data.aws_ami.amazon_linux.idは AWS から取ってきた値 -
local.instance_nameは自分で組み立てた値
7-3. 判断基準
data を使う
- AWS に問い合わせる必要がある
- 既存リソースを探したい
- provider から値を取得したい
locals を使う
- 文字列結合したい
- 共通タグをまとめたい
- 長い式に名前を付けたい
8. data と resource と locals を組み合わせる AWS 例
variable "ami_id" {
type = string
}
locals {
project_name = "sample-app"
env = "dev"
instance_name = "${local.project_name}-${local.env}-web"
common_tags = {
Project = local.project_name
Env = local.env
}
}
data "aws_subnet" "public_a" {
filter {
name = "tag:Name"
values = ["public-subnet-a"]
}
}
resource "aws_security_group" "web_sg" {
name = "${local.project_name}-${local.env}-web-sg"
vpc_id = var.vpc_id
tags = local.common_tags
}
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = "t3.micro"
subnet_id = data.aws_subnet.public_a.id
vpc_security_group_ids = [aws_security_group.web_sg.id]
tags = merge(local.common_tags, {
Name = local.instance_name
})
}
この例の役割はこうです。
-
variable= 外から値を受け取る -
locals= 中間値や共通値を作る -
data= 既存の Subnet を読む -
resource= Security Group と EC2 を作る
9. output とは何か
output は、最後に外へ見せたい値を公開するための仕組み です。
たとえば、EC2 を作ったあとに
- インスタンスID
- パブリックIP
- Security Group ID
などを表示したいことがあります。
そのために output を使います。
9-1. 基本構文
output "名前" {
value = 出したい値
}
例
output "instance_id" {
value = aws_instance.web.id
}
9-2. AWS 実例
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = "t3.micro"
}
output "instance_id" {
value = aws_instance.web.id
}
output "public_ip" {
value = aws_instance.web.public_ip
}
9-3. data や locals も出せる
output "selected_ami_id" {
value = data.aws_ami.amazon_linux.id
}
output "instance_name" {
value = local.instance_name
}
9-4. output のイメージ
-
variable= 入力 -
output= 出力 -
locals= 中間値 -
resource= 作る -
data= 読む
かなり整理しやすくなります。
10. output の実務での用途
よく出力するものは次のような値です。
- VPC ID
- Subnet ID
- Security Group ID
- EC2 の IP
- ALB の DNS 名
- RDS endpoint
例
output "instance_info" {
value = {
id = aws_instance.web.id
public_ip = aws_instance.web.public_ip
type = aws_instance.web.instance_type
}
}
リストやマップも出せます。
10-1. sensitive
機密値を扱うときは sensitive = true を付けられます。
output "db_password" {
value = var.db_password
sensitive = true
}
11. ファイル間の参照に import は必要か
基本は 不要です。
Terraform は、同じディレクトリ内の .tf ファイルを自動的にまとめて読み込みます。
たとえば:
variables.tf
variable "instance_type" {
type = string
default = "t3.micro"
}
main.tf
resource "aws_instance" "web" {
ami = "ami-xxxxxxxxxxxxxxxxx"
instance_type = var.instance_type
}
このように、別ファイルでもそのまま参照できます。
Python や JavaScript のような import は通常不要です。
11-1. なぜ不要なのか
Terraform から見ると、同じディレクトリ内の .tf は 全部で1つの設定 だからです。
よくある分け方
provider.tfvariables.tflocals.tfmain.tfoutputs.tf
この分割は読み込みのためではなく、見やすさのため です。
12. 別ディレクトリはどうするのか
別ディレクトリは自動参照されません。
その場合は module を使います。
12-1. 例
.
├── main.tf
└── modules
└── ec2
├── main.tf
├── variables.tf
└── outputs.tf
親側の main.tf:
module "ec2" {
source = "./modules/ec2"
instance_type = "t3.micro"
}
つまり整理すると:
- 同じディレクトリ内 →
import不要 - 別ディレクトリ →
moduleを使う
13. main.tf には何を書くべきか
Terraform には「main.tf に必ずこれを書く」という厳密ルールはありません。
main.tf は 慣例的な名前 です。
ただ、一般的には main.tf に そのディレクトリの中心となる定義 を置くことが多いです。
13-1. よくある役割分担
-
main.tf
主要なresource、data、module -
variables.tf
variable -
locals.tf
locals -
outputs.tf
output -
provider.tf
terraform、provider
13-2. main.tf の例
data "aws_subnet" "public_a" {
filter {
name = "tag:Name"
values = ["public-subnet-a"]
}
}
resource "aws_security_group" "web_sg" {
name = "${local.project_name}-${local.env}-web-sg"
vpc_id = var.vpc_id
tags = local.common_tags
}
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = var.instance_type
subnet_id = data.aws_subnet.public_a.id
vpc_security_group_ids = [aws_security_group.web_sg.id]
tags = merge(local.common_tags, {
Name = local.instance_name
})
}
つまり main.tf は、その構成の本体 を置く場所として使うのが分かりやすいです。
13-3. 小規模なら全部 main.tf でもよい
学習用や小規模構成なら、全部 main.tf に書いても問題ありません。
ファイル分割はあくまで 可読性のため です。
14. Security Group を含む AWS 実例
ここまでの知識を組み合わせた、少し実務っぽい例です。
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
variable "aws_region" {
type = string
default = "ap-northeast-1"
}
variable "ami_id" {
type = string
}
provider "aws" {
region = var.aws_region
}
locals {
project_name = "sample-app"
env = "dev"
instance_name = "${local.project_name}-${local.env}-web"
common_tags = {
Project = local.project_name
Env = local.env
}
}
data "aws_subnet" "public_a" {
filter {
name = "tag:Name"
values = ["public-subnet-a"]
}
}
resource "aws_security_group" "web_sg" {
name = "${local.project_name}-${local.env}-web-sg"
vpc_id = var.vpc_id
tags = local.common_tags
}
resource "aws_vpc_security_group_ingress_rule" "http" {
security_group_id = aws_security_group.web_sg.id
cidr_ipv4 = "0.0.0.0/0"
from_port = 80
ip_protocol = "tcp"
to_port = 80
}
resource "aws_vpc_security_group_ingress_rule" "ssh" {
security_group_id = aws_security_group.web_sg.id
cidr_ipv4 = "0.0.0.0/0"
from_port = 22
ip_protocol = "tcp"
to_port = 22
}
resource "aws_vpc_security_group_egress_rule" "all" {
security_group_id = aws_security_group.web_sg.id
cidr_ipv4 = "0.0.0.0/0"
ip_protocol = "-1"
}
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = "t3.micro"
subnet_id = data.aws_subnet.public_a.id
vpc_security_group_ids = [aws_security_group.web_sg.id]
tags = merge(local.common_tags, {
Name = local.instance_name
})
}
output "instance_id" {
value = aws_instance.web.id
}
output "security_group_id" {
value = aws_security_group.web_sg.id
}
14-1. この例の読み方
-
providerで AWS を使う -
variableで外から受け取る値を定義 -
localsで命名規則や共通タグを作る -
dataで既存 Subnet を取得 -
resourceで Security Group と EC2 を作る -
outputで結果を表示する
Terraform の全体像がこの1つにかなり詰まっています。
15. data / locals / resource / output の違いを整理
最後に、ここが一番重要なのでまとめます。
variable
外から受け取る入力
variable "instance_type" {
type = string
default = "t3.micro"
}
locals
Terraform 内で使う中間値
locals {
instance_name = "sample-dev-web"
}
data
既存情報を読む
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
}
resource
新しく作る
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = var.instance_type
}
output
最後に外へ出す
output "instance_id" {
value = aws_instance.web.id
}
16. 初学者向けの覚え方
まずはこれで十分です。
-
resource= 作る -
data= 読む -
locals= 中で使う値を整える -
variable= 外から入れる -
output= 外へ出す
そして AWS の場合は、最初は次の形だけ読めれば大丈夫です。
provider "aws" {
region = "ap-northeast-1"
}
resource "aws_XXX" "name" {
...
}
そこに少しずつ
variablelocalsdataoutput
を足していけば理解しやすいです。
17. よくあるつまずきポイント
17-1. name とラベルは違う
resource "aws_security_group" "web_sg" {
name = "terraform-web-sg"
}
ここで:
-
web_sg= Terraform 内の識別名 -
name = "terraform-web-sg"= AWS 上のリソース名
これは別物です。
17-2. ファイル分割しても import は不要
同じディレクトリ内なら .tf は自動的にまとめて読まれます。
17-3. main.tf は特別なファイルではない
ただの慣例です。
aaa.tf でも動きます。
17-4. locals は外から入力できない
入力は variable、内部の中間値は locals です。
17-5. data では作れない
data は既存情報の取得専用です。
新規作成は resource を使います。
18. まとめ
Terraform は最初は記法が独特に見えますが、実際は役割を分けて考えるとかなり整理しやすいです。
-
variableで入力を受ける -
localsで中間値を作る -
dataで既存情報を読む -
resourceで新規作成する -
outputで結果を出す
さらに、
- 同じディレクトリ内の
.tfは自動的にまとめて読み込まれる -
main.tfは慣例的に中心のリソースを書くことが多い - 別ディレクトリを使うときは
moduleを使う
という流れを押さえると、Terraform の構造がかなり見えてきます。
Terraform 初学者は、まずは次の順で試すのがおすすめです。
-
local_fileで基本構文に慣れる -
aws_instanceで AWS リソースを1つ作る -
variableとoutputを追加する -
dataで既存 AMI や Subnet を読む -
localsで命名やタグを整理する - ファイル分割と
moduleを覚える
ここまで理解できると、Terraform を「なんとなく読む」段階から、かなり「自分で書ける」段階に近づきます。