Skip to content

Commit

Permalink
feat(app-staging-synthesizer-alpha): encryption type for staging buck…
Browse files Browse the repository at this point in the history
…et (#28903)

Closes #28815.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
msambol authored Jan 30, 2024
1 parent 8430ea5 commit 69f4b8d
Show file tree
Hide file tree
Showing 13 changed files with 1,223 additions and 6 deletions.
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
Loading

0 comments on commit 69f4b8d

Please sign in to comment.