LAB07_AWS_SQS Queue with Dead Letter Queue and Access Policy

SQS Queue with Dead Letter Queue and Access Policy

☁ AWS ⭐ Beginner SQS Dead Letter Queue Queue Policy Message Retention

Scenario

Your application publishes job requests to an SQS queue and a worker process consumes them. Sometimes a message cannot be processed (corrupted data, temporary downstream failure). Instead of losing those messages, you want failed messages automatically moved to a Dead Letter Queue (DLQ) so they can be inspected and replayed later.

Your Objectives
  • Create a Dead Letter Queue (aws_sqs_queue) with 4-day message retention.
  • Create the main queue with a redrive policy pointing to the DLQ after maxReceiveCount = 3 failures.
  • Enable server-side encryption on both queues using SqsManagedSseEnabled.
  • Attach a queue policy to the main queue allowing the current AWS account to send messages.
  • Output both queue URLs and ARNs.

Additional Context

The redrive_policy in SQS is a JSON string containing the DLQ ARN and the maxReceiveCount. After a message is received (but not deleted) 3 times, SQS automatically moves it to the DLQ. This prevents poison-pill messages from blocking the queue. Use jsonencode() in Terraform to build this JSON safely.

versions.tf

terraform {
  required_version = ">= 1.5.0"
  required_providers {
    aws = { source = "hashicorp/aws", version = "~> 5.0" }
  }
}

variables.tf

variable "aws_region"       { type = string; default = "us-east-1" }
variable "queue_name"       { type = string; default = "job-queue" }
variable "environment"      { type = string; default = "dev" }
variable "max_receive_count"{ type = number; default = 3 }

main.tf

# main.tf

provider "aws" {
  region = var.aws_region
}

# Retrieve the current AWS account ID — needed for the queue policy.
data "aws_caller_identity" "current" {}

# Dead Letter Queue — receives messages that the main queue failed to process.
# Use a longer retention period so you have time to investigate failures.
resource "aws_sqs_queue" "dlq" {
  name                      = "${var.queue_name}-dlq-${var.environment}"
  message_retention_seconds = 345600   # 4 days (in seconds)

  # Encrypt messages at rest (SQS-managed keys — no cost).
  sqs_managed_sse_enabled = true

  tags = { Name = "${var.queue_name}-dlq", Environment = var.environment, ManagedBy = "terraform" }
}

# Main queue — receives new job messages from producers.
resource "aws_sqs_queue" "main" {
  name                       = "${var.queue_name}-${var.environment}"
  message_retention_seconds  = 86400    # 1 day
  visibility_timeout_seconds = 30       # worker has 30s to process each message

  sqs_managed_sse_enabled = true

  # Redrive policy: after maxReceiveCount failed receive attempts, move to DLQ.
  # jsonencode() creates valid JSON inline — no need for heredocs or raw strings.
  redrive_policy = jsonencode({
    deadLetterTargetArn = aws_sqs_queue.dlq.arn
    maxReceiveCount     = var.max_receive_count
  })

  tags = { Name = var.queue_name, Environment = var.environment, ManagedBy = "terraform" }
}

# Queue policy — IAM resource policy attached to the queue.
# This grants the AWS account's IAM principals the ability to use the queue.
data "aws_iam_policy_document" "queue_policy" {
  statement {
    sid    = "AllowAccountAccess"
    effect = "Allow"

    principals {
      type        = "AWS"
      identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"]
    }

    actions = [
      "sqs:SendMessage",
      "sqs:ReceiveMessage",
      "sqs:DeleteMessage",
      "sqs:GetQueueAttributes"
    ]

    resources = [aws_sqs_queue.main.arn]
  }
}

resource "aws_sqs_queue_policy" "main" {
  queue_url = aws_sqs_queue.main.id
  policy    = data.aws_iam_policy_document.queue_policy.json
}

outputs.tf

output "queue_url"  { value = aws_sqs_queue.main.id }
output "queue_arn"  { value = aws_sqs_queue.main.arn }
output "dlq_url"    { value = aws_sqs_queue.dlq.id }
output "dlq_arn"    { value = aws_sqs_queue.dlq.arn }

Workflow Commands

terraform init
terraform apply

# Send a test message
aws sqs send-message \
  --queue-url $(terraform output -raw queue_url) \
  --message-body '{"job_id": "123", "type": "report"}'

# Receive and delete the message (simulating a successful worker)
aws sqs receive-message --queue-url $(terraform output -raw queue_url)
aws sqs delete-message \
  --queue-url $(terraform output -raw queue_url) \
  --receipt-handle PASTE_RECEIPT_HANDLE_HERE

terraform destroy
✓ Expected Output After Apply
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Outputs:

dlq_arn   = "arn:aws:sqs:us-east-1:123456789012:job-queue-dlq-dev"
dlq_url   = "https://sqs.us-east-1.amazonaws.com/123456789012/job-queue-dlq-dev"
queue_arn = "arn:aws:sqs:us-east-1:123456789012:job-queue-dev"
queue_url = "https://sqs.us-east-1.amazonaws.com/123456789012/job-queue-dev"