はじめに
普段なんとなく書いているブロックを復習するために、よく使う 9 個と、リファクタや運用で使う 3 個の合計 12 個を、実際に AWS 上で 1 つずつ動かして整理してみました。
| # | ブロック | ひとことで | 手順 |
|---|---|---|---|
| 1 | terraform |
Terraform 本体とプロバイダのバージョンを決める | 1 |
| 2 | provider |
どのクラウドの、どのリージョンを操作するか決める | 1 |
| 3 | variable |
外から渡せる入力値(引数) | 2 |
| 4 | data |
既存の情報を読み取るだけ(作らない) | 2 |
| 5 | locals |
コード内だけで使う計算済みの値 | 2 |
| 6 | resource |
実際に作るリソース | 2 |
| 7 | output |
実行後に表示・受け渡しする値 | 2 |
| 8 | module |
ブロックのまとまりを部品として呼び出す | 3 |
| 9 | import |
手で作った既存リソースを Terraform の管理下に取り込む | 4 |
| 10 | moved |
リソース名を変えても作り直さないようにする | 5 |
| 11 | removed |
リソースを消さずに Terraform の管理から外す | 6 |
| 12 | check |
実環境が期待どおりかを確かめ、ずれていたら警告する | 7 |
使うのは空の S3 バケットと SSM パラメータだけなので、料金はほぼかかりません。最後に全部削除します。
手順ごとに 1 ファイルずつ増やしていき、最終的に次のファイル構成になります。
tf-blocks-demo/
├── versions.tf # 手順 1
├── main.tf # 手順 2
├── s3.tf # 手順 2(手順 5 で上書き)
├── module.tf # 手順 3(呼び出す側)
├── modules/
│ └── ssm_param/
│ └── main.tf # 手順 3(部品側)
├── moved.tf # 手順 5
├── removed.tf # 手順 6(手順 4 の import.tf と入れ替え)
└── check.tf # 手順 7
やってみた
手順 0:準備
removed ブロックは Terraform 1.7 以降で使えるので、1.7 以上であることを確認します。
AWS の認証情報も有効か確かめておきます。
terraform version
aws sts get-caller-identity
mkdir tf-blocks-demo && cd tf-blocks-demo
以降のファイルは、すべてこの tf-blocks-demo ディレクトリを基準に作成します。
手順 1:terraform / provider ブロック(versions.tf)
-
terraform:Terraform 本体の設定です。使うバージョンやプロバイダを決めます
-
provider:接続先の設定です。どのクラウドの、どのリージョンを操作するかを決めます
この手順では「AWS プロバイダの 6.x を使う」「東京リージョンを操作する」と決めます。
terraform {
required_version = ">= 1.7"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
provider "aws" {
region = "ap-northeast-1"
}
terraform init を実行すると、ここに書いたバージョンの AWS プロバイダがダウンロードされます。
terraform init
Installed hashicorp/aws v6.x.x と表示されれば OK です。
手順 2:variable / data / locals / resource / output ブロック(main.tf, s3.tf)
-
variable:外から変えられる入力値です。defaultがあるので、今回は何も渡さなくても動きます
-
data:AWS から情報を読むだけのブロックで、リソースは作りません
-
locals:コード内で何度も使う値に名前を付けます。variableと違い、外からは変えられません
-
resource:実際に作るリソースです
-
output:作った結果を表示したり、ほかに受け渡したりする値です
この手順では S3 バケットを 1 つ作ります。バケット名は世界で一意にする必要があるので、「プロジェクト名 + AWS アカウント ID」で組み立てます。
入力値と名前の組み立ては main.tf に書きます。
variable "project" {
type = string
default = "tf-blocks-demo"
description = "リソース名の接頭辞"
}
data "aws_caller_identity" "current" {}
locals {
bucket_name = "${var.project}-${data.aws_caller_identity.current.account_id}"
}
バケット本体と出力は s3.tf に書きます。force_destroy = true は、中身が残っていても最後に削除できるようにするための設定です。
resource "aws_s3_bucket" "demo" {
bucket = local.bucket_name
force_destroy = true
}
output "bucket_name" {
value = aws_s3_bucket.demo.bucket
}
terraform apply -auto-approve
Apply complete! Resources: 1 added と、bucket_name = "tf-blocks-demo-<アカウントID>" が表示されれば OK です。
手順 3:module ブロック(module.tf, modules/ssm_param/main.tf)
-
module:ブロックのまとまりを「部品」として呼び出す仕組みです。関数のように、引数(variable)を受け取り、戻り値(output)を返します
この手順では、SSM パラメータを作る部品(モジュール)を作り、それを呼び出します。呼び出す側の module.tf は tf-blocks-demo の直下、部品側は modules/ssm_param/ の中に置きます。
まず部品側(子モジュール)を作ります。
mkdir -p modules/ssm_param
variable "name" {
type = string
}
variable "value" {
type = string
}
resource "aws_ssm_parameter" "this" {
name = var.name
type = "String"
value = var.value
}
output "name" {
value = aws_ssm_parameter.this.name
}
次に呼び出す側です。source に部品のディレクトリを指定します。
module "app_version" {
source = "./modules/ssm_param"
name = "/${var.project}/app/version"
value = "v1"
}
output "app_version_param" {
value = module.app_version.name
}
モジュールを追加したら terraform init をやり直す必要があります。
terraform init
terraform apply -auto-approve
module.app_version.aws_ssm_parameter.this: Creation complete と表示されれば OK です。
手順 4:import ブロック(import.tf)
-
import:手作業で作った既存のリソースを、Terraform の管理下に取り込むブロックです
この手順では、Terraform の外で作った SSM パラメータを取り込みます。まず、CLI で手作業のパラメータを作ります。
aws ssm put-parameter --name /tf-blocks-demo/legacy --type String --value legacy
import ブロックで「どのリソース(id)を、どの resource ブロック(to)として取り込むか」を指定します。取り込み先の resource ブロックも一緒に書きます。
import {
to = aws_ssm_parameter.legacy
id = "/tf-blocks-demo/legacy"
}
resource "aws_ssm_parameter" "legacy" {
name = "/tf-blocks-demo/legacy"
type = "String"
value = "legacy"
}
terraform plan
terraform apply -auto-approve
terraform state list
plan が 1 to import, 0 to add, 0 to change になり、terraform state list に aws_ssm_parameter.legacy が出れば OK です。 0 to add なので、作り直さずに取り込めています。
手順 5:moved ブロック(s3.tf, moved.tf)
-
moved:resourceの名前(ラベル)を変えても、作り直さないようにするブロックです
resource の名前を変えるだけだと、Terraform は 「古い名前のリソースを削除して、新しい名前で作る」 と解釈します。この手順では、moved ブロックを使って S3 バケットの名前を demo から main に変えます。
s3.tf を次の内容で上書きします。
resource "aws_s3_bucket" "main" {
bucket = local.bucket_name
force_destroy = true
}
output "bucket_name" {
value = aws_s3_bucket.main.bucket
}
古い名前と新しい名前の対応を moved.tf に書きます。
moved {
from = aws_s3_bucket.demo
to = aws_s3_bucket.main
}
terraform plan
terraform apply -auto-approve
aws_s3_bucket.demo has moved to aws_s3_bucket.main と 0 to add, 0 to change, 0 to destroy が表示されれば OK です。 state 上の名前だけが変わり、バケットは作り直されていません。
手順 6:removed ブロック(removed.tf)
-
removed:AWS 上のリソースは消さずに、Terraform の管理からだけ外すブロックです
resource ブロックを消すだけだと、Terraform は AWS 上のリソースも削除します。この手順では、手順 4 で取り込んだ legacy パラメータを、AWS 上には残したまま管理対象から外します。
resource ブロックごと import.tf を削除します。
rm import.tf
代わりに removed.tf を作ります。destroy = false が「削除はしない」という指定です。
removed {
from = aws_ssm_parameter.legacy
lifecycle {
destroy = false
}
}
terraform plan
terraform apply -auto-approve
terraform state list
aws ssm get-parameter --name /tf-blocks-demo/legacy --query Parameter.Value --output text
will no longer be managed by Terraform, but will not be destroyed と表示され、state list から legacy が消えているのに、get-parameter では legacy が返れば OK です。
手順 7:check ブロック(check.tf)
-
check:実環境が期待どおりかを確かめるブロックです。条件を満たさなくても plan や apply は止まらず、Warning が出るだけです
この手順では「SSM パラメータの値が v1 のままか」を確かめます。値を手で書き換えて、Warning が出ることを確認します。
check の中に書いた data ブロックは、チェックのためだけに AWS の今の値を読みます。
check "app_version" {
data "aws_ssm_parameter" "current" {
name = "/${var.project}/app/version"
}
assert {
condition = data.aws_ssm_parameter.current.value == "v1"
error_message = "SSM パラメータの値が v1 ではありません(手動で変更された可能性があります)"
}
}
まず、そのまま plan を実行します(Warning は出ません)。次に、誰かがコンソールから手で値を変えたという想定で、CLI で v2 に書き換えてから、もう一度 plan を実行します。
terraform plan
aws ssm put-parameter --name /tf-blocks-demo/app/version --type String --value v2 --overwrite
terraform plan
2 回目の plan で Warning: Check block assertion failed と、自分で書いた error_message が表示されれば OK です。 Error ではないので、plan は最後まで進みます。
terraform apply を実行すると、値は v1 に戻ります。
terraform apply -auto-approve
手順 8:後片付け
この手順では、作ったリソースをすべて削除します。手順 6 で管理対象から外した legacy パラメータは terraform destroy では消えないので、CLI で削除します。
terraform destroy -auto-approve
aws ssm delete-parameter --name /tf-blocks-demo/legacy
Destroy complete! Resources: 2 destroyed. と表示されれば完了です。
まとめ
- ほとんどのコードは、
resourceと、それを支えるterraform/provider/variable/data/locals/output/moduleで書けます -
import/moved/removedは、既存のリソースを作り直さずに state だけを操作するブロックで、リファクタや移行のときに役立ちます -
checkは処理を止めずに警告だけ出すので、手作業による変更(ドリフト)の早期発見に使えそうです