はじめに
Terraformをゼロから学びながら、その内容を自分なりに整理して残していく入門連載です。
第3回では、Terraform StateをS3のリモートバックエンドで管理しました。
Terraformの設定が大きくなってくると、同じような設定を何度も書いたり、1つのディレクトリに多くのリソースを詰め込んだりしがちです。そこで今回は、Terraformのモジュールを使って設定を整理し、再利用する方法を試します。
第2回で作成したSSMパラメータの設定をモジュールに切り出します。既に作成したリソースをそのまま管理し続けられるよう、Terraform State上のリソースアドレスも移動します。
この記事は学習の記録を兼ねています。内容に誤りや、よりよい方法があればコメントで教えていただけると助かります。
今回のゴール
以下をできるようにすることが目標です。
- Terraformモジュールの役割を理解する
- 設定をローカルモジュールへ切り出す
- モジュールの入力と出力を定義する
- 既存リソースのStateアドレスを変更し、作り直しを防ぐ
今回もAWS Systems Manager Parameter StoreのSSMパラメータを管理します。第3回でS3バックエンドへ移行したStateを引き続き利用します。
前提条件
以下の準備ができていることを前提に進めます。
- 第3回で作成した
terraform-aws-learningディレクトリがある - S3バックエンドを設定済みで、Terraform StateがS3に保存されている
- 第2回で作成したSSMパラメータがStateに登録されている
- AWS CLIとTerraformを利用でき、AWS CLIで認証済みである
環境構築やStateのリモート化がまだの場合は、第1回から順に進めてください。
- Terraform入門 第1回:Dev ContainerでAWSリソースを作成してみる
- Terraform入門 第2回:入力変数と出力値で設定を使いやすくする
- Terraform入門 第3回:StateをS3リモートバックエンドで管理する
Terraformのモジュールとは
Terraformでは、.tfファイルが置かれたディレクトリをモジュールとして扱います。これまで作成してきたterraform-aws-learningディレクトリも、ルートモジュールです。
ルートモジュールから別のディレクトリにある設定を呼び出すと、その設定を子モジュールとして利用できます。モジュールに入力値を渡し、モジュール側で定義した出力値を受け取れます。
terraform-aws-learning/
├── main.tf
├── variables.tf
├── outputs.tf
└── modules/
└── ssm-parameter/
├── main.tf
├── variables.tf
└── outputs.tf
今回はSSMパラメータを作成する設定をmodules/ssm-parameterに切り出します。モジュールを使うと、同じ構成を別の値で呼び出したり、ルート側の設定とリソースの詳細を分けたりできます。
SSMパラメータの設定をモジュールに切り出す
第2回で作成したディレクトリに移動します。
cd terraform-aws-learning
この時点で、Stateに登録されているリソースアドレスを確認します。
terraform state list
今回の手順では、次のように表示されることを前提にしています。
aws_ssm_parameter.example
子モジュールを作成する
モジュール用のディレクトリを作成します。
mkdir -p modules/ssm-parameter
modules/ssm-parameter/main.tfを作成し、SSMパラメータのリソースを定義します。
resource "aws_ssm_parameter" "this" {
name = var.parameter_name
type = "String"
value = var.message
tier = "Standard"
}
続けて、modules/ssm-parameter/variables.tfを作成します。
variable "parameter_name" {
description = "Name of the SSM parameter to create"
type = string
}
variable "message" {
description = "Value to store in the SSM parameter"
type = string
}
子モジュールにdefaultを設定せず、呼び出し側から値を受け取る形にしました。これにより、モジュールを使う側が作成するパラメータ名と値を明示できます。
modules/ssm-parameter/outputs.tfを作成し、モジュールの外へ公開する値を定義します。
output "parameter_name" {
description = "Name of the SSM parameter"
value = aws_ssm_parameter.this.name
}
output "parameter_arn" {
description = "ARN of the SSM parameter"
value = aws_ssm_parameter.this.arn
}
output "parameter_version" {
description = "Current version of the SSM parameter"
value = aws_ssm_parameter.this.version
}
aws_ssm_parameter.this.nameのようなリソース属性は、子モジュール内でのみ参照できます。ルートモジュールから値を使うには、このようにoutputで公開します。
ルートモジュールから呼び出す
ルートのmain.tfを次の内容に更新します。required_version、Providerの宣言、aws_region変数はこれまでどおりルート側に残し、SSMパラメータの設定をmoduleブロックから呼び出します。
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.55"
}
}
}
provider "aws" {
region = var.aws_region
}
module "ssm_parameter" {
source = "./modules/ssm-parameter"
parameter_name = var.parameter_name
message = var.message
}
sourceにはモジュールの場所を指定します。./modules/ssm-parameterはルートモジュールからの相対パスです。
AWS Providerはルートモジュールで設定しています。今回のようにProviderのエイリアスを使わない構成では、Provider設定が子モジュールへ引き継がれるため、子モジュール内にprovider "aws"ブロックを追加する必要はありません。
ルートのvariables.tfは第2回で作成したものをそのまま使います。既定値が設定されているため、今回の操作で変数値を入力し直す必要はありません。
ルートのoutputs.tfは、モジュールの出力値を参照するように更新します。
output "parameter_name" {
description = "Name of the SSM parameter"
value = module.ssm_parameter.parameter_name
}
output "parameter_arn" {
description = "ARN of the SSM parameter"
value = module.ssm_parameter.parameter_arn
}
output "parameter_version" {
description = "Current version of the SSM parameter"
value = module.ssm_parameter.parameter_version
}
モジュールの出力値はmodule.<モジュール名>.<出力名>の形式で参照します。今回はmodule.ssm_parameter.parameter_arnのように記述します。
設定を初期化・検証する
設定ファイルを整形し、ローカルモジュールを読み込むために初期化します。
terraform fmt -recursive
terraform init
terraform validate
terraform initは、ルートモジュールが利用するProviderや子モジュールを準備します。今回はS3バックエンドの接続先を変更していないため、Stateの移行オプションは付けません。
Stateのリソースアドレスを変更する
リソースをルートモジュールから子モジュールへ移したため、Terraform上のリソースアドレスも変わります。
変更前: aws_ssm_parameter.example
変更後: module.ssm_parameter.aws_ssm_parameter.this
このままterraform planを実行すると、Terraformは変更前のリソースを削除し、変更後のリソースを新規作成するものとして扱う可能性があります。実際のSSMパラメータを作り直さず、同じリソースを管理し続けるために、terraform state mvでState内のアドレスを変更します。
terraform state mv \
aws_ssm_parameter.example \
module.ssm_parameter.aws_ssm_parameter.this
この操作はTerraform State上のアドレスを変更するもので、AWS上のSSMパラメータ自体は変更しません。S3バックエンドを利用している場合、Stateの更新はS3上のStateに反映されます。
移動後のアドレスを確認します。
terraform state list
次のように表示されれば、Stateの移動は完了です。
module.ssm_parameter.aws_ssm_parameter.this
Stateに対象リソースがない場合
上記のterraform state mvは、移動元のリソースがStateに登録済みの場合に実行します。既にSSMパラメータを削除していて、Stateにも対象がない場合は、このコマンドを実行しないでください。その場合はterraform planを実行し、新しいリソースの作成として表示されることを確認します。
実行計画を確認する
Stateのアドレスを変更したあと、実行計画を確認します。
terraform plan
既存のリソースをStateに登録した状態でモジュールへ移動できていれば、次のように変更なしの計画が表示されます。
No changes. Your infrastructure matches the configuration.
Plan: 1 to add, 0 to change, 1 to destroy.のように作成や削除が表示された場合は、terraform applyを実行せず、Stateのアドレスやモジュールの引数が以前の設定と一致しているか確認してください。
出力値も確認します。
terraform output
パラメータ名、ARN、バージョンが以前と同じ値で表示されれば、モジュール化後もリソースの情報を参照できています。
同じモジュールを複数回使う
同じモジュールを別の値で呼び出せば、同じ構成のリソースを追加できます。例えば、ルートのmain.tfに次のmoduleブロックを追加すると、別のSSMパラメータを管理できます。
module "another_ssm_parameter" {
source = "./modules/ssm-parameter"
parameter_name = "/sandbox/another-message"
message = "hello-from-another-module"
}
同じモジュールを複数回使う場合は、SSMパラメータ名のようにAWS側で一意である必要がある値を重複させないようにします。追加後は、terraform planで新しいリソースだけが作成対象になっていることを確認してから適用します。
リソースを削除する
今回は第2回から管理しているSSMパラメータを削除します。
terraform destroy
確認画面に表示された削除対象がSSMパラメータであることを確認してから、yesを入力します。State用S3バケットは別のbootstrap構成で管理しているため、このコマンドでは削除されません。
前の例の追加モジュールも実際に設定した場合は、そのSSMパラメータも削除対象になります。yesを入力する前に、表示された対象を確認してください。
まとめ
今回は、SSMパラメータの設定をローカルモジュールへ切り出しました。
- Terraformでは、ディレクトリをモジュールとして扱う
- ルートモジュールから
moduleブロックを使って子モジュールを呼び出す - 入力変数でモジュールへ値を渡し、出力値で必要な情報を受け取る
- Providerはルートモジュールで設定し、今回の構成では子モジュールへ引き継いだ
- 既存リソースのアドレスを変更するときは、
terraform state mvでStateを移動する -
terraform planで意図しない作成・削除がないことを確認してから適用する
モジュールへ切り出すことで、リソースの詳細とルート側の構成を分けられました。同じモジュールを別の入力値で呼び出せば、同じパターンの設定も再利用できます。


