小さな再利用可能なモジュールを作成する

AWSBeginner
オンラインで実践に進む

はじめに

2 つのチームが、名前、所有者、レポートの文面を変えて、同じ小規模なレポートストアの設計を利用したいと考えています。用意されたモジュールのインターフェースを完成させ、独立した 2 つのルートから呼び出し、既存の要素を置き換えずに、安定したキーを持つレポートのコレクションを拡張します。

ローカルモジュール、変数、出力、リソースアドレスを理解していることを前提とします。リソースのひな形と呼び出し側の入力は用意されています。入出力の契約を定義し、ツールが準備されたこの新しい環境でデプロイします。個人の AWS アカウントは不要です。

認定試験との関連

認定資格 試験のタスク 演習内容
CloudOps Engineer – Associate (SOA-C03) Task 3.1, Skill 3.1.6 稼働中のリソースを保持しながら、確認済みの設定と状態の変更を通じてインフラストラクチャを保守する、Terraform の基礎演習です。

実験の概要

概念図:保守作業で、設定、状態、既存のレポート用ストレージの関係を保持します。

型を指定したモジュールインターフェースを定義する

このステップでは、再利用可能な小さなストレージモジュールの入出力の契約を完成させます。子モジュールは、インターフェースを通じて利用できる形でリソースをまとめます。ルートモジュールは、具体的な入力を渡し、独自の状態を所有します。

cd /home/labex/project/reusable-reports
cat modules/report-store/main.tf
cat dev/main.tf

用意されたリソースのひな形は、var.bucket_name、var.owner、var.reports を使用します。for_each は、マップのキーごとに 1 つのオブジェクトインスタンスを作成し、aws_s3_object.reports["daily"] のようなアドレスを使います。安定した名前付きキーにより、既存のオブジェクトを新しく追加する要素と区別できます。バケットのバージョニングリソースが明示的な依存先になっているため、レポートの書き込みにはバージョン ID が付与されます。

インターフェースを定義します。map(object({ body = string })) は、各要素がレポートの文面を渡す必要がある、名前付きのコレクションを受け取ります。これにより、文書化されていない入力形式に頼らず、モジュールが期待する内容を明示できます。

cat > modules/report-store/variables.tf <<'HCL'
variable "bucket_name" {
  type        = string
  description = "Unique bucket name supplied by this environment root."
}
variable "owner" {
  type        = string
  description = "Owner tag supplied by the caller."
}
variable "reports" {
  type        = map(object({ body = string }))
  description = "Stable report keys and their text content."
}
HCL

出力を使うと、呼び出し側がモジュール内部に直接アクセスせずに、有用なリソースの情報を取得できます。次の for 式は、キーをオブジェクトキーに対応付けるマップを作成します。

cat > modules/report-store/outputs.tf <<'HCL'
output "bucket_id" {
  value = aws_s3_bucket.store.id
}
output "object_keys" {
  value = { for key, report in aws_s3_object.reports : key => report.key }
}
HCL

dev ルートを初期化し、契約を確認します。

terraform -chdir=dev init
terraform -chdir=dev validate
terraform -chdir=dev plan

設定が有効で、バケット、バージョニング設定、daily レポートオブジェクトの 3 to add が表示されるはずです。呼び出し側のバケット名、所有者、レポートの文面を確認するまでは、適用しないでください。独立した財務チームの参照用ストレージは、このモジュールとは無関係なので、変更せずに保持する必要があります。

独立した 2 つのルートから同じモジュールを呼び出す

このステップでは、同じモジュールに異なる入力を渡して dev と preview をデプロイします。ルート間で共有するのはモジュールのソースファイルであり、状態ではありません。現在、別々の作業ディレクトリは、それぞれ別のローカル状態を使用しています。for_each が区別するのは、1 つの状態の中にあるリソースだけです。

dev のプランを保存し、確認してから適用します。

terraform -chdir=dev plan -out=dev.tfplan

3 to add, 0 to change, 0 to destroy と表示されるはずです。

terraform -chdir=dev apply dev.tfplan

preview を初期化する前に、呼び出し側の設定を読みます。

cat preview/main.tf
terraform -chdir=preview init
terraform -chdir=preview validate
terraform -chdir=preview plan -out=preview.tfplan

個別の入力によって、module-preview-reports、所有者 preview、文面 preview daily report が選択されます。dev には変更を加えず、さらに 3 つのリソースを作成するプランになるはずです。

terraform -chdir=preview apply preview.tfplan

各ルートの出力と、安定したインスタンスアドレスを確認します。

terraform -chdir=dev output
terraform -chdir=preview output
terraform -chdir=dev state list
terraform -chdir=preview state list

両方のルートが bucket_id と object_keys を出力します。それぞれに "daily" をキーとするレポートインスタンスがありますが、これらのアドレスは異なるローカル状態と異なるバケットに属します。実際の内容を読み取ります。

aws s3 \
  cp s3://module-dev-reports/reports/daily.txt -
aws s3 \
  cp s3://module-preview-reports/reports/daily.txt -

dev daily report と preview daily report が表示されるはずです。AWS View には、独立した 2 つのストアと財務チームの参照用ストレージが表示されます。コレクションを拡張した後で比較できるよう、dev の daily の現在の VersionId を表示します。

aws s3api \
  head-object \
  --bucket module-dev-reports \
  --key reports/daily.txt \
  --query VersionId \
  --output text

既存レポートを置き換えずに安定したキーの要素を追加する

このステップでは、dev のマップだけを拡張します。コレクションのキーを変えるとリソースインスタンスの識別が変わります。キー daily を保持して weekly を追加すれば、daily オブジェクトはそのまま残るはずです。

dev の呼び出し側を、既存の入力は同じまま、新しい要素を 1 つ追加した内容に置き換えます。

cat > dev/main.tf <<'HCL'
module "report_store" {
  source      = "../modules/report-store"
  bucket_name = "module-dev-reports"
  owner       = "dev"
  reports = {
    daily  = { body = "dev daily report\n" }
    weekly = { body = "dev weekly report\n" }
  }
}
output "bucket_id" { value = module.report_store.bucket_id }
output "object_keys" { value = module.report_store.object_keys }
HCL

変更を確認します。

terraform -chdir=dev plan -out=extra.tfplan

1 to add, 0 to change, 0 to destroy と表示され、追加されるのは module.report_store.aws_s3_object.reports["weekly"] だけのはずです。既存の daily オブジェクトやバケットが置き換えられる場合は作業を止め、キーと、変更していないはずの入力を比較してください。

terraform -chdir=dev apply extra.tfplan

daily の VersionId を前のステップの出力と比較します。

aws s3api \
  head-object \
  --bucket module-dev-reports \
  --key reports/daily.txt \
  --query VersionId \
  --output text

元と同じバージョンでなければなりません。新しい要素と、独立した preview のレポートを読み取ります。

aws s3 \
  cp s3://module-dev-reports/reports/weekly.txt -
aws s3 \
  cp s3://module-preview-reports/reports/daily.txt -

dev weekly report と、変更されていない preview daily report が表示されるはずです。バックグラウンドチェックは、置き換えられたオブジェクトのバイト列が同じならよいとはせず、サービスが記録した初回書き込みのバージョンを比較します。

terraform -chdir=dev output
terraform -chdir=dev plan
terraform -chdir=preview plan

dev の出力マップには両方のキーが含まれ、preview には引き続き daily だけが含まれます。両方のルートが収束し、No changes と表示されるはずです。稼働中のストアは利用できる状態で保持してください。このモジュールは、インターフェースとリソースのコレクションを定義します。個別のリモートバックエンドキーの選択は、環境の分離に関する別の判断であり、後で扱います。

モジュールの入力と出力を境界として使う方法については、HashiCorp のモジュール構成のガイドを参照してください。

まとめ

型を指定したモジュールインターフェースを作成し、独立した 2 つのルートに有用な出力を返し、元のレポートを置き換えずに安定したキーを持つ for_each の要素を追加しました。リソースのコレクションと、環境の状態を分ける境界は別のものです。