How to Send AWS EventBridge Events to CloudWatch Logs

Ostensibly this is easy. Set an event rule target to a log group and it Just Works ™.

I suspect that’s probably the case in the AWS UI, but with infra as code there’s a missing piece: cloudwatch logs resource policies. There has to be a policy in place that allows events to delivery logs. In the UI, I suspect AWS does this for you, but without the UI, there is a bit of extra code.


data "aws_iam_policy_document" "cloudwatch-logs-from-events" {
  statement {
    effect = "Allow"
    actions = [
      "logs:CreateLogStream",
      "logs:PutLogEvents",
    ]
    resources = [
      "arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:log-group:/aws/events/*:*"
    ]

    principals {
      type = "Service"
      identifiers = [
        "events.amazonaws.com",
        "delivery.logs.amazonaws.com", // unclear if this is necessary, but every example I could find has it
      ]
    }
  }
}

resource "aws_cloudwatch_log_resource_policy" "cloudwatch-logs-from-events" {
  policy_document = data.aws_iam_policy_document.cloudwatch-logs-from-events.json
  policy_name     = "${var.app}-${var.env}-cloudwatch-logs-from-events"
}

Note that I attemped confused deputy protection in the above via an aws:SourceAccount condition and it did not work.

This allows events to send to /aws/events/* log groups.

With this in place connecting a rule target to a cloudwatch logs will work.

resource "aws_cloudwatch_log_group" "ses-events" {
  name              = "/aws/events/example"
  retention_in_days = 14
}

resource "aws_cloudwatch_event_rule" "example" {
  name        = "example"

  event_pattern = jsonencode({
    // CHANGEME
  })
}

resource "aws_cloudwatch_event_target" "ses-events-cloudwatch" {
  target_id = "example"
  rule      = aws_cloudwatch_event_rule.example.name
  arn       = aws_cloudwatch_log_group.example.arn
}