過去記事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 graph

 

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行を追記。

この時点で、グラフを出力すると以下の通りに変化した。一応参照関係が描かれているように見える。

Terraform graph

 

再び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

 

Otaru Otaru


関連がありそうな記事