Terraform
CloudWatch Logs
Lambda
Cloud Infrastructure
AWS Integration

Terraform configuring cloudwatch log subscription delivery to lambda?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

To send CloudWatch Logs events to Lambda with Terraform, you need more than just the log group and the function. The essential wiring is a log subscription filter plus explicit permission allowing CloudWatch Logs to invoke the Lambda function. Most failed setups come from missing that permission or from creating the resources in the wrong dependency order.

The Required Pieces

A working setup typically needs:

  • an existing CloudWatch log group
  • a Lambda function
  • 'aws_lambda_permission allowing logs.amazonaws.com to invoke the function'
  • 'aws_cloudwatch_log_subscription_filter connecting the log group to the function'

The subscription filter is the delivery rule, but it will not work unless Lambda permits that source principal to call it.

Define the Lambda Function and Log Group

A minimal example starts with the function and the log group.

hcl
1resource "aws_lambda_function" "processor" {
2  function_name = "log-processor"
3  role          = aws_iam_role.lambda_exec.arn
4  handler       = "index.handler"
5  runtime       = "python3.12"
6  filename      = "lambda.zip"
7  source_code_hash = filebase64sha256("lambda.zip")
8}
9
10resource "aws_cloudwatch_log_group" "app" {
11  name = "/aws/app/example"
12}

In real code the function may already exist, but the subscription logic is the same.

Grant CloudWatch Logs Permission to Invoke Lambda

This resource is easy to miss and is often the real fix.

hcl
1resource "aws_lambda_permission" "allow_logs" {
2  statement_id  = "AllowExecutionFromCloudWatchLogs"
3  action        = "lambda:InvokeFunction"
4  function_name = aws_lambda_function.processor.function_name
5  principal     = "logs.amazonaws.com"
6  source_arn    = "${aws_cloudwatch_log_group.app.arn}:*"
7}

The source_arn restriction is important. It scopes the permission to that log group instead of allowing arbitrary log groups to invoke the function.

Create the Subscription Filter

Now connect the log group to Lambda.

hcl
1resource "aws_cloudwatch_log_subscription_filter" "to_lambda" {
2  name            = "app-to-lambda"
3  log_group_name  = aws_cloudwatch_log_group.app.name
4  filter_pattern  = ""
5  destination_arn = aws_lambda_function.processor.arn
6
7  depends_on = [aws_lambda_permission.allow_logs]
8}

An empty filter_pattern means all log events are forwarded. If you want only matching events, use a specific CloudWatch Logs filter pattern.

The explicit depends_on is helpful because the subscription can fail if Terraform tries to create it before the invoke permission exists.

Be Careful with Existing Log Groups

Sometimes the log group already exists because another service created it automatically. In that case you may want to reference it with a data source or import it into state rather than trying to recreate it.

The important idea is that the subscription filter attaches to the real log group name currently in AWS. Terraform resource ownership is a separate concern from the AWS wiring requirement.

Test the Whole Flow, Not Just Terraform Apply

A successful terraform apply does not prove that events are actually reaching the function. After deployment, generate a test log event and check Lambda logs or metrics.

Operationally, verify:

  • the subscription filter exists on the intended log group
  • Lambda invocation metrics increase
  • the Lambda handler can decode CloudWatch Logs payloads

That last point matters because CloudWatch Logs delivers a compressed, encoded event wrapper, not plain log lines.

Common Pitfalls

  • Creating the subscription filter without first granting Lambda permission to logs.amazonaws.com.
  • Using the wrong source_arn and accidentally preventing the log group from invoking the function.
  • Assuming terraform apply success means the Lambda is processing events correctly.
  • Forgetting that CloudWatch Logs payloads arrive wrapped and encoded rather than as raw text lines.
  • Trying to manage an already existing log group as a new Terraform resource without importing or referencing it properly.

Summary

  • The key Terraform resources are the log group, Lambda function, invoke permission, and subscription filter.
  • 'aws_lambda_permission is required so CloudWatch Logs can invoke the function.'
  • The subscription filter connects the log group to the Lambda destination.
  • Add a dependency so Terraform does not create the subscription before permission exists.
  • Validate the end-to-end event flow after apply instead of stopping at infrastructure creation.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.