Terraform

TerraformでALBターゲットグループのポートやプロトコルの設定を変更する

TerraformでALBターゲットグループのポートやプロトコルの設定を変更してみます。この変更はターゲットグループの作り直しになるため、一度リスナーに登録してあるターゲットグループを削除する必要があります。

ただ既にリスナーに登録してあるターゲットグループを、そのまま削除しようとするとエラーになります。(AWSマネジメントコンソールで行った場合も同様の挙動になります)

そのため、まず設定値を変更したターゲットグループを新規に作成してから、ALBのリスナーでターゲットグループの変更(新旧の入れ替え)を行います。最後に不要になった古いターゲットグループは削除という流れになります。

これをTerraformでやってみます。

スポンサーリンク

NGなコード

おそらくターゲットグループを普通に書いたら以下のようになると思います。

## ターゲットグループ
resource "aws_lb_target_group" "demo" {
  name                          = "demo"
  port                          = "80"
  protocol                      = "HTTP"
...
}

## ターゲットグループを指定しているリスナー
resource "aws_lb_listener_rule" "ecs" {
  action {
    order            = "1"
    target_group_arn = aws_lb_target_group.demo.arn
    type             = "forward"
  }
...

これで一度ターゲットグループを作成したあとに、portやprotocolを変更しようとすると以下のようにエラーとなります。

$ terraform apply
...
Plan: 1 to add, 2 to change, 1 to destroy.
...
aws_lb_target_group.demo: Destroying... [id=arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/demo/xxx]
aws_lb_target_group.demo: Still destroying... [id=arn:aws:elasticloadbalancing:ap-northea...6155:targetgroup/demo/xxx, 10s elapsed]
aws_lb_target_group.demo: Still destroying... [id=arn:aws:elasticloadbalancing:ap-northea...6155:targetgroup/demo/xxx, 20s elapsed]
...
╷
│ Error: error deleting Target Group: ResourceInUse: Target group 'arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/demo/xxx' is currently in use by a listener or a rule
│ 	status code: 400, request id: xxx
│
│
╵
ERRO[0136] 1 error occurred:
	* exit status 1

$

実行ログからわかるように、真っ先に既にリスナーに登録してあるターゲットグループを削除しようとしているためエラーとなります。

OKなコード

lifecycleとname_prefixを使う

lifecyclecreate_before_destroy を追加します。

ただnameという固定値のままであると、新規に作成するターゲットグループの名前が既存のものと同じであるため重複エラーとなってしまいます。これを回避するためにname_prefixで名前を指定します。

## ターゲットグループ
resource "aws_lb_target_group" "demo" {
# name                          = "demo"
  name_prefix                   = "demo-"
  port                          = "80"
  protocol                      = "HTTP"
...
  lifecycle {
    create_before_destroy = true
  }
}

このコードを実行すると以下のとおり正常終了します。

$ terraform apply
...
Plan: 1 to add, 2 to change, 1 to destroy.
...
aws_lb_target_group.demo: Creating...
aws_lb_target_group.demo: Creation complete after 1s [id=arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/demo-20220606003112231700000001/xxx]
aws_lb_listener_rule.ecs: Modifying... [id=arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:listener-rule/app/demo/xxx]
...
aws_lb_listener_rule.ecs: Modifications complete after 0s [id=arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:listener-rule/app/demo/xxx]
aws_lb_target_group.demo (deposed object xxx): Destroying... [id=arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/demo/xxx]
aws_lb_target_group.demo: Destruction complete after 0s

Apply complete! Resources: 1 added, 2 changed, 1 destroyed.
$

実行ログより、まず新しいターゲットグループを作成してからリスナールールを変更、最後に古いターゲットグループを削除していることがわかります。

なおname_prefixで作成されるALB名はdemo-20220606003112231700000001のようにプレフィクスの後に26文字の一意な文字列が付与されます



name_prefixの代替手段

ターゲットグループの名前は最大 32 文字の英数字またはハイフンのみであるため実質name_prefixには6文字しか使用できません

7文字以上指定すると以下のようなエラーとなります。

│ Error: "name_prefix" cannot be longer than 6 characters
│
│   with aws_lb_target_group.demo,
│   on resource_alb.tf line 27, in resource "aws_lb_target_group" "demo":
│   27:   name_prefix                   = "demodemo"

タグを使う

そのため7文字以上をname_prefix扱いとしたい人は以下のようにタグで代替する案もあります。この場合nameやname_prefixの指定は不要です。自動的に一意な名前が生成されます。

resource "aws_lb_target_group" "demo" {
  # name_prefix                   = "demodemo"
..
  tags = {
    Name = "demodemo"
  }
...

ランダムな文字列を使う

こちらのように重複しないのように試行錯誤している方もいらっしゃるようです。

resource "random_id" "target-group-suffix" {
  keepers = {
    name = aws_lb.demo.name
    protocol = "HTTP"
    vpc_id = var.vpc-demo-id
    target_type = "ip"
  }
  byte_length = 8
}

resource "aws_lb_target_group" "demo" {
# demo-65ce7da6aaccb16d のような名前が生成されます
  name = "demo-${random_id.target-group-suffix.hex}"
...

Tips

Tipsというか僕が適当過ぎてハマったのですが、以下のようにaws_lb_listener_ruleのlifecycleでターゲットグループの変更を無視するようにしていると場合によっては実行エラーとなります。

resource "aws_lb_listener_rule" "ecs" {
  action {
    order            = "1"
    target_group_arn = aws_lb_target_group.demo.arn
    type             = "forward"
  }
...
  lifecycle {
    ignore_changes = [
      action[0].target_group_arn,
    ]
  }
}

もともとECSのBlueGreenデプロイ用に設定していたもの(デプロイのたびにターゲットグループが常に変わるので無視するようにしていた)で、それを無意識にコピペしていました。

このままだと変更後のターゲットグループがALBにアタッチされず、今回の検証ではローリングデプロイ方式のECSでしたが、以下のようなエラーとなりました。(Terraformで依存関係をちゃんと定義すれば回避できるかもしれません)

$ terraform apply
...
aws_lb_target_group.demo: Creating...
aws_lb_target_group.demo: Creation complete after 0s [id=arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/demo-20220524010712900900000001/xxx]
aws_ecs_service.demo: Modifying... [id=arn:aws:ecs:ap-northeast-1:12:service/demo/demo]
aws_ecs_service.demo: Still modifying... [id=arn:aws:ecs:ap-northeast-1:12:service/demo/demo, 10s elapsed]
..
aws_ecs_service.demo: Still modifying... [id=arn:aws:ecs:ap-northeast-1:12:service/demo/demo, 3m51s elapsed]
╷
│ Error: error updating ECS Service (arn:aws:ecs:ap-northeast-1:123456789012:service/demo/demo): InvalidParameterException: The target group with targetGroupArn arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/demo-20220524010712900900000001/xxx does not have an associated load balancer.
│
│   with aws_ecs_service.demo,
│   on resource_ecs.tf line 59, in resource "aws_ecs_service" "demo":
│   59: resource "aws_ecs_service" "demo" {
│
╵
ERRO[0259] 1 error occurred:
	* exit status 1

$

今回は以上です〜ノシ

参考

(`・ω・´)ノ アリガトウゴザイマス!!

aws_alb_target_group can`t recreate with attached listeners
Error deleting Target Group: ResourceInUse when changing target ports in AWS through Terraform