Skip to content

feat(logs): add L2 constructs for CloudWatch Logs vended log delivery - #38408

Open
letsgomeow wants to merge 4 commits into
aws:mainfrom
letsgomeow:feature/37544
Open

feat(logs): add L2 constructs for CloudWatch Logs vended log delivery#38408
letsgomeow wants to merge 4 commits into
aws:mainfrom
letsgomeow:feature/37544

Conversation

@letsgomeow

Copy link
Copy Markdown
Contributor

Issue # (if applicable)

Closes #37544.

Reason for this change

CloudWatch Logs vended log delivery lets services such as Amazon EKS, Amazon Bedrock, and Amazon
EventBridge deliver their logs directly to Amazon S3, CloudWatch Logs, or Kinesis Data Firehose,
without requiring an intermediate Lambda function or Kinesis stream. The underlying CloudFormation
resources (AWS::Logs::DeliverySource, AWS::Logs::DeliveryDestination, AWS::Logs::Delivery) had
no L2 coverage in aws-cdk-lib, forcing users onto raw Cfn* constructs and manual IAM wiring.

Description of changes

Adds three new L2 constructs to aws-cdk-lib/aws-logs:

  • DeliverySource — represents the AWS resource that emits logs (identified by ARN + LogType).
  • DeliveryDestination — represents the target that receives logs (S3 bucket, CloudWatch Logs log
    group, or Kinesis Data Firehose delivery stream), via DeliveryDestinationTarget.fromBucket() /
    .fromLogGroup() / .fromDeliveryStream().
  • Delivery — connects one DeliverySource to one DeliveryDestination and automatically grants
    the delivery.logs.amazonaws.com service principal the permissions it needs on the destination
    (bucket policy / log group resource policy), scoped to the source's ARN with a SourceArn
    condition (confused-deputy prevention). No grant is needed for Firehose — AWS creates the
    AWSServiceRoleForLogDelivery service-linked role automatically.

Also adds LogType and OutputFormat enum-like classes, both with a LogType.of() / OutputFormat
escape hatch for source/format values not yet exposed as static constants.

Design decisions / alternatives considered (for awslint.json excludes below):

Three awslint rules are excluded for this feature, following established precedent elsewhere in
the codebase (see @aws-cdk/aws-bedrock-agentcore-alpha, which excludes all three for the same
underlying reasons):

Rule Target Reason
props-no-arn-refs DeliverySourceProps.resourceArn A delivery source can be practically any AWS resource type (EKS cluster, Bedrock agent, EventBridge bus, Network Firewall, etc.) with no shared CDK construct interface across them — a raw ARN is the only common denominator.
props-physical-name DeliveryProps AWS::Logs::Delivery has no Name-equivalent property in CloudFormation; AWS assigns the identifier (DeliveryId) automatically.
prefer-ref-interface DeliveryProps.destination The Delivery constructor calls destination.grantWrite(source), which requires the full IDeliveryDestination interface — IDeliveryDestinationRef (bare identifiers only) doesn't expose grant methods. This exact pattern (a props field requiring the full interface because the constructor calls a grant* method on it) is also used independently in @aws-cdk/aws-bedrock-alpha's AgentProps.guardrail / IGuardrail.grantApply(), suggesting it's an established convention rather than a one-off deviation.

Alternatives considered and rejected:

  • Wrapping resourceArn in a DeliverySourceResource.fromArn()-style abstract class: rejected —
    there's no behavioral difference across resource types to justify the abstraction (a single
    fromArn() factory would be a speculative, empty wrapper).
  • Excluding Delivery from Resource/dropping the @resource tag to sidestep
    props-physical-name: rejected — would require hand-rolling IResource, breaks consistency with
    the sibling DeliverySource/DeliveryDestination constructs, and affects tooling that relies on
    @resource.

Describe any new or updated permissions being added

Delivery writes IAM permissions on the destination resource (not on any identity), scoped to the
delivery source's ARN via aws:SourceArn, trusting only the delivery.logs.amazonaws.com service
principal:

  • S3 destination: adds s3:GetBucketAcl, s3:ListBucket, s3:PutObject to the bucket policy.
  • CloudWatch Logs destination: adds logs:CreateLogStream, logs:PutLogEvents to a log group
    resource policy.
  • Kinesis Data Firehose destination: no new policy — AWS's own service-linked role
    (AWSServiceRoleForLogDelivery) covers this case.

Description of how you validated changes

  • Unit tests added: delivery-source.test.ts, delivery-destination.test.ts, delivery.test.ts
    (169 tests total in aws-logs, all passing), covering all three destination types, cross-account
    delivery destination policies, import (fromXxx) methods, and default/all-props construction.
  • Integration test added: integ.delivery.ts, deploying one DeliverySource (EventBridge event bus)
    fanned out to all three destination types (S3, CloudWatch Logs, Firehose) in a single stack.
    Deploy-only (no awsApiCall assertions), consistent with existing precedent for this class of
    confused-deputy IAM wiring (aws-cloudtrail, aws-elasticloadbalancingv2's integ.alb.log.ts) —
    the resource-policy content itself is already covered exhaustively by unit tests.
  • Manually verified end-to-end against a real AWS account (deployed with --no-clean): confirmed
    actual log records flowing from an EventBridge event bus through the delivery pipeline into all
    three destinations (S3 object, CloudWatch Logs stream, Firehose-delivered S3 object). Test stack
    has since been deleted.
  • README updated with a new "Log Delivery" section: basic example, LogType.of() escape hatch,
    per-destination-type examples, grant behavior, cross-account delivery, delivery options, and
    importing existing resources.

Checklist


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

Complete the remaining deliverables for the CloudWatch Logs vended log
delivery L2s (DeliverySource/DeliveryDestination/Delivery): an
integration test covering S3, CloudWatch Logs, and Firehose
destinations from a single EventBridge source, a new "Log Delivery"
README section, and awslint exclusions (with rationale, matching
aws-bedrock-agentcore-alpha precedent) for the three ARN/physical-name/
ref-interface deviations required by this feature's design.
@github-actions github-actions Bot added beginning-contributor [Pilot] contributed between 0-2 PRs to the CDK effort/medium Medium work item – several days of effort feature-request A feature should be added or improved. labels Jul 27, 2026
@github-actions github-actions Bot added the p2 label Jul 27, 2026
@aws-cdk-automation
aws-cdk-automation requested a review from a team July 27, 2026 04:36
@aws-cdk-automation aws-cdk-automation added the pr/needs-further-review PR requires additional review from our team specialists due to the scope or complexity of changes. label Jul 27, 2026
@aws-cdk-automation aws-cdk-automation added the pr/needs-community-review This PR needs a review from a Trusted Community Member or Core Team Member. label Jul 27, 2026

@mrgrain mrgrain left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aws-cdk-automation aws-cdk-automation removed the pr/needs-community-review This PR needs a review from a Trusted Community Member or Core Team Member. label Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

beginning-contributor [Pilot] contributed between 0-2 PRs to the CDK effort/medium Medium work item – several days of effort feature-request A feature should be added or improved. p2 pr/needs-further-review PR requires additional review from our team specialists due to the scope or complexity of changes.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

(aws-logs): L2 constructs for CloudWatch log delivery

3 participants