fix: render Distribution functionAssociations; grant lambda:InvokeFunction for public Function URLs - #159
Conversation
…s before destroy
Distribution silently dropped functionAssociations: the prop was declared
but never rendered into the aws_cloudfront_distribution function_association
blocks, for the default and ordered cache behaviors alike. Render it, and
validate at most one association per event type per behavior.
Set create_before_destroy on aws_cloudfront_function so a name-forced
replacement no longer fails with FunctionInUse while the function is
attached to a distribution.
Add regression tests for FunctionCode.fromFile ${ -> $${ escaping
(including a synth-level assertion) and document that fromInline
intentionally passes ${ through for token interpolation.
Port the upstream aws-cdk integ.distribution-function.ts integration test;
it asserts the deployed distribution's FunctionAssociations via the
CloudFront API. Verified live: TestDistributionFunction passed.
Closes #99
Closes #50
FunctionUrl with authType NONE only added lambda:InvokeFunctionUrl, so every public Function URL returned 403 AccessDeniedException. AWS requires a second lambda:InvokeFunction statement (the console's FunctionURLInvokeAllowPublicAccess). The grant is scoped with the lambda:InvokedViaFunctionUrl condition key via the provider's invoked_via_function_url argument, exposed as a new optional invokedViaFunctionUrl prop on Permission, so it only applies to invocations arriving through the Function URL. Verified live: TestNodeJsFunctionUrl passed (public URL returns 200). Closes #98
Review findings with reproduction steps1.
|
…ject unpublished functions Address PR #159 review findings: - The default cache behavior's function_association is now produced via Lazy.anyValue so associations pushed onto a caller-held array after the Distribution constructor are still rendered at synth. The producer wraps each element with the generated ...FunctionAssociationToTerraform mapper (lazy tokens bypass the struct mapper, see #116). Duplicate-event-type validation for the default behavior consequently moves to synth time. - Associating a locally-created edge.Function with autoPublish: false now fails fast at synth for both default and ordered behaviors: CloudFront only allows LIVE-stage functions in cache behaviors, so the config would synth fine and fail at apply. Imported/general IFunction implementations are unverifiable and left alone. Exposed via a jsii-internal _autoPublish member on Function. Re-verified live: TestDistributionFunction passed after the change.
|
Both findings addressed in 27d35c0. 1. Eager default-behavior rendering (this thread + the constructor comment): 2. One deliberate trade-off to flag: the guard is a hard synth error with no escape hatch, so a user who sets Re-verified live after the change: |
|
I need to correct my earlier request on The current check infers the function's eventual I also want to narrow my subsequent example: “create the function, manually publish it with the AWS CLI, then associate it” is not a realistic default IaC workflow and should not be normalized as an ordinary silent path. A separately managed function stage should be treated as exceptional and made visible to the consumer—whether through an explicit acknowledgment of that ownership, a warning, or another deliberate contract. Rather than prescribing the mechanism, could you please reconsider this part of the implementation around the broader stage-lifecycle cases raised by vincenthsh: publication performed by another resource or pipeline, and distributions created only after the function is known to be |
…ledgeable warning Per review, the hard synth failure was too restrictive for intentionally separate stage management (publication by a pipeline or a later apply, distribution created only after the function is LIVE). Associating a locally-created edge.Function with autoPublish: false now emits an Annotations warning with the stable id prefix [terraconstructs/aws-edge:unpublishedFunctionAssociation] instead of throwing. Consumers acknowledge intentional out-of-band publication with a new skipPublishCheck flag on the association (skipPermissions-style contract); the acknowledgment is per association, so other accidental unpublished associations still warn. Imported functions remain unchecked. Warnings emitted inside the Lazy behavior producers are captured by the synth manifest (prepareStack resolves lazies before annotation collection; verified empirically) and deduped across the double resolution pass. TODO(#161) tracks migrating to an id-based addWarningV2/acknowledgeWarning Annotations facade.
|
Reworked in 4d78a07 — the hard failure is gone, replaced by an acknowledgeable warning. New behavior: associating a locally-created functionAssociations: [{
function: fn,
eventType: FunctionEventType.VIEWER_REQUEST,
skipPublishCheck: true, // publication managed out-of-band
}]The acknowledgment is deliberately per-association (contract modeled on On "could another resource in the construct tree publish it?" — verified against the provider (aws 6.58.0): there is no separate publish resource ( Why not Implementation notes: warnings are emitted inside the lazy behavior producers and land reliably in the synth manifest ( |
The unpublished-function check used `fa.function instanceof Function`, which silently skips the warning when the associated function comes from a duplicate installed copy of this library. Add the repo's cross-package runtime-identification pattern (AwsStack.isAwsStack / Role.isRole): a Symbol.for marker on Function.prototype and a static Function.isFunction type guard, and use it in Distribution.renderFunctionAssociations.
sakul-learning
left a comment
There was a problem hiding this comment.
Approved at f9a23d1.
src/aws/edge/distribution.ts now lazily renders default-cache functionAssociations through the generated provider mapper, so associations attached after construction are retained with the required event_type and function_arn fields. Duplicate event types remain rejected. Locally created unpublished functions produce an acknowledgeable warning rather than blocking separately managed publication, and Function.isFunction() now uses the repository’s Symbol.for(...) marker pattern so that advisory check survives compatible duplicate package copies.
The public Function URL path also grants the required lambda:InvokeFunction permission while restricting it to invocations through the Function URL.
The focused distribution suite passed 16/16 tests with 3/3 snapshots. JSII compilation and ESLint both pass. No blocking correctness, security, compatibility, or artifact-value findings remain.
Fixes #99, fixes #50, fixes #98.
edge: render
functionAssociationsand survive function replacement (#99, #50)Distribution._renderDefaultCacheBehaviornow rendersfunctionAssociationsinto the provider'sfunction_associationblocks (event_type+function_arn). Since default and ordered cache behaviors share this render path, the fix coversdefaultBehavior,additionalBehaviors, andaddBehavior()alike.FunctionEventTypeper behavior (CloudFront's actual constraint).edge.Functionnow setslifecycle { create_before_destroy = true }on theaws_cloudfront_functionresource, so a name-forced replacement no longer fails withFunctionInUse409 while attached to a distribution (per the workaround note on Distribution: functionAssociations not rendered to Terraform JSON #99).${→$${escaping issue from aws/edge: CDN FunctionAssociation does not work #50 was already fixed onmainforFunctionCode.fromFile(since v0.1.0); this PR adds regression tests for it, including a synth-level assertion that the escaped code survives intocdk.tf.json.fromInlineintentionally does not escape (inline code is often built from cdktn tokens); its doc comment now spells out the$${escaping rule for literal${.compute: public Function URLs no longer 403 (#98)
FunctionUrlwithauthType: NONEnow adds the second required permission —lambda:InvokeFunction— alongsidelambda:InvokeFunctionUrl.aws_lambda_permissionexposesinvoked_via_function_url, which maps 1:1 to thelambda:InvokedViaFunctionUrlcondition key the AWS Console uses in itsFunctionURLInvokeAllowPublicAccessstatement. The new grant is therefore scoped to invocations arriving via the Function URL only. Exposed as a new optionalinvokedViaFunctionUrlprop onPermission.Note: #98 is unrelated to the two edge issues but was bundled per request; it lives in its own commit.
Tests
test/aws/edge/function.test.ts(escaping regression incl. synth-level,create_before_destroy), extendedtest/aws/edge/distribution.test.ts(function_associationrendered on both default and ordered behaviors, duplicate-event-type validation on both eager and lazy paths), newtest/aws/compute/function-url.test.ts(both permissions forNONE, none forAWS_IAM, alias-qualified URL).integ/aws/edge/apps/distribution-function.ts+TestDistributionFunction(ported from upstream aws-cdkinteg.distribution-function.ts). It asserts the deployed distribution'sDefaultCacheBehavior.FunctionAssociationsvia the CloudFront API — the assertion that would have caught Distribution: functionAssociations not rendered to Terraform JSON #99 — plus aTestFunctioninvocation of the associated function.make nodejs-function-url(validates FunctionUrl with AuthType.NONE missing lambda:InvokeFunction permission — 403 on all Function URLs #98 end-to-end: public URL returns 200) andmake distribution-function— results below.Live integ results (account 694710432912, us-east-1, 2026-08-16):
Follow-up found during review
Alias.functionArnreturns the aliasinvoke_arn(the API-Gateway-style invocation ARN) instead of the alias ARN, which makes anyaddPermission()on anAliasemit an invalidaws_lambda_permission.function_name. Pre-existing, not addressed here; filed separately.