Skip to content

(lambda-web-function-alpha): add L2 constructs for Lambda Web Functions #39234

Open

Description

@davide-desio-eleva

Describe the feature

Add an L2 construct library for Lambda Web Functions, which run a standard HTTP server on Lambda
without rewriting it as an event handler.

CloudFormation already exposes the resources (AWS::Lambda::WebFunction, AWS::Lambda::WebFunctionRevision,
AWS::Lambda::WebFunctionEndpoint, AWS::Lambda::ResourcePolicy), but CDK has no higher-level construct for them.

I have a prototype in an alpha package, @aws-cdk/aws-lambda-web-function-alpha, and I plan to open a PR for it.

Use Case

Deploying an HTTP application (for example Express) on Lambda currently means writing an event handler or
adapter. With an L2 the application can be deployed as-is, with an endpoint, traffic splitting and access
control expressed in code:

const fn = new WebFunction(this, 'WebFunction', {
  code: WebFunctionCode.fromAsset(path.join(__dirname, 'app')),
  timeout: Duration.seconds(30),
});
const endpoint = new WebFunctionEndpoint(this, 'Endpoint', { webFunction: fn });
endpoint.grants.invoke(role);

Proposed Solution

Prototype (alpha package):

  • WebFunction: function, revision (code, runtime, timeout, environment, logging, KMS) and execution role.
  • WebFunctionEndpoint: HomeRegion / MultiRegion / PerRegion, IamAuth / ApplicationManaged, revision
    weights, scaling and throttling, with validation at synth time.
  • grants.invoke() on endpoints (lambda:InvokeWebFunctionEndpoint).
  • Import methods for existing functions and endpoints.
  • Resource-based policies through the L1 CfnResourcePolicy.

Workaround to be removed: the Web Functions service cannot read assets in the CDK bootstrap bucket, and the
current bootstrap template does not grant that access. Deploying an asset fails with
lambda service principal lambda.amazonaws.com does not have s3:GetObject ... on s3://cdk-hnb659fds-assets-....
Until bootstrap grants the access, fromAsset copies the asset into a versioned bucket owned by the web function.
A bootstrap update that grants the service read access would let this copy be removed.

Other Information

  • I verified the CloudFormation side in a test account in eu-west-1: an AWS::Lambda::ResourcePolicy with a web
    function ARN as ResourceArn is accepted, and it reads back with the CloudControl API.
  • Rerunning the bootstrap does not fix the asset issue: the current bootstrap template (version 32) does not grant
    the service read access to the asset bucket.
  • Open questions: whether the bootstrap template should grant the Web Functions service read access to the asset
    bucket, and where the alpha package should live long term.

Acknowledgements

  • I may be able to implement this feature request
  • This feature might incur a breaking change

AWS CDK Library version (aws-cdk-lib)

main at 805bfbe (unreleased, built from source)

AWS CDK CLI version

2.1129.0

Environment details (OS name and version, etc.)

macOS (Darwin 23.6.0), Node.js 22.13.1, Apple Silicon

Update

  • The prototype now also requires authType on endpoints (no default), grants kms:Decrypt to the service on a
    customer managed key, validates versioning and encryption for fromBucket, and limits the code bucket policy to
    the function's revisions (aws:SourceArn).
  • The prototype now includes NetworkConnector and VPC egress (egressNetworkConnector), which pins the revision
    to the connector's LatestVersionArn. The connector must be in the same stack as the function.
  • Temporary: the pinned @aws-cdk/aws-service-spec (0.1.218) does not have the revision's
    EgressNetworkConnectorArn, and its validator does not know the WebFunction compute type or the connector's
    LatestVersionArn. The CloudFormation registry schemas for AWS::Lambda::NetworkConnector and
    AWS::Lambda::WebFunctionRevision are therefore kept in tools/@aws-cdk/spec2cdk/temporary-schemas/us-east-1/.
    The latest published spec (0.1.221) has these, so the maintainers can remove the schema files once it is bumped.
    We have verified the connector and egress with a deploy in a test account.

Update 2

  • The code bucket is now shared by the web functions of a stack, and fromAssetToProvidedBucket copies code into a
    bucket the caller provides.
  • NetworkConnector.fromNetworkConnectorVersionArn imports a connector version, including from another stack.
  • Egress is rejected at synth for endpoints other than HomeRegion.
  • The prototype is an alpha package on purpose: the maintainers can decide whether to adopt it

Update 3

  • Implementation is in PR feat(lambda): add alpha module for Lambda Web Functions #39235 (alpha package @aws-cdk/aws-lambda-web-function-alpha).
  • Revisions are retained on removal (DeletionPolicy and UpdateReplacePolicy set to Retain). Without this, replacing
    a revision that an endpoint still routes to, for example a canary, fails with 409 ConflictException. We reproduced
    the failure and confirmed the fix in a test account.
  • The service read statement is now added to buckets passed to fromAssetToProvidedBucket and fromBucket, not only to
    the shared code bucket.
  • Callers of IamAuth endpoints must send the X-Amz-Content-SHA256 header when signing requests. Without it the
    endpoint answers 403 even with a valid signature. The README documents this.
  • Verified in eu-west-1: the integration test deploys and destroys cleanly, a signed request to an IamAuth endpoint
    returns 200, and an unsigned one returns 403.

Update 4

  • The copy custom resource handler is now written in TypeScript and compiled by the package build, instead of hand-written
    JavaScript. Its tests cover the copy, the missing version ID error, and the no-op on delete.
  • Checked again in eu-west-1 after the change: a web function from fromAsset, one from fromAssetToProvidedBucket,
    and an IamAuth endpoint with a resource policy. The endpoints answer as expected, and the test stacks were destroyed.
  • Six prefer-ref-interface exclusions remain, each marked in the JSDoc of its property with the reason it keeps the full
    interface.
  • autoDeploymentMode is not exposed: it is not in the service spec or the CloudFormation registry schema, and
    CloudFormation rejects it as an unsupported property.

Update 5 — CloudFront follow-up (planned)

CloudFront origin for web function endpoints (follow-up)

This work is not in this PR. It is a separate change built on top of this one, because it uses the WebFunctionEndpoint types defined here. main does not have these types yet.

Goal: let a CloudFront distribution front an IamAuth endpoint with origin access control (OAC), so that callers reach the endpoint through CloudFront without signing requests themselves.

API (in this alpha package):

  • WebFunctionEndpointOrigin.withOriginAccessControl(endpoint: IWebFunctionEndpoint, props?: WebFunctionEndpointOriginProps): cloudfront.IOrigin, modelled on FunctionUrlOrigin.withOriginAccessControl in aws-cdk-lib/aws-cloudfront-origins.
  • WebFunctionEndpointOriginProps extends the standard origin props used by OriginBase: originAccessControl?, originPath?, customHeaders?, connectionAttempts?, connectionTimeout?, readTimeout?, keepaliveTimeout?. Each one carries a @default tag.
  • When no OAC is passed, the origin creates a cloudfront.CfnOriginAccessControl with origin type lambda, signing behavior always-amz-auth, and signing protocol sigv4.

Behavior:

  1. The origin domain is the endpoint's DomainName.
  2. In bind(), the origin adds one statement to the resource policy of the web function: Principal: cloudfront.amazonaws.com, Action: lambda:InvokeWebFunctionEndpoint, Resource: the endpoint ARN, Condition: StringEquals on aws:SourceArn = the distribution ARN, using options.distributionId, as FunctionUrlOrigin does.
  3. The web function has one resource policy, and put-resource-policy replaces the whole policy. So the statements of all origins on the same function go into one AWS::Lambda::ResourcePolicy owned by the function, not one resource per origin. Each origin registers its statement with a shared object on the function (the same pattern as the code bucket policy in code.ts). That object emits the single policy once, at synth.

Validation (synth time):

  • The endpoint must be IamAuth. An ApplicationManaged endpoint is not evaluated against the resource policy, so the origin fails with a clear error.
  • The endpoint must be HomeRegion or PerRegion. MultiRegion uses SigV4a, and CloudFront support for it is not verified, so it is out of scope for this change.

Tests:

  • Unit: the OAC has origin type lambda, always-amz-auth and sigv4. The resource policy has the CloudFront principal and the aws:SourceArn condition for the distribution.
  • Unit: two distributions on the same function produce one resource policy with two statements.
  • Unit: an ApplicationManaged endpoint fails at synth; a MultiRegion endpoint fails at synth.
  • Integration (integ.web-function-cloudfront.ts): one IamAuth endpoint behind one distribution. Deployed in eu-west-1, checked, destroyed. Check that a request through the distribution returns 200 and a direct unsigned request to the endpoint returns 403.

Docs: a "CloudFront" section in the README with a compiling example (rosetta), and a note that the CloudFront origin must forward the Authorization header, as the Lambda access control guide describes.

Acceptance criteria:

  • All tests above pass, and the integration test passes with a real deployment.
  • No change to the existing API of this package.
  • awslint shows no new errors.

Out of scope: MultiRegion endpoints, custom domains on the function (CloudFront is the documented way to add one), caching policy design beyond the defaults, and the stable aws-cloudfront-origins package. Moving the origin to aws-cdk-lib would be a separate proposal, after this one is in use.

Dependencies and open points:

  • Depends on this PR being merged, or on a stacked PR.
  • No spec bump is needed: the cloudfront OAC type lambda is already in aws-cdk-lib.
  • Open: whether the resource policy merge should live on the web function (proposed) or in a small helper in this package. This needs a maintainer decision before implementation.

Activity

  1. pahud commented on Oct 23, 2026

    @pahud
    Contributor

    Hi @davide-desio-eleva,

    Thanks for the very detailed work here and in #39235, especially the deploy-verified findings. As @matboros mentioned on #39235, the service team's #39210 covers the same L2 in aws-cdk-lib/aws-lambda, so let's consolidate there and keep this issue as the tracking issue for the feature (#39210 doesn't link one yet).

    From a quick look at #39210's patches, a few of your findings may not be covered yet. Bringing them over as review comments there would be very valuable:

    The bootstrap asset-bucket access is a separate question for aws/aws-cdk-cli, and a dedicated issue there would help both implementations drop the code copy.

  2. added
    feature-requestA feature should be added or improved.
    effort/largeLarge work item – several weeks of effort
    and removed
    needs-triageThis issue or PR still needs to be triaged.
    on Oct 23, 2026
  3. davide-desio-eleva commented on Oct 26, 2026

    @davide-desio-eleva
    Author

    Thanks @pahud. I've opened the three points as review comments on #39210: revision retention with the 409 reproduction, the single resource-based policy statement collector needed for CloudFront, and the X-Amz-Content-SHA256 requirement for IamAuth callers.

    I'll keep this issue as the reference for the feature. If the maintainers want to link it to #39210, that works for me, and I'm fine with closing #39235 in favour of #39210.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    @aws-cdk/aws-lambdaRelated to AWS Lambdaeffort/largeLarge work item – several weeks of effortfeature-requestA feature should be added or improved.p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions