継続は力なり

タイトル通り定期的な更新を心掛けるブログです。

Terraform で SQS のメインキューとデッドレターキューを同時に作成する時ハマったことと解決策をまとめる

タダです.

SQS で非同期処理を作ろうとするときにセットで非同期処理をリトライできるようにデッドレターキューを作ることがあると思います.Terraform でデッドレターキューを作る時にハマったことがあり,その経験から次にこうすれば Terraform でデッドレターキューを構成しきれる経験を得られたのでこの記事でまとめていきます.

デッドレターキューとメインキューの作成

デッドレターキューを設定するためにはメインのキューとデッドレターキューの2つのキューを作成する必要があります.2つのキューはそれぞれの ARN を参照しあう必要があるため,2つのキューを別々に作るか,同時に作成する必要があります.今回は同時作成時に Terraform の記述でハマったことがありましたので紹介します.

ハマったこと1: 循環参照エラーになる

Terrraform でお互いのリソースを参照し合うように記載するような記述は同時に作る場合において実行計画の段階で循環参照エラーになります.

resource "aws_sqs_queue" "main_queue" {
  name                      = "my-sqs-queue"
  delay_seconds             = 90
  max_message_size          = 2048
  message_retention_seconds = 86400
  receive_wait_time_seconds = 10
  redrive_policy = jsonencode({
    deadLetterTargetArn = aws_sqs_queue.dead_letter_queue.arn
    maxReceiveCount     = 3
  })
  tags = {
    Name = "blog-sqs"
  }
}
resource "aws_sqs_queue" "dead_letter_queue" {
  name = "my-sqs-queue-dead-letter"
  redrive_allow_policy = jsonencode({
    redrivePermission = "byQueue",
    sourceQueueArns   = [aws_sqs_queue.main_queue.arn]
  })
  tags = {
    "Name" = "blog-sqs-dead-letter"
  }
}

terraform plan 実行結果

terraform plan                                                                                                                                                                   
Releasing state lock. This may take a few moments...
│ Error: Cycle: aws_sqs_queue.main_queue, aws_sqs_queue.dead_letter_queue

関連情報

registry.terraform.io

ハマったこと2:terraoform apply 時にエラーが発生

次に一部 ARN を直指定するように変えてみました.デッドレターキューの ARN が作られた想定で指定しています.

resource "aws_sqs_queue" "main_queue" {
  name                      = "my-sqs-queue"
  delay_seconds             = 90
  max_message_size          = 2048
  message_retention_seconds = 86400
  receive_wait_time_seconds = 10
  redrive_policy = jsonencode({
    deadLetterTargetArn = "arn:aws:sqs:ap-northeast-1:123456789012:my-sqs-queue-dead-letter",
    maxReceiveCount     = 3
  })
  tags = {
    Name = "blog-sqs"
  }
}
resource "aws_sqs_queue" "dead_letter_queue" {
  name = "my-sqs-queue-dead-letter"
  redrive_allow_policy = jsonencode({
    redrivePermission = "byQueue",
    sourceQueueArns   = [aws_sqs_queue.main_queue.arn]
  })
  tags = {
    "Name" = "blog-sqs-dead-letter"
  }
}

このように記述すると実行計画は成功します.これで terraform apply してみましょう.

terraform plan 実行結果

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_sqs_queue.dead_letter_queue will be created
  + resource "aws_sqs_queue" "dead_letter_queue" {
      + arn                               = (known after apply)
      + content_based_deduplication       = false
      + deduplication_scope               = (known after apply)
      + delay_seconds                     = 0
      + fifo_queue                        = false
      + fifo_throughput_limit             = (known after apply)
      + id                                = (known after apply)
      + kms_data_key_reuse_period_seconds = (known after apply)
      + max_message_size                  = 262144
      + message_retention_seconds         = 345600
      + name                              = "my-sqs-queue-dead-letter"
      + name_prefix                       = (known after apply)
      + policy                            = (known after apply)
      + receive_wait_time_seconds         = 0
      + redrive_allow_policy              = (known after apply)
      + redrive_policy                    = (known after apply)
      + sqs_managed_sse_enabled           = (known after apply)
      + tags                              = {
          + "Name" = "blog-sqs-dead-letter"
        }
      + tags_all                          = {
          + "Name" = "blog-sqs-dead-letter"
        }
      + url                               = (known after apply)
      + visibility_timeout_seconds        = 30
    }

  # aws_sqs_queue.main_queue will be created
  + resource "aws_sqs_queue" "main_queue" {
      + arn                               = (known after apply)
      + content_based_deduplication       = false
      + deduplication_scope               = (known after apply)
      + delay_seconds                     = 90
      + fifo_queue                        = false
      + fifo_throughput_limit             = (known after apply)
      + id                                = (known after apply)
      + kms_data_key_reuse_period_seconds = (known after apply)
      + max_message_size                  = 2048
      + message_retention_seconds         = 86400
      + name                              = "my-sqs-queue"
      + name_prefix                       = (known after apply)
      + policy                            = (known after apply)
      + receive_wait_time_seconds         = 10
      + redrive_allow_policy              = (known after apply)
      + redrive_policy                    = jsonencode(
            {
              + deadLetterTargetArn = "arn:aws:sqs:ap-northeast-1:123456789012:my-sqs-queue-dead-letter"
              + maxReceiveCount     = 4
            }
        )
      + sqs_managed_sse_enabled           = (known after apply)
      + tags                              = {
          + "Name" = "blog-sqs"
        }
      + tags_all                          = {
          + "Name" = "blog-sqs"
        }
      + url                               = (known after apply)
      + visibility_timeout_seconds        = 30
    }

Plan: 2 to add, 0 to change, 0 to destroy.

しかし,デッドレターキューが存在しないというエラーがでてしまい,terraform apply が失敗します.この事態に遭遇してから Terraform でデッドレターキューとメインキューを同時に作成するのが難しいのだと勘違いして,泣きながら terraform import していました.

terraform apply 実行結果

Plan: 2 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_sqs_queue.main_queue: Creating...
╷
│ Error: creating SQS Queue (my-sqs-queue): InvalidParameterValue: Value {"deadLetterTargetArn":"arn:aws:sqs:ap-northeast-1:123456789012:my-sqs-queue-dead-letter","maxReceiveCount":3} for parameter RedrivePolicy is invalid. Reason: Dead letter target does not exist.
│       status code: 400, request id: 3549a839-d9ae-552e-929b-f4b6f7a0bdff
│ 
│   with aws_sqs_queue.main_queue,
│   on sqs.tf line 1, in resource "aws_sqs_queue" "main_queue":
│    1: resource "aws_sqs_queue" "main_queue" {
│ 
╵
Releasing state lock. This may take a few moments...

解決策: メインキューではデッドレターキューのリソース ARN を指定し,デッドレターキューでは ARN を直書き

それでは解決したアプローチなのですが,ハマリポイント2の記述のメインキューからデッドレターキューの ARN を直書きだったところをリソース ARN で指定して,デッドレターキューのメインキュー ARN 参照を直書きしました.これでメインキューとデッドレターキューの同時作成が成功するのですが,terraform apply の進み方とハマリポイント2での terraform apply の進み方で違いがあり,前者はデッドレターキューから作り始め,後者はメインキューから作り始めています.つまりリソース ARN を指定している場合はそのリソースから作られ,ARN を直書きにした場合はその後に作られる動作になっているのだとわかりました.この順番であればデッドレターキューが先に存在し,参照するメインキューもデッドレターキューが存在しているから作ることができるのだと学びました.

resource "aws_sqs_queue" "main_queue" {
  name                      = "my-sqs-queue"
  delay_seconds             = 90
  max_message_size          = 2048
  message_retention_seconds = 86400
  receive_wait_time_seconds = 10
  redrive_policy = jsonencode({
    deadLetterTargetArn = aws_sqs_queue.dead_letter_queue.arn,
    maxReceiveCount     = 3
  })
  tags = {
    Name = "blog-sqs"
  }
}
resource "aws_sqs_queue" "dead_letter_queue" {
  name = "my-sqs-queue-dead-letter"
  redrive_allow_policy = jsonencode({
    redrivePermission = "byQueue",
    sourceQueueArns   = ["arn:aws:sqs:ap-northeast-1:123456789012:my-sqs-queue"]
  })
  tags = {
    "Name" = "blog-sqs-dead-letter"
  }
}

terraform apply 実行結果

Plan: 2 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_sqs_queue.dead_letter_queue: Creating...
aws_sqs_queue.dead_letter_queue: Still creating... [10s elapsed]
aws_sqs_queue.dead_letter_queue: Still creating... [20s elapsed]
aws_sqs_queue.dead_letter_queue: Creation complete after 26s [id=https://sqs.ap-northeast-1.amazonaws.com/123456789012/my-sqs-queue-dead-letter]
aws_sqs_queue.main_queue: Creating...
aws_sqs_queue.main_queue: Still creating... [10s elapsed]
aws_sqs_queue.main_queue: Still creating... [20s elapsed]
aws_sqs_queue.main_queue: Creation complete after 26s [id=https://sqs.ap-northeast-1.amazonaws.com/123456789012/my-sqs-queue]
Releasing state lock. This may take a few moments...

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

まとめ

Terraform でメインキューとデッドレターキューを同時作成する時のハマったと事と解決策をまとめました.ネットで検索したときはパッと出てこなかったので,参考になることがあれば幸いです.