Skip to content

fix: apply Datadog env vars via synth-time CDK aspect#622

Draft
ava-silver wants to merge 4 commits into
mainfrom
ava.silver/svls-9359/poc-apply-env-vars-via-cdk-aspect
Draft

fix: apply Datadog env vars via synth-time CDK aspect#622
ava-silver wants to merge 4 commits into
mainfrom
ava.silver/svls-9359/poc-apply-env-vars-via-cdk-aspect

Conversation

@ava-silver

@ava-silver ava-silver commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Proof of concept -- alternative to #621. Demonstrates option 3 (CDK Aspects) from the design discussion. Not intended to merge as-is; opened to evaluate the approach and its cost against the WeakMap approach in #621.

What does this PR do?

Applies Datadog Lambda environment variables from a synth-time CDK Aspect instead of eagerly during addLambdaFunctions. This removes all reads of aws-cdk-lib's private Function.environment field and preserves the original "don't override user-set env vars" behavior -- for vars set both before and after addLambdaFunctions.

Motivation

aws-cdk-lib changed Function.environment's internal shape in a semver-minor (2.252 -> 2.253), breaking cdk synth (#596). The root cause is that the library reads that private field to avoid clobbering user-set env vars, and aws-cdk-lib exposes no public API to read env vars eagerly.

#621 fixes the crash with a WeakMap that tracks the library's own writes, but it cannot see env vars a user sets via func.addEnvironment() before addLambdaFunctions() -- a documented behavior change. This PR explores keeping that behavior.

How it works

A CDK Aspect runs during synthesis, after all user addEnvironment calls have executed (regardless of ordering). At that point the function's env vars can be read through the public token-resolution API -- Stack.of(fn).resolve(cfnFunction.environment) -- which returns the fully-resolved { Variables: {...} }. The aspect fills in Datadog defaults only for keys the user hasn't set, then appends source code integration tags to DD_TAGS.

Changes

src/env-aspect.ts (new) -- DatadogLambdaEnvAspect implementing IAspect. Reads resolved env, applies defaults, appends SCI tags. Git overrides are read lazily via getter closures so overrideGitMetadata works regardless of call order.

src/env.ts

  • readResolvedFunctionEnv(fn) -- public-API read helper (Stack.resolve).
  • getSourceCodeIntegrationTags(...) / applySourceCodeIntegration(...) -- git tag computation split out, reads existing DD_TAGS from the passed-in resolved env map.
  • applyEnvVariables(fn, baseProps, existingEnv) -- now takes the resolved env map instead of reading the private field.

src/datadog-lambda.ts

  • addLambdaFunctions registers the aspect (once per stack) and adds functions to its target set instead of applying env vars eagerly. Prop-driven unconditional writes stay eager.
  • overrideGitMetadata just records overrides -- the aspect builds DD_TAGS fresh at synth, so no rewriting of already-added functions (and no private field access).
  • addGitCommitMetadata registers the given functions with the aspect.

Behavior vs #621

#621 (WeakMap) This PR (Aspect)
Reads private Function.environment No No
DatadogLambdaProps config Preserved Preserved
addEnvironment after addLambdaFunctions Preserved Preserved
addEnvironment before addLambdaFunctions Overridden (behavior change) Preserved
Approach Track own writes Read resolved env at synth

The entire existing test suite passes without changing the env-var assertions -- including the "does not override user-defined env variables before/after" tests that #621 had to rewrite. Only the overrideGitMetadata tests changed, to assert against Template.fromStack() instead of reading the private field directly.

Tradeoffs / open questions

  • More moving parts. Env application is deferred to synth and split across eager writes + an aspect, which is harder to follow than a straight-line addLambdaFunctions.
  • Timing of validation. The datadogAppSecMode: tracer runtime check in applyEnvVariables now throws at synth rather than at addLambdaFunctions.
  • resolve() on tokenized values. Env values that are CDK tokens resolve to ${Token[...]} strings; presence checks are unaffected, but appending to a tokenized DD_TAGS would be odd (edge case, same as before).
  • Aspect registration scope. Registered per function stack; revisit for cross-stack setups.

Testing Guidelines

yarn test -- full suite passes (240 tests). Manual cdk synth on an example stack recommended before considering for real.

Types of Changes

  • Bug fix
  • Misc (architecture exploration / POC)

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@datadog-datadog-prod-us1-2

This comment has been minimized.

@ava-silver ava-silver changed the title [SVLS-9359] POC: apply Datadog env vars via synth-time CDK aspect fix: apply Datadog env vars via synth-time CDK aspect Jun 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant