過去記事Terraform loop処理の超シンプルな例 の続き。loopで作成したTerraformリソースの参照方法を検証したらやはりハマったので記録書いておく。
前回はCodeCommitリポジトリを作成したが、今回はそれ抜きでCodeDeployのリソースを作成した。CodeDeployは (1)アプリケーションと、(2)デプロイメントグループの2つのリソースを作成する。(2)は(1)に依存している。
作業ディレクトリ構成
work_dir
├── cicd.auto.tfvars
├── cicd.tf
├── config.tf #初期化用config
├── terraform.tfvars #regionのみ定義
└── variables.tf
cicd.tf (リソース作成用コード)
####################################
# CodeDeploy Application
####################################
resource "aws_codedeploy_app" "codedeploy" {
for_each = var.deploy_param_list
name = lookup(each.value, "name")
compute_platform = "Server"
}
####################################
# CodeDeploy Deployment Group
####################################
resource "aws_codedeploy_deployment_group" "codedeploy_grp" {
for_each = var.deploy_param_list
app_name = lookup(each.value, "name")
deployment_group_name = lookup(each.value, "deployment_group_name")
service_role_arn = var.deploy_role
deployment_config_name = "CodeDeployDefault.AllAtOnce"
ec2_tag_set {
ec2_tag_filter {
key = "Name"
type = "KEY_AND_VALUE"
value = lookup(each.value, "value")
}
}
deployment_style {
deployment_option = "WITHOUT_TRAFFIC_CONTROL"
deployment_type = "IN_PLACE"
}
auto_rollback_configuration {
enabled = true
events = ["DEPLOYMENT_FAILURE"]
}
}
cicd.auto.tfvars(cicd.tfが参照する変数)
########################################
# codedeploy vars
########################################
deploy_param_list = {
param1 = {
name = "deploy_app001"
deployment_group_name = "deploy_grp001"
value = "ec2-tag001"
}
param2 = {
name = "deploy_app002"
deployment_group_name = "deploy_grp002"
value = "ec2-tag002"
}
param3 = {
name = "deploy_app003"
deployment_group_name = "deploy_grp003"
value = "ec2-tag003"
}
}
########################################
# CodeDeploy IAM role var
########################################
deploy_role = "arn:aws:iam::[my-account-id]:role/CodeDeployRole"
variables.tf (宣言のみ行う)
#################
# main
#################
variable "region" {
type = string
description = ""
}
#################
# IAM
#################
variable "deploy_role" {
type = string
description = ""
}
#################
# codedeploy
#################
variable "deploy_param_list" {
type = map(map(string))
description = ""
}
この状態で、plan実行時はエラーなし。しかしapplyしたところ、以下のエラーになった。参照するアプリケーション"deploy_app001"がないよ、と。
No application found for name: deploy_app001
この時点でマネコンから確認すると、アプリケーションは出来ているが配下のデプロイメントグループが存在しない状態。実際にはdeploy_app00*は作成されているのだが、terraform実行時にうまく参照できていないらしい。当初、特別な記述をしなくてもTerraformがよしなにやってくれるもんだと思っていたんだけどなぁ。
│ Error: Error creating CodeDeploy deployment group: ApplicationDoesNotExistException: No application found for name: deploy_app001
│
│ with aws_codedeploy_deployment_group.codedeploy_grp["param1"],
│ on cicd.tf line 16, in resource "aws_codedeploy_deployment_group" "codedeploy_grp":
│ 16: resource "aws_codedeploy_deployment_group" "codedeploy_grp" {
│
この後でもう一回applyすると、今度はデプロイメントグループ(deploy_app00*)が作成された。しかし初回にエラーになるのはよろしくない。ありがちな事象だから何らかのソリューションはあるだろう、と調べる。
以下はツールGraphvizにより出力した、この時点におけるTerraformリソースの依存関係を表したグラフ。aws_codedeploy_appとaws_codedeploy_deployment_groupが並列になっていて、互いに関連を持たないように見える。(記事では削っているが実はCodeCommitの記述も含まれているため、グラフに表現されている)
Terraformの依存関係の定義には「暗黙的な依存関係定義」と「明示的な依存関係定義」の2種類がある。後者は、前者が利用できないケースで使用するべき、とのこと。
暗黙的な依存関係定義は、公式ドキュメントにもよく登場する role_arn = aws_iam_role.role001.arn
といった記述で、意識せずとも普段から書いているやつ。今回みたく参照リソースがloopの場合、個別の値はaws_codedeploy_app.codedeploy["param1"].id
のような記述になるが、参照する側もloopだからこの方式では書けない。
一方、明示的な依存関係はdepends_on
を利用する。
depends_on = [aws_iam_role.role001]
以下の記事を読むとloop処理の場合は別の記述方法が要るようなこと書いてあった。面倒くさそうだなぁ〜、と引いてしまう。(こちらはloop処理をcountで実行)
Terraformリソース間の依存関係を確認する
しかし以下フォーラムによれば、for_eachの場合これだけでいけそうではある。
For_each depends_on
と、いうことでやってみる。一旦destroyしてクリーンにしてから、cicd.tfにdepends_on = [aws_codedeploy_app.codedeploy]
1行を追記。
この時点で、グラフを出力すると以下の通りに変化した。一応参照関係が描かれているように見える。
再びapplyしたところ成功。loop処理でも、for_eachはdepends_on
記述だけでOKと判明した。やれやれ。
更新版のcicd.tf
####################################
# CodeDeploy Application
####################################
resource "aws_codedeploy_app" "codedeploy" {
for_each = var.deploy_param_list
name = lookup(each.value, "name")
compute_platform = "Server"
}
####################################
# CodeDeploy Deployment Group
####################################
resource "aws_codedeploy_deployment_group" "codedeploy_grp" {
for_each = var.deploy_param_list
app_name = lookup(each.value, "name")
deployment_group_name = lookup(each.value, "deployment_group_name")
depends_on = [aws_codedeploy_app.codedeploy]
service_role_arn = var.deploy_role
deployment_config_name = "CodeDeployDefault.AllAtOnce"
ec2_tag_set {
ec2_tag_filter {
key = "Name"
type = "KEY_AND_VALUE"
value = lookup(each.value, "value")
}
}
deployment_style {
deployment_option = "WITHOUT_TRAFFIC_CONTROL"
deployment_type = "IN_PLACE"
}
auto_rollback_configuration {
enabled = true
events = ["DEPLOYMENT_FAILURE"]
}
}
ちなみにec2_tag_filterは、apply実行時に指定したタグを持つEC2インスタンスが存在しなくても成功する。あとはこれに、前半にCodeCommit, 後半にCodePipelineを追加して、一気にCI/CDリソース作成目指したい。
余談:Graphvizインストール
参照した記事に記載があって気になったので入れてみた。本来はdot言語で記述したテキストデータをグラフで表現してくれるもので、terraform以外でも使える。現状深掘りしてる余裕はないけど別の機会に遊んでみよう。
テキストデータをグラフ画像に変換するツール「Graphviz」ことはじめ
Graphvizとdot言語でグラフを描く方法のまとめ
$ brew install graphviz
(すっげぇ膨大な依存関係ライブラリが一緒に入ってくる...)
$ dot -V
dot - graphviz version 2.49.3 (20211023.0002)
Terraform作業ディレクトリ内で、以下のようなコマンドを実行してグラフを出力する。
$ terraform graph | dot -Tpng > cicd_graph.png