Terraformとは、サーバーやネットワーク、ストレージなどのインフラを、コード(設定ファイル)で定義して作成・変更・削除できるツールです。こうした考え方を IaC(Infrastructure as Code) と呼びます。管理画面での手作業の代わりにコードで管理することで、同じ環境を何度でも作れる・変更の履歴が残る・レビューできるようになります。

入門記事の多くは plan と apply までで終わりますが、実務で本当に大切なのは、state(状態ファイル)の管理と、すでにある環境をどうTerraformに取り込むかです。この記事では、AWSのS3バケットを例に基本の流れを解説したうえで、チームで使うための運用まで紹介します。

Terraformの早見表

やりたいこと コマンド・書き方
初期化(プロバイダーのダウンロード) terraform init
コードの整形 terraform fmt
文法のチェック terraform validate
変更内容の確認(実際には変更しない) terraform plan
変更の適用 terraform apply
管理しているリソースの一覧 terraform state list
すべて削除 terraform destroy
既存リソースの取り込み import ブロック + terraform plan -generate-config-out=generated.tf
リソース名の変更 moved ブロック
stateのロック(S3) backend "s3" に use_lockfile = true

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Terraform入門|基本の流れからstate管理・既存リソースの取り込みまで」

Terraformの仕組み

Terraformは、次の3つを見比べて、何を作り、何を変え、何を消すかを計算します。

もの 内容
コード(.tf ファイル) あるべき姿
state(terraform.tfstate) Terraformが前回作ったもの・管理しているものの記録
実際のクラウドの状態 いま本当にあるもの

この「あるべき姿」との差分を plan で確認し、apply で反映する、というのが基本の流れです。手順(何をするか)ではなく、完成形(どうあるべきか)を書くのが、シェルスクリプトとの大きな違いです。

最初のコード:S3バケットを作る

my-infra/
├── versions.tf    # Terraformとプロバイダーのバージョン
├── main.tf        # リソースの定義
├── variables.tf   # 変数
└── outputs.tf     # 出力
# versions.tf
terraform {
  required_version = ">= 1.10"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.0"
    }
  }
}

provider "aws" {
  region = var.region
}
# variables.tf
variable "region" {
  type    = string
  default = "ap-northeast-1"
}

variable "env" {
  type        = string
  description = "環境名(dev / stg / prod)"
}
# main.tf
resource "aws_s3_bucket" "reports" {
  bucket = "example-reports-${var.env}"

  tags = {
    Env       = var.env
    ManagedBy = "terraform"
  }
}

resource "aws_s3_bucket_versioning" "reports" {
  bucket = aws_s3_bucket.reports.id
  versioning_configuration {
    status = "Enabled"
  }
}
# outputs.tf
output "reports_bucket_name" {
  value = aws_s3_bucket.reports.bucket
}

aws_s3_bucket.reports.id のように、ほかのリソースの値を参照すると、Terraformが作る順番を自動で判断します。バケットができてから、バージョニングの設定が行われます。

基本の流れ

terraform init                   # プロバイダーをダウンロード
terraform fmt                    # 書き方を整える
terraform validate               # 文法をチェック
terraform plan -var="env=dev"    # 何が変わるかを確認
terraform apply -var="env=dev"   # 反映(確認のあと yes を入力)

plan の結果は、記号で表示されます。

記号 意味
+ 作成
~ 変更(その場で更新)
- 削除
-/+ 削除してから作り直し(データが消える可能性がある)

特に -/+(作り直し)には注意が必要です。名前など、変更できない項目を変えると、既存のリソースを消して作り直す計画になります。DBやストレージで出たら、apply の前に立ち止まって確認します。

変数は、-var のほかに terraform.tfvars ファイルでも渡せます。

# dev.tfvars
env = "dev"
terraform plan -var-file="dev.tfvars"

stateの管理:チームで使うための一番大切なポイント

terraform apply をすると、作ったリソースの情報が state(terraform.tfstate) に記録されます。

stateはローカルに置いたままにしないのが基本です。

問題 起きること
各自のPCにstateがある 別の人が同じリソースを「知らない」まま作ろうとする
2人が同時に apply する stateが壊れる
PCが壊れる・消える Terraformが管理しているものがわからなくなる
stateをGitにコミットする stateにはパスワードなどの値が平文で入ることがあり、漏えいの原因になる

S3にstateを置き、ロックする

terraform {
  backend "s3" {
    bucket       = "example-terraform-state"
    key          = "my-infra/dev/terraform.tfstate"
    region       = "ap-northeast-1"
    encrypt      = true
    use_lockfile = true
  }
}
  • use_lockfile = true で、S3上のロックファイルを使って同時実行を防ぎます(Terraform 1.10以降)
  • 以前はロックにDynamoDBのテーブル(dynamodb_table)を使っていましたが、公式ドキュメントでは非推奨とされ、S3のロックへの移行が勧められています
  • stateのバケットではバージョニングを有効にしておくと、誤った変更から復元できます(公式ドキュメントでも推奨)

.gitignoreに入れるもの・入れないもの

# .gitignore
.terraform/
*.tfstate
*.tfstate.*
*.tfvars      # 秘密の値を含む場合

.terraform.lock.hcl はコミットします。プロバイダーのバージョンを固定するファイルで、チームのメンバー全員が同じバージョンを使えるようになります。

既存のリソースを取り込む:importブロック

管理画面で手作業で作ったリソースも、Terraformの管理下に取り込めます。

# import.tf
import {
  to = aws_s3_bucket.legacy_logs
  id = "example-legacy-logs"
}
terraform plan -generate-config-out=generated.tf

-generate-config-out を付けると、取り込むリソースの設定を、Terraformのコードとして自動生成してくれます。生成された generated.tf を見直して整え、plan で差分がないことを確認してから apply すると、取り込みが完了します。

方法 特徴
import ブロック(Terraform 1.5以降) コードとして残り、planで確認してからレビュー・適用できる
terraform import コマンド その場でstateに取り込む。記録が残りにくい

チームで作業するなら import ブロックがおすすめです。取り込みもプルリクエストでレビューできます。

名前を変えるときは:movedブロック

リソースの名前(aws_s3_bucket.reports の reports の部分)をそのまま変えると、Terraformは「古いものを削除して、新しいものを作る」と解釈します。

moved {
  from = aws_s3_bucket.reports
  to   = aws_s3_bucket.monthly_reports
}

moved ブロックを書くと、実際のリソースはそのままで、Terraform上の名前だけを変更できます。リファクタリングのときに欠かせない書き方です。

モジュール:同じ構成を使い回す

同じ構成を複数の環境で使うときは、モジュールにまとめます。

my-infra/
├── modules/
│   └── report_bucket/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
└── envs/
    ├── dev/
    │   └── main.tf
    └── prod/
        └── main.tf
# envs/dev/main.tf
module "reports" {
  source = "../../modules/report_bucket"
  env    = "dev"
}

環境ごとにディレクトリを分け、それぞれに別のstateを持たせると、開発環境の作業が本番環境に影響しにくくなります。

チームでの運用:プルリクエストでplanを確認する

手順 内容
1. ブランチで変更 .tf ファイルを編集する
2. プルリクエストを作る CIで fmt -check・validate・plan を自動で実行する
3. planの結果をレビュー 特に「削除」「作り直し」がないかを確認する
4. マージ後にapply mainにマージされたものだけを、CIや決められた担当者が適用する

「コードのレビュー」だけでなく、「planの結果のレビュー」を必須にするのがポイントです。同じコードでも、実際の環境によって計画が変わることがあるためです。

つまずきやすいポイント

つまずき 対処
管理画面で手動で変更してしまった 次の plan で差分として表示される。コードに反映するか、コードどおりに戻すかを決める
-/+(作り直し)が出た 変更できない項目を変えていないか確認。名前の変更なら moved を使う
stateのロックが外れない 実行中の処理がないことを確認してから terraform force-unlock
秘密の値をコードに書いてしまった 変数(sensitive = true)や、シークレット管理サービスから読み込む形にする
削除を防ぎたいリソースがある lifecycle { prevent_destroy = true } を付ける

よくある質問

Q. CloudFormationやAWS CDKとの違いは何ですか? A. CloudFormationとCDKはAWS専用ですが、TerraformはAWS・Google Cloud・Azureなど複数のクラウドや、GitHub・Datadogなどのサービスも同じ書き方で管理できます。AWSだけを使い、AWSの新機能にすぐ対応したい場合はCloudFormationやCDKも有力な選択肢です。

Q. OpenTofuとは何ですか? A. Terraformのライセンス変更をきっかけに生まれた、オープンソースのフォークです。基本的な書き方はTerraformと共通です。

Q. AIにTerraformのコードを書かせてもいいですか? A. 下書きを作るのには向いています。ただし、planの結果は必ず人が確認します。特に削除や作り直しの計画は、AIの説明ではなく、実際のplanの出力で判断することが大切です。

まとめ

  • Terraformは、インフラをコードで定義し、planで確認してapplyで反映するIaCのツール
  • plan の -/+(作り直し) に注意する
  • stateはS3などのリモートに置き、use_lockfile = true でロックする。stateはGitにコミットしない
  • .terraform.lock.hcl はコミットしてバージョンを揃える
  • 既存のリソースは importブロック + -generate-config-out で取り込む
  • 名前の変更は movedブロック、使い回しはモジュール
  • チームでは planの結果をプルリクエストでレビューしてから適用する

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Terraform入門|基本の流れからstate管理・既存リソースの取り込みまで」

株式会社RJCでは、クラウドやインフラの技術も、プロジェクトやグループ活動の中で仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?