0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Terraformって結局何をするもの? Config・Provider・State・Plan/Applyから全体像をつかむ

0
Posted at

はじめに

Terraformを学び始めたとき、自分は「AWSをコードで作るツール」くらいに考えていました。
でも、それだけだと StateProvider がなぜ必要なのか、plan が何を比べているのかがうまくつながりません。

調べていくと、Terraformは 何を材料に差分を考え、どこを通じて実物を読み書きするのか で見ると理解しやすいと分かりました。

TL;DR

Terraformは、Stateを手がかりに実インフラの現在状態を確認します。
Configとの差分をPlanとして計算し、Provider経由で必要な変更を実行するツールです。

1. Terraformは「手順」ではなく「最終状態」を書く

TerraformはInfrastructure as Code(IaC)のツールです。
IaCは、インフラを画面操作だけで管理するのではなく、設定ファイルとして定義し、変更履歴やレビューの対象にする考え方です。

Terraformらしさが出るのは、その設定が宣言的なところでした。

resource "aws_instance" "web" {
  ami           = "ami-xxxxxxxx"
  instance_type = "t3.micro"
}

ここには「EC2作成APIを呼んで、次にこの設定をして……」という手順は書いていません。
書いているのは「web というEC2がこの状態で存在してほしい」という最終状態です。

手順を中心に書く Terraform
何をどの順番で実行するかを書く 最終的にどうなってほしいかを書く
変更手順を人間が組み立てる Terraformが現在との差分から変更内容を考える
API呼び出しの順番が主役 Resource同士の関係とDesired Stateが主役

宣言だけで、作成順序は大丈夫なのか

ここで気になったのが、「VPCより先にSubnetを作ったりしないのか?」ということでした。

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"
}

aws_subnet.appaws_vpc.main.id を参照しています。
Terraformはこの参照から依存関係を検出し、「SubnetはVPCの後に処理する必要がある」と判断します。依存しないResourceは並列で処理できます。

image.png

つまり、人間は手順そのものを書く代わりに、Resource同士の関係をConfigに表現するわけです。

2. 全体像は Config・State・Provider・実インフラで見る

「最終状態を書く」だけなら、Terraformは何を見て差分を判断するのでしょうか。
自分は次の4要素で見ると整理しやすくなりました。

image.png

Config: 「こうしたい」

Configは .tf に書くTerraform configurationです。
ここにはTerraformに維持してほしい状態、つまりDesired Stateを書きます。

State: 「どの実物を管理しているか」

Configには aws_instance.web と書けますが、AWS上の実物は i-012345... のようなIDを持ちます。
Stateは、このTerraform上のResourceと実際のremote objectの対応を保持します。

Provider: 外部システムを読み書きする窓口

Terraform本体がAWSやGitHubなど各サービスのAPI仕様を全部持つのではなく、Providerを通じて外部システムとやり取りします。

初学者としては、Providerを 「Terraformがremote systemを読み書きする窓口」 と考えるとつながりやすかったです。

実インフラ: AWSなどに存在する本物

Terraformが扱うのはConfig上の情報だけではありません。
通常の plan ではProvider経由で実物の現在状態を読み、apply ではProvider経由で必要な変更を書き込みます。

plan  : remote system → Provider → Terraform
apply : Terraform → Provider → remote system

なのでTerraformは、ConfigをそのままAWS APIへ変換するだけのツールではありません。
Stateで管理対象を特定し、実物を読み、Configとの差分を判断する仕組みと考えると、4要素が一本につながりました。

3. init → plan → apply は1本のworkflow

全体モデルが見えると、initplanapply も単独のコマンドではなく役割分担として理解できます。

image.png

terraform init: 実行準備

terraform init はインフラを作るコマンドではありません。
Providerの取得、Moduleの取得、Backendの初期化など、そのworking directoryでTerraformを動かす準備をします。

terraform plan: 現在との差分を計算

通常の terraform plan は、既存remote objectのcurrent stateを読みます。
そのうえでcurrent configurationとprior stateを比較し、必要な変更を提案します。

たとえばConfigを次のように変えたとします。

 resource "aws_instance" "web" {
-  instance_type = "t3.micro"
+  instance_type = "t3.small"
 }

TerraformはGit diffだけを見ているわけではありません。
Stateから管理対象を特定し、Provider経由で実物を確認したうえでPlanを作ります。

そのため、AWSコンソールなどから手動変更が入っていれば、その現在状態もPlanに影響します。

plan 自体は、提案したcreate / update / destroyを実行しません。

terraform apply: 実際に変更する

ここは2つの使い方を分けると混乱しません。

terraform apply

保存済みPlanを渡さない場合、Terraformはその場で新しいexecution planを作成し、確認後に実行します。

一方、

terraform plan -out=tfplan
terraform apply tfplan

とした場合は、保存したPlanを実行します。

変更が成功するとStateも更新され、そのStateが次の plan で再び使われます。

Configを変更
   ↓
planで差分確認
   ↓
applyで実物を変更
   ↓
State更新
   ↓
次のplanへ

Terraformがインフラのlifecycleを管理するというのは、このループを継続することでもあります。

4. なぜStateが必要なのか

自分が最初に引っかかったのは、「AWS APIから現在状態を取得できるなら、Stateはいらないのでは?」という疑問でした。

でもTerraformには、実物の状態だけでなく、Config上のどのResourceと、どの実物が対応しているのかという情報が必要です。

image.png

Configには、

aws_instance.web

というResourceがあります。
AWS上では、たとえば、

i-0123456789abcdef0

という実体です。

TerraformはStateにこのbindingを持ち、「aws_instance.web はこのEC2を管理している」と追跡します。
HashiCorpもStateのprimary purposeを、configuration内のResource instanceと
remote objectのbindingを保存することだと説明しています。

Stateには属性値や依存関係などの情報も含まれます。
ただ、「AWSの状態をコピーしたファイル」と考えるより、Terraformが前回から何を管理しているかを覚えるデータと考える方が理解しやすかったです。

Stateの保存方法を決めるのがBackendです。Backendを明示しなければlocal backendが使われます。
チームでのremote StateやlockingはStateだけでかなり話が広がるので、全体像の記事ではここまでに留めます。

5. Resourceは「管理」、Data Sourceは「参照」

Terraformが外部システムを読み書きできると分かると、「外部のものは全部Resourceとして管理するのか?」という疑問が出てきます。

Terraformには、管理する resource と、データを読み取る data があります。

image.png

Resource: Terraformにlifecycleを管理させる

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

ResourceはTerraformがcreate・update・destroyなどのlifecycleを管理する対象です。
Configを変えれば差分を計算し、Configから削除すればdestroy候補になることがあります。

Data Source: Terraform自身が管理せず、データを読み取る

Data Sourceは、Terraform自身がその対象のlifecycleを管理するのではありません。
Providerなどからデータを読み取り、Config内で参照するために使います。

たとえば、Canonicalが公開しているUbuntu 24.04のAMIから最新のものを探すなら、次のように書けます。

data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"]

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
  }
}

resource "aws_instance" "web" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t3.micro"
}

data.aws_ami.ubuntu はAMIをTerraform管理下に入れているわけではありません。
条件に合うデータを取得し、そのIDをEC2 Resourceから参照しています。

TerraformのLanguage docsでも、Data SourceはProviderなどからデータを取得しますが、
Resourceをcreate・modifyしないと説明されています。

AWS Provider v6では、aws_amimost_recent = true を使う場合、
owners または owner-id / image-id で検索対象を絞る必要があります。
上の例ではCanonicalのowner IDを指定しています。

自分の中では、

resource = Terraformに管理してほしいもの
data     = Terraformから参照したいデータ

と分けると整理しやすかったです。

Terraformが得意な領域

Terraformの中心は、Providerが扱えるremote objectを宣言し、その状態を継続管理することです。

やりたいこと Terraformとの相性
VPC / Subnetを管理する 高い
EC2 / RDSの構成を管理する 高い
GitHub Repositoryを管理する 高い
既存AMIなどの情報を参照する Data Sourceが合う
EC2内部の細かなOS設定を順番に流す 主役ではない
DB schema migrationを順番に適用する 主役ではない

クラウドかどうかより、APIで表現できるremote objectの状態を宣言的に扱う仕事か
考える方が、Terraformの担当範囲を判断しやすいと思います。

6. Terraform Repositoryは「何を管理するか」から読む

全体像がつながった後は、Repositoryもファイル名を片っ端から読むより、いくつかの観点を順番に見る方が読みやすくなりました。

image.png

1. Resource、またはModuleを見る

小さなRepositoryなら resource を見れば管理対象が分かります。

resource "aws_s3_bucket" "logs" {
  # ...
}

ただし、root moduleにResourceがほとんどなく、

module "network" {
  source = "./modules/network"
}

のようにModule呼び出しが中心の構成もあります。
その場合は、呼び出しているModuleの中で何を管理しているかをたどります。

Module設計自体を理解しなくても、「Resourceがない = 何もしていない」ではないと知っておくだけでRepository読解が止まりにくくなります。

2. Providerを見る

required_providersprovider blockから、どのremote systemとやり取りするのかを確認します。

3. Stateの管理方法を見る

backendcloud の設定があれば、Stateをどう管理しているかの手がかりになります。

terraform {
  backend "s3" {}
}

ただし、Backend設定を terraform init -backend-config=... で追加する
partial configurationもあります。
そのためRepositoryだけでState保存先を完全に特定できないケースもあります。

4. Planで実際の変更を見る

最後に terraform plan で、「今の実物に対して何が変わるのか」を確認します。

Configを読むと宣言内容は分かりますが、実際の変更案はConfigだけでは決まりません。
Stateとremote objectのcurrent stateも含めてPlanが作られるからです。

この順番で見ると、Terraform Repositoryを単なる .tf ファイルの集合ではなく、
管理対象・接続先・State・実際の差分として読めるようになりました。

おわりに

Terraformを「AWSをコードで作るツール」とだけ覚えていたときは、Resource、Provider、State、Planが別々の用語に見えていました。

今は、

Terraformは、Stateを手がかりに実インフラを確認し、Configとの差分をPlanにして、Provider経由で必要な変更を実行する

という流れで捉えています。

ConfigにはDesired Stateを書く。
Resourceの依存関係から実行順序を判断する。
Stateで管理対象との対応を持つ。
Providerで外部システムを読み書きする。

この流れがつながると、init / plan / apply も単なるコマンド暗記ではありません。
Terraformがインフラを継続管理するworkflowの一部として見えるようになりました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?