Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(app-staging-synthesizer-alpha): encryption type for staging bucket #28903

Merged
merged 7 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 16 additions & 0 deletions packages/@aws-cdk/app-staging-synthesizer-alpha/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,22 @@ const app = new App({
});
```

### Staging Bucket Encryption

By default, the staging resources will be stored in an S3 Bucket with KMS encryption. To use
SSE-S3, set `stagingBucketEncryption` to `BucketEncryption.S3_MANAGED`.

```ts
import { BucketEncryption } from 'aws-cdk-lib/aws-s3';

const app = new App({
defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({
appId: 'my-app-id',
stagingBucketEncryption: BucketEncryption.S3_MANAGED,
}),
});
```

## Using a Custom Staging Stack per Environment

If you want to customize some behavior that is not configurable via properties,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ export interface DefaultStagingStackOptions {
*/
readonly stagingBucketName?: string;

/**
* Encryption type for staging bucket
*
* @default - s3.BucketEncryption.KMS
*/
readonly stagingBucketEncryption?: s3.BucketEncryption;

/**
* Pass in an existing role to be used as the file publishing role.
*
Expand Down Expand Up @@ -219,6 +226,7 @@ export class DefaultStagingStack extends Stack implements IStagingResources {

private readonly appId: string;
private readonly stagingBucketName?: string;
private stagingBucketEncryption?: s3.BucketEncryption;

/**
* File publish role ARN in asset manifest format
Expand Down Expand Up @@ -259,6 +267,7 @@ export class DefaultStagingStack extends Stack implements IStagingResources {

this.deployRoleArn = props.deployRoleArn;
this.stagingBucketName = props.stagingBucketName;
this.stagingBucketEncryption = props.stagingBucketEncryption;
const specializer = new StringSpecializer(this, props.qualifier);

this.providedFileRole = props.fileAssetPublishingRole?._specialize(specializer);
Expand Down Expand Up @@ -358,7 +367,15 @@ export class DefaultStagingStack extends Stack implements IStagingResources {
}

this.ensureFileRole();
const key = this.createBucketKey();

let key = undefined;
if (this.stagingBucketEncryption === s3.BucketEncryption.KMS || this.stagingBucketEncryption === undefined) {
if (this.stagingBucketEncryption === undefined) {
// default is KMS as an AWS best practice, and for backwards compatibility
this.stagingBucketEncryption = s3.BucketEncryption.KMS;
}
key = this.createBucketKey();
}

// Create the bucket once the dependencies have been created
const bucket = new s3.Bucket(this, bucketId, {
Expand All @@ -369,7 +386,7 @@ export class DefaultStagingStack extends Stack implements IStagingResources {
} : {
removalPolicy: RemovalPolicy.RETAIN,
}),
encryption: s3.BucketEncryption.KMS,
encryption: this.stagingBucketEncryption,
encryptionKey: key,

// Many AWS account safety checkers will complain when buckets aren't versioned
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as path from 'path';
import { App, Stack, CfnResource, FileAssetPackaging, Token, Lazy, Duration } from 'aws-cdk-lib';
import { Match, Template } from 'aws-cdk-lib/assertions';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import { BucketEncryption } from 'aws-cdk-lib/aws-s3';
import * as cxschema from 'aws-cdk-lib/cloud-assembly-schema';
import { CloudAssembly } from 'aws-cdk-lib/cx-api';
import { evaluateCFN } from './evaluate-cfn';
Expand Down Expand Up @@ -257,7 +258,7 @@ describe(AppStagingSynthesizer, () => {
stack = new Stack(app, 'Stack', {
env: {
account: '000000000000',
region: 'us-west-2',
region: 'us-east-1',
},
});
new CfnResource(stack, 'Resource', {
Expand All @@ -268,16 +269,60 @@ describe(AppStagingSynthesizer, () => {
const asm = app.synth();

// THEN
const stagingStackArtifact = asm.getStackArtifact(`StagingStack-${APP_ID}-000000000000-us-west-2`);

Template.fromJSON(stagingStackArtifact.template).hasResourceProperties('AWS::S3::Bucket', {
Template.fromJSON(getStagingResourceStack(asm).template).hasResourceProperties('AWS::S3::Bucket', {
LifecycleConfiguration: {
Rules: Match.arrayWith([{
ExpirationInDays: 1,
Prefix: DEPLOY_TIME_PREFIX,
Status: 'Enabled',
}]),
},
// When stagingBucketEncryption is not specified, it should be KMS for backwards compatibility
BucketEncryption: {
ServerSideEncryptionConfiguration: [
{
ServerSideEncryptionByDefault: {
SSEAlgorithm: 'aws:kms',
},
},
],
},
});
});

test('staging bucket with SSE-S3 encryption', () => {
// GIVEN
app = new App({
defaultStackSynthesizer: AppStagingSynthesizer.defaultResources({
appId: APP_ID,
deployTimeFileAssetLifetime: Duration.days(1),
stagingBucketEncryption: BucketEncryption.S3_MANAGED,
}),
});
stack = new Stack(app, 'Stack', {
env: {
account: '000000000000',
region: 'us-east-1',
},
});
new CfnResource(stack, 'Resource', {
type: 'Some::Resource',
});

// WHEN
const asm = app.synth();

// THEN
Template.fromJSON(getStagingResourceStack(asm).template).hasResourceProperties('AWS::S3::Bucket', {
BucketEncryption: {
ServerSideEncryptionConfiguration: [
{
ServerSideEncryptionByDefault: {
SSEAlgorithm: 'AES256',
},
},
],
},
});
});
});
Expand Down