> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fjall.io/llms.txt
> Use this file to discover all available pages before exploring further.

# S3 Bucket

> Create secure AWS S3 buckets with Fjall, with SSL enforcement, versioning, and optional website hosting built in.

## Overview

The `S3Bucket` construct provides secure object storage with SSL enforcement, auto-cleanup, and optional versioning. Configure a single class for all use cases: private storage, website hosting, and public read access.

## Resource Class

```typescript theme={null}
import { S3Bucket } from "@fjall/components-infrastructure/lib/resources/aws/storage/s3";
```

## Basic Usage

### Standard Private Bucket

```typescript theme={null}
const bucket = new S3Bucket(this, "MyBucket", {
  bucketName: "my-secure-bucket",
});
```

### Website Hosting Bucket

```typescript theme={null}
const website = new S3Bucket(this, "MyWebsite", {
  bucketName: "my-website-bucket",
  websiteHosting: {
    indexDocument: "index.html",
    errorDocument: "error.html",
  },
});
```

### Public Read Bucket

```typescript theme={null}
const publicBucket = new S3Bucket(this, "PublicAssets", {
  bucketName: "my-public-assets",
  publicReadAccess: true,
});
```

## Default Behaviour

| Feature             | Default Value          | Description                                         |
| ------------------- | ---------------------- | --------------------------------------------------- |
| `enforceSSL`        | `true`                 | HTTPS required for access                           |
| `autoDeleteObjects` | `true`                 | Delete objects when stack deleted                   |
| `removalPolicy`     | `DESTROY`              | Bucket deleted with stack                           |
| `versioned`         | Depends on backup tier | Auto-enabled for "resilient" and "enterprise" tiers |

## Configuration Options

### Core Properties

| Property           | Type                   | Description                                         | Default                   |
| ------------------ | ---------------------- | --------------------------------------------------- | ------------------------- |
| `bucketName`       | `string`               | Bucket name                                         | Auto-generated            |
| `publicReadAccess` | `boolean`              | Allow public read access                            | `false`                   |
| `websiteHosting`   | `WebsiteHostingConfig` | Website hosting config                              | -                         |
| `backupVaultTier`  | `BackupTier`           | Backup tier ("standard", "resilient", "enterprise") | -                         |
| `removalPolicy`    | `RemovalPolicy`        | What happens on stack deletion                      | `DESTROY`                 |
| `versioned`        | `boolean`              | Enable object versioning                            | Auto based on backup tier |

All standard CDK `BucketProps` are also supported.

### WebsiteHostingConfig

| Property        | Type     | Description             | Default        |
| --------------- | -------- | ----------------------- | -------------- |
| `indexDocument` | `string` | Index document filename | Required       |
| `errorDocument` | `string` | Error document filename | `"error.html"` |

## Use Cases

### Private Application Storage

The default configuration creates a private bucket with SSL enforcement.

```typescript theme={null}
const bucket = new S3Bucket(this, "AppBucket", {
  bucketName: "my-app-data",
  removalPolicy: RemovalPolicy.RETAIN,
  lifecycleRules: [
    {
      id: "delete-old-versions",
      noncurrentVersionExpiration: Duration.days(90),
    },
  ],
});
```

### Static Website Hosting

Set `websiteHosting` to enable S3 website hosting. This automatically enables public read access and disables block public access settings.

```typescript theme={null}
const website = new S3Bucket(this, "CorporateWebsite", {
  bucketName: "www.example.com",
  websiteHosting: {
    indexDocument: "index.html",
    errorDocument: "404.html",
  },
});
```

### Public Asset Delivery

Set `publicReadAccess: true` for publicly readable buckets. This disables block public access settings.

```typescript theme={null}
const assets = new S3Bucket(this, "PublicAssets", {
  bucketName: "assets.example.com",
  publicReadAccess: true,
  cors: [
    {
      allowedMethods: [HttpMethods.GET, HttpMethods.HEAD],
      allowedOrigins: ["*"],
      allowedHeaders: ["*"],
      maxAge: 3000,
    },
  ],
});
```

<Warning>
  **Public buckets expose all contents to the internet.** Never store sensitive data in a public bucket. Consider CloudFront for access control.
</Warning>

### Versioned Bucket with Backup Tier

When `backupVaultTier` is set to "resilient" or "enterprise", versioning is automatically enabled with a 30-day noncurrent version expiration lifecycle rule.

```typescript theme={null}
const bucket = new S3Bucket(this, "ResilientBucket", {
  backupVaultTier: "resilient",
});
```

## Advanced Configuration

### Encryption

```typescript theme={null}
const key = new Key(this, "BucketKey");

const bucket = new S3Bucket(this, "EncryptedBucket", {
  encryption: BucketEncryption.KMS,
  encryptionKey: key,
  bucketKeyEnabled: true,
});
```

### Lifecycle Rules

```typescript theme={null}
const bucket = new S3Bucket(this, "ArchiveBucket", {
  lifecycleRules: [
    {
      id: "archive-old-objects",
      transitions: [
        {
          storageClass: StorageClass.INFREQUENT_ACCESS,
          transitionAfter: Duration.days(30),
        },
        {
          storageClass: StorageClass.GLACIER,
          transitionAfter: Duration.days(90),
        },
      ],
    },
    {
      id: "delete-old-versions",
      noncurrentVersionExpiration: Duration.days(180),
    },
  ],
});
```

### Event Notifications

```typescript theme={null}
const bucket = new S3Bucket(this, "ProcessingBucket");

bucket.addEventNotification(
  EventType.OBJECT_CREATED,
  new LambdaDestination(processorFunction),
  { prefix: "uploads/", suffix: ".csv" },
);

bucket.addEventNotification(
  EventType.OBJECT_REMOVED,
  new SqsDestination(deletionQueue),
);
```

## Access Control

### Bucket Policies

```typescript theme={null}
const bucket = new S3Bucket(this, "PolicyBucket");

bucket.addToResourcePolicy(
  new PolicyStatement({
    actions: ["s3:GetObject"],
    resources: [bucket.arnForObjects("public/*")],
    principals: [new AnyPrincipal()],
    conditions: {
      IpAddress: {
        "aws:SourceIp": ["203.0.113.0/24"],
      },
    },
  }),
);
```

### IAM Permissions

```typescript theme={null}
const bucket = new S3Bucket(this, "AppBucket");

// Grant read access
bucket.grantRead(role);

// Grant write access
bucket.grantWrite(role, "uploads/*");

// Grant full access
bucket.grantReadWrite(role);
```

### CORS Configuration

```typescript theme={null}
const bucket = new S3Bucket(this, "APIBucket", {
  cors: [
    {
      allowedHeaders: ["Authorization", "Content-Type"],
      allowedMethods: [HttpMethods.GET, HttpMethods.PUT, HttpMethods.POST],
      allowedOrigins: ["https://app.example.com"],
      exposedHeaders: ["ETag"],
      maxAge: 3000,
    },
  ],
});
```

## Monitoring

### Access Logging

```typescript theme={null}
const logBucket = new S3Bucket(this, "LogBucket");

const bucket = new S3Bucket(this, "AppBucket", {
  serverAccessLogsBucket: logBucket,
  serverAccessLogsPrefix: "app-bucket-logs/",
});
```

## Complete Example

```typescript theme={null}
import { S3Bucket } from "@fjall/components-infrastructure/lib/resources/aws/storage/s3";
import { Duration, RemovalPolicy } from "aws-cdk-lib";

const appBucket = new S3Bucket(this, "ApplicationStorage", {
  bucketName: `${props.appName}-storage-${props.environment}`,
  removalPolicy: RemovalPolicy.RETAIN,

  // Encryption
  encryption: BucketEncryption.S3_MANAGED,

  // Lifecycle
  lifecycleRules: [
    {
      id: "cleanup-incomplete-uploads",
      abortIncompleteMultipartUploadAfter: Duration.days(7),
    },
    {
      id: "archive-old-data",
      transitions: [
        {
          storageClass: StorageClass.INFREQUENT_ACCESS,
          transitionAfter: Duration.days(60),
        },
      ],
    },
  ],

  // CORS for web app
  cors: [
    {
      allowedHeaders: ["*"],
      allowedMethods: [HttpMethods.GET, HttpMethods.PUT],
      allowedOrigins: [props.appDomain],
      maxAge: 3600,
    },
  ],
});

// Grant app permissions
appRole.addToPolicy(
  new PolicyStatement({
    actions: ["s3:GetObject", "s3:PutObject"],
    resources: [appBucket.arnForObjects("user-uploads/*")],
  }),
);
```

## Best Practices

1. **Enable versioning** for data protection when needed
2. **Use encryption** for sensitive data
3. **Configure lifecycle rules** to reduce costs
4. **Enable access logging** for security audits
5. **Use least privilege** IAM policies
6. **Use `websiteHosting`** instead of manual website configuration
7. **Use `publicReadAccess`** instead of manual policy changes

## Next Steps

<CardGroup cols={2}>
  <Card title="ECR Repository" icon="box" href="/resources/storage/ecr-repository">
    Store container images alongside object storage
  </Card>

  <Card title="Lambda Function" icon="bolt" href="/resources/compute/lambda-function">
    Process objects on upload with event notifications
  </Card>

  <Card title="IAM Role" icon="user-shield" href="/resources/security/iam-role">
    Grant least-privilege access to bucket objects
  </Card>

  <Card title="Storage Factory" icon="box-archive" href="/patterns/storage-factory">
    Create buckets with the StorageFactory pattern
  </Card>
</CardGroup>
