Problem
There is no L2 construct for CloudFront Origin Access Control (OAC) anywhere in src/. The only origin-access primitive is the legacy Origin Access Identity (OAI) at src/aws/storage/origin-access-identity.ts.
This is not merely a missing convenience — the private-S3-origin path is hard-blocked on OAI. S3BucketOrigin throws in its constructor when the bucket has no OAI (src/aws/edge/origin.ts:311-317):
if (!this.bucket.bucketOutputs.originAccessIdentity) {
throw new Error(
"The bucket must have an origin access identity to be used as a CloudFront origin. " +
"If the bucket is imported, you must include the originaccess identity in the import options.",
);
}
and renderS3OriginConfig() can only emit originAccessIdentity (origin.ts:320-326). So there is no way to express "private bucket + CloudFront + OAC" with L2s, even by opting out.
Verified at c2bd9c7 (current main):
$ grep -rniI "originaccesscontrol|origin_access_control" src/ --include="*.ts" -l
src/aws/storage/origin-access-identity.ts # legacy OAI only — no OAC anywhere
Why it matters
OAI is AWS-legacy; OAC is the documented, recommended mechanism and is required, not optional, for several common cases:
- SSE-KMS encrypted origin buckets — OAI cannot sign KMS requests; OAC can.
- Regions launched after December 2022 (opt-in regions), which are SigV4-only.
PUT/POST to the origin, and dynamic-request signing generally.
New stacks that follow current AWS guidance therefore cannot be expressed in TerraConstructs today without dropping to L1.
Concrete downstream consumer
KiroCrew's artifact-publishing stack (kirodotdev/KiroCrew, src/kiro_crew/deploy/) provisions private S3 + CloudFront + OAC with a bucket policy pinned via AWS:SourceArn, in both of its code paths (skills/artifact-deploy/templates/base-stack.yaml, and engine.py:212-410). Porting that to TerraConstructs is currently blocked on this gap. It is also the same stack that motivated #99 / #50 (thanks for the functionAssociations fix in #159 — that half is now unblocked on main).
Workaround in use
Drop to the L1 cloudfrontOriginAccessControl resource from @cdktn/provider-aws (present as of v24.8.0 / AWS provider 6.52+), wire renderS3OriginConfig manually via an escape hatch, and hand-write the bucket policy AWS:SourceArn condition against the distribution ARN. The BucketPolicy L2 and iam.PolicyStatement are fine for that last part — it's only the OAC resource and the origin wiring that need L1.
Suggested shape
- An
OriginAccessControl L2 (likely src/aws/edge/), covering at minimum originAccessControlOriginType: "s3", signingBehavior: "always", signingProtocol: "sigv4".
- Teach
S3BucketOrigin to accept either an OAI or an OAC, emitting origin_access_control_id on the distribution origin rather than the s3_origin_config.origin_access_identity block. The current unconditional throw becomes "must have one of OAI or OAC".
- Optionally a
S3BucketOrigin.withOriginAccessControl(bucket) static that creates the OAC and attaches the AWS:SourceArn-scoped bucket policy, mirroring aws-cdk's S3BucketOrigin.withOriginAccessControl.
Two related gaps noticed while looking, mentioned here only so they aren't lost — happy to split into separate issues if preferred:
- The
Bucket L2 wires public_access_block / ownership_controls only on the public: true path; a private bucket gets neither, so an explicitly-hardened origin bucket needs L1 s3BucketPublicAccessBlock / s3BucketOwnershipControls alongside it.
Problem
There is no L2 construct for CloudFront Origin Access Control (OAC) anywhere in
src/. The only origin-access primitive is the legacy Origin Access Identity (OAI) atsrc/aws/storage/origin-access-identity.ts.This is not merely a missing convenience — the private-S3-origin path is hard-blocked on OAI.
S3BucketOriginthrows in its constructor when the bucket has no OAI (src/aws/edge/origin.ts:311-317):and
renderS3OriginConfig()can only emitoriginAccessIdentity(origin.ts:320-326). So there is no way to express "private bucket + CloudFront + OAC" with L2s, even by opting out.Verified at
c2bd9c7(currentmain):Why it matters
OAI is AWS-legacy; OAC is the documented, recommended mechanism and is required, not optional, for several common cases:
PUT/POSTto the origin, and dynamic-request signing generally.New stacks that follow current AWS guidance therefore cannot be expressed in TerraConstructs today without dropping to L1.
Concrete downstream consumer
KiroCrew's artifact-publishing stack (
kirodotdev/KiroCrew,src/kiro_crew/deploy/) provisions private S3 + CloudFront + OAC with a bucket policy pinned viaAWS:SourceArn, in both of its code paths (skills/artifact-deploy/templates/base-stack.yaml, andengine.py:212-410). Porting that to TerraConstructs is currently blocked on this gap. It is also the same stack that motivated #99 / #50 (thanks for thefunctionAssociationsfix in #159 — that half is now unblocked onmain).Workaround in use
Drop to the L1
cloudfrontOriginAccessControlresource from@cdktn/provider-aws(present as of v24.8.0 / AWS provider 6.52+), wirerenderS3OriginConfigmanually via an escape hatch, and hand-write the bucket policyAWS:SourceArncondition against the distribution ARN. TheBucketPolicyL2 andiam.PolicyStatementare fine for that last part — it's only the OAC resource and the origin wiring that need L1.Suggested shape
OriginAccessControlL2 (likelysrc/aws/edge/), covering at minimumoriginAccessControlOriginType: "s3",signingBehavior: "always",signingProtocol: "sigv4".S3BucketOriginto accept either an OAI or an OAC, emittingorigin_access_control_idon the distribution origin rather than thes3_origin_config.origin_access_identityblock. The current unconditional throw becomes "must have one of OAI or OAC".S3BucketOrigin.withOriginAccessControl(bucket)static that creates the OAC and attaches theAWS:SourceArn-scoped bucket policy, mirroring aws-cdk'sS3BucketOrigin.withOriginAccessControl.Two related gaps noticed while looking, mentioned here only so they aren't lost — happy to split into separate issues if preferred:
BucketL2 wirespublic_access_block/ownership_controlsonly on thepublic: truepath; a private bucket gets neither, so an explicitly-hardened origin bucket needs L1s3BucketPublicAccessBlock/s3BucketOwnershipControlsalongside it.