一覧CL-14-1 · Infrastructure as Code

Terraformの基本操作

宣言した値、保存した計画、state、実ファイルを別々に確認し、どの時点の値が反映されるか説明します。

説明

Terraformは宣言と管理対象の状態を比較し、変更計画を作ります。planは計画の作成、applyは変更の適用、stateは管理する資源と実体の対応記録です。ファイルを書き換えただけでは、管理対象へ反映されません。

local providerでapp.confと、その内容のハッシュを持つmanifest.txtを作ります。保存したplanを確認して適用し、入力を8080から9090へ変えて差分と実ファイルを比較します。対象は専用ディレクトリ内の2ファイルです。

宣言した値、保存した計画、state、実ファイルを別々に確認し、どの時点の値が反映されるか説明します。

1宣言を読む

portは8080が既定です。manifestのcontentはconfigのcontentを参照するため、資源間の依存関係になります。

cat main.tf変数・ファイル資源・参照関係を読む

app.confとmanifest.txtの2資源があります。file_permission=0600は作成するファイルの権限です。

2計画を保存する

plan.binに計画を保存します。inspect.pyはterraform show -jsonから資源ごとのactionsを抜き出します。

terraform plan変更計画を保存する -input=false -no-color -out=plan.bin > plan.log python3 inspect.py plan資源別の変更を読む python3 inspect.py dependency宣言内の依存参照を読む

2資源ともcreateです。manifestのcontent_referencesにconfigが含まれ、参照が依存を作っていることも確認できます。

3計画だけではファイルを作らない

plan後の実ファイルの有無を確認します。

python3 -c管理対象ファイルがまだないことを確認する 'from pathlib import Path; print("config_exists="+str(Path("app.conf").exists()))'

Falseです。変更計画の作成と、実際の書込みは別の操作です。

4保存した計画を適用する

確認したplan.binをapplyへ渡します。適用後にstateの資源一覧とファイルを読みます。

terraform apply保存した計画を適用する -input=false -no-color plan.bin > apply.log terraform state list管理資源のアドレスを読む cat app.conf manifest.txt実ファイルの内容を読む

2資源がstateに登録され、app.confはport=8080です。manifestにはその内容から求めたSHA-256が入っています。

5同じ宣言なら差分はない

同じ入力で再度planします。

terraform plan適用後の状態を比較する -input=false -no-color -out=plan.bin > plan.log python3 inspect.py plan変更不要のactionsを読む

両方no-opです。変更する必要がない状態になっています。

6入力を変えて差分を確認する

terraform.tfvars.jsonでport=9090を指定します。local_fileのcontent変更は、このproviderではファイルの置換として計画されます。

python3 -c入力を9090へ変更する 'import json; json.dump({"port":9090},open("terraform.tfvars.json","w"))' terraform plan新しい入力の差分を保存する -input=false -no-color -out=plan.bin > plan.log python3 inspect.py plan置換対象を読む cat app.confまだ変わっていない実ファイルを読む

計画にはdelete,createが表示されます。apply前の実ファイルは8080のままで、まだ更新されていません。

7変更後の値とハッシュを照合する

新しい計画を適用し、実ファイルから別にSHA-256を計算します。

terraform apply変更計画を適用する -input=false -no-color plan.bin > apply.log cat app.conf実際のportを読む python3 -c実ファイルからハッシュを再計算する 'from pathlib import Path; import hashlib; digest=hashlib.sha256(Path("app.conf").read_bytes()).hexdigest(); print("manifest_matches="+str(Path("manifest.txt").read_text()=="config_sha256="+digest+"\n"))'

port=9090、manifest_matches=Trueです。宣言の参照だけでなく、保存された2ファイルの整合を確認できました。

8入力の範囲外を拒否する

port=80を指定すると変数のvalidationに違反します。inspect.pyは実際のplanの終了値とエラー文を確認します。

python3 inspect.py invalid範囲外の値でplanが失敗することを確認する cat app.conf拒否後に実ファイルが保持されたことを読む

planは終了値1で拒否され、実ファイルは9090のままです。妥当でない入力を反映する前に止められました。

まとめ

planは差分を作り、applyで実体を変更します。

資源間の参照は依存関係を作ります。

stateだけでなく実ファイルの値と整合も確認します。

操作履歴と解説を振り返れます。

Terraform 1.17.0 / hashicorp/local 2.5.3 / local backend

1
cat main.tf
terraform {
  required_providers {
    local = {
      source = "hashicorp/local"
      version = "2.5.3"
    }
  }
}
variable "port" {
  type = number
  default = 8080
  validation {
    condition = var.port >= 1024 && var.port <= 65535 && floor(var.port) == var.port
    error_message = "port must be an integer from 1024 to 65535."
  }
}
resource "local_file" "config" {
  filename = "${path.module}/app.conf"
  content = "port=${var.port}\n"
  file_permission = "0600"
}
resource "local_file" "manifest" {
  filename = "${path.module}/manifest.txt"
  content = "config_sha256=${sha256(local_file.config.content)}\n"
  file_permission = "0600"
}
output "port" { value = var.port }
2
terraform plan -input=false -no-color -out=plan.bin > plan.log python3 inspect.py plan python3 inspect.py dependency
local_file.config create
local_file.manifest create
content_references=['local_file.config.content', 'local_file.config']
3
python3 -c 'from pathlib import Path; print("config_exists="+str(Path("app.conf").exists()))'
config_exists=False
4
terraform apply -input=false -no-color plan.bin > apply.log terraform state list cat app.conf manifest.txt
local_file.config
local_file.manifest
port=8080
config_sha256=732322f37243042be9e5af21441ccfeed748f1cc2dacce6a9cc8cf31b4207083
5
terraform plan -input=false -no-color -out=plan.bin > plan.log python3 inspect.py plan
local_file.config no-op
local_file.manifest no-op
6
python3 -c 'import json; json.dump({"port":9090},open("terraform.tfvars.json","w"))' terraform plan -input=false -no-color -out=plan.bin > plan.log python3 inspect.py plan cat app.conf
local_file.config delete,create
local_file.manifest delete,create
port=8080
7
terraform apply -input=false -no-color plan.bin > apply.log cat app.conf python3 -c 'from pathlib import Path; import hashlib; digest=hashlib.sha256(Path("app.conf").read_bytes()).hexdigest(); print("manifest_matches="+str(Path("manifest.txt").read_text()=="config_sha256="+digest+"\n"))'
port=9090
manifest_matches=True
8
python3 inspect.py invalid cat app.conf
exit=1 validation=port must be an integer from 1024 to 65535.
port=9090

演習完了