Skip to main content

Overview

S3Bucket extends the CDK Bucket with Fjall’s secure defaults: SSL enforcement, Block Public Access asserted on every deploy, automatic multipart-upload cleanup, and versioning driven by the application’s backup tier. One class covers private storage, static website hosting, and public read access.

Resource Class

Basic Usage

Standard Private Bucket

Website Hosting Bucket

websiteHosting requires appName. The website exports are keyed on the pair (app, bucket), so two applications with identically named buckets stay deployable side by side.

Public Read Bucket

Default Behaviour

Removal policy

The default is environment-aware, not fixed:
  • production resolves to RETAIN. The bucket survives stack deletion.
  • staging, development, platform, compliance, root resolve to DESTROY. The bucket is tagged for the deploy-time pre-empty pass, which empties it SDK-side before CloudFormation deletes it.
  • An unrecognised ENVIRONMENT value fails at synth rather than defaulting to DESTROY. A bare cdk synth with no environment signal falls back to DESTROY, because a template you only inspect deploys nothing.
Pass removalPolicy explicitly to override the default in either direction.

Configuration Options

Core Properties

All standard CDK BucketProps are also supported. enforceSSL and autoDeleteObjects are the two exceptions: both are fixed by the construct.
StorageFactory.build() and CdnFactory fill appName and appBackupTier from the App automatically. Only direct new S3Bucket(...) or new Storage(...) construction needs to pass them.

WebsiteHostingConfig

ResourcePolicyStatement

Service and Federated principals are not modelled. The TLS-only enforceSSL deny is applied separately and must not be listed here.

Use Cases

Private Application Storage

The default configuration creates a private bucket with SSL enforcement and BLOCK_ALL public access.

Static Website Hosting

Set websiteHosting to serve the bucket as an S3 website. This turns on public read access and opens the bucket-policy halves of Block Public Access.
Two CloudFormation exports are emitted, WebsiteEndpoint and WebsiteHostedZoneId, both qualified by appName and the bucket name. Domain alias targets import that pair to point DNS at the website endpoint.

Public Asset Delivery

Set publicReadAccess: true for a publicly readable bucket.
Public buckets expose all contents to the internet. Never store sensitive data in a public bucket. Front the bucket with CloudFront when you need access control.
Public and website buckets open only the two policy halves of Block Public Access (blockPublicPolicy and restrictPublicBuckets). The ACL halves, blockPublicAcls and ignorePublicAcls, stay true, because access here is bucket-policy-based and ACLs are disabled on modern buckets.

Versioning Follows the Backup Tier

Versioning resolves from the bucket’s effective backup tier: the per-bucket backupVaultTier if set, otherwise the application’s backup.tier from App.getApp. Tiers resilient and enterprise turn versioning on. An explicit versioned wins over the tier in either direction. Buckets built through StorageFactory, the StaticSite site bucket, and the Buildkite artefact bucket inherit the application tier automatically.
When versioning turns on and you supply no lifecycleRules, the construct pairs it with a 30-day noncurrent-version expiry.
Supplying your own lifecycleRules replaces that auto-paired expiry. A versioned bucket with custom rules and no noncurrentVersionExpiration accumulates noncurrent versions forever. Carry noncurrentVersionExpiration: Duration.days(30) explicitly in your rule set.

Buckets unversioned by design

Some buckets churn by nature, and versioning them would keep a billed copy of every overwrite: a build-output bucket, an ISR cache, a database’s cold tier. A construct that leaves such a bucket unversioned says so with versioningExemption, and the bucket carries the fjall:s3:versioning-exempt tag valued with that reason. Tags outlive the stack, so a posture scan can tell a bucket left unversioned on purpose from one that was forgotten. The tag is stamped only while the bucket’s resolved versioned is false: turn versioning on and the marker goes with it. The Payload pattern and the ClickHouse cold tier set it for you.

Advanced Configuration

Encryption

KMS-encrypted buckets get bucketKeyEnabled: true automatically, which cuts KMS request cost. Pass bucketKeyEnabled: false to opt out.

Lifecycle Rules

The built-in AbortIncompleteMultipartUploads rule is added on top of these. Do not restate it.

Event Notifications

Access Control

Declarative Bucket Policy

resourcePolicyStatements keeps the policy in the props. The shape mirrors the generator’s S3ResourcePlanSchema.resourcePolicyStatements, so a bucket edited by the codemod engine and one written by hand look the same.

Imperative Bucket Policy

IAM Permissions

CORS Configuration

Monitoring

Access Logging

Complete Example

Best Practices

  1. Set appName on any bucket with websiteHosting, or synth fails.
  2. Set removalPolicy explicitly when a non-production bucket holds data you want to keep.
  3. Carry noncurrentVersionExpiration in every custom rule set on a versioned bucket.
  4. Use encryption for sensitive data, and leave bucketKeyEnabled at its default to keep KMS cost down.
  5. Configure lifecycle transitions to move cold objects to cheaper storage classes.
  6. Enable access logging for security audits.
  7. Use least-privilege IAM grants over broad bucket policies.
  8. Use websiteHosting and publicReadAccess rather than editing Block Public Access or bucket policies by hand.

Next Steps

ECR Repository

Store container images alongside object storage

Lambda Function

Process objects on upload with event notifications

IAM Role

Grant least-privilege access to bucket objects

Storage Factory

Create buckets with the StorageFactory pattern