Terraform中Lambda与SQS事件源映射循环依赖问题及触发器UUID获取方法
Looks like you've hit a classic Terraform cycle dependency issue—let's break down why this is happening and how to fix it.
Why the Cycle Happens
The error stems from a circular reference chain:
- Your Lambda function tries to reference the trigger's UUID (
local.my_queue_trigger.uuid) in its environment variables before the trigger is created. - The Lambda trigger (
aws_lambda_event_source_mapping) needs the Lambda function to exist first to attach to it.
Terraform can't resolve which resource to create first, so it throws a cycle error.
Solution 1: Remove the Trigger UUID from Lambda Environment Variables (Simplest Fix)
If your Lambda function doesn't actually need the trigger's UUID at runtime, just remove the MY_TRIGGER_ID variable from the Lambda's environment block. This immediately breaks the cycle.
lambda.tf
locals { my_lambda = aws_lambda_function.vdf_lambda["my-lambda"] } # ... rest of your Lambda resource config ... environment { variables = { "MY_QUEUE_URL" = local.my_queue.id # Remove MY_TRIGGER_ID entirely } }
sqs.tf
locals { my_queue = aws_sqs_queue.fifo_queue["my-queue"] } resource "aws_sqs_queue" "fifo_queue" { # ... your SQS queue config ... } resource "aws_lambda_event_source_mapping" "my_lambda_trigger" { batch_size = 1 event_source_arn = aws_sqs_queue.fifo_queue["my-queue"].arn function_name = aws_lambda_function.vdf_lambda["my-lambda"].function_name # Reference directly instead of local for simplicity }
Solution 2: Fetch the Trigger UUID at Lambda Runtime (If You Need It)
If your Lambda function requires the trigger's UUID, don't pass it via environment variables. Instead, use the AWS SDK in your Lambda code to fetch the trigger info dynamically at runtime.
For example, in Python:
import boto3 import os def lambda_handler(event, context): lambda_client = boto3.client('lambda') # Fetch event source mappings for this Lambda function mappings = lambda_client.list_event_source_mappings( FunctionName=context.function_name, EventSourceArn=os.environ["MY_QUEUE_ARN"] # Add queue ARN to env vars instead of URL if needed ) # Get the trigger UUID (adjust index if you have multiple triggers) trigger_uuid = mappings['EventSourceMappings'][0]['UUID'] # Use the trigger UUID in your logic print(f"Trigger UUID: {trigger_uuid}")
Update your Lambda's environment variables to include the queue ARN instead of relying on the trigger UUID during deployment:
environment { variables = { "MY_QUEUE_URL" = local.my_queue.id "MY_QUEUE_ARN" = aws_sqs_queue.fifo_queue["my-queue"].arn } }
This eliminates the cycle entirely because the Lambda no longer depends on the trigger during creation.
Solution 3: Two-Step Deployment (For Strict Environment Variable Requirements)
If you absolutely must have the trigger UUID in the Lambda's environment variables, split your deployment into two steps to break the cycle.
Step 1: Deploy Lambda and SQS First
Add a toggle variable to control trigger deployment, then deploy without the trigger:
variables.tf
variable "deploy_trigger" { type = bool default = false }
lambda.tf
environment { variables = merge( { "MY_QUEUE_URL" = local.my_queue.id }, # Only add MY_TRIGGER_ID if we're deploying the trigger var.deploy_trigger ? { "MY_TRIGGER_ID" = aws_lambda_event_source_mapping.my_lambda_trigger[0].uuid } : {} ) }
sqs.tf
resource "aws_lambda_event_source_mapping" "my_lambda_trigger" { # Only create the trigger when deploy_trigger is true count = var.deploy_trigger ? 1 : 0 batch_size = 1 event_source_arn = aws_sqs_queue.fifo_queue["my-queue"].arn function_name = aws_lambda_function.vdf_lambda["my-lambda"].function_name }
Run the first deployment:
terraform apply -var deploy_trigger=false
Step 2: Deploy the Trigger and Update Lambda
Now deploy the trigger and let Terraform update the Lambda's environment variables with the generated UUID:
terraform apply -var deploy_trigger=true
This works because the Lambda already exists when Terraform creates the trigger, so there's no circular dependency anymore.
内容的提问来源于stack exchange,提问作者Osh Mansor

