Skip to content

Commit 6ec8f5c

Browse files
authored
Merge pull request #194 from aws-solutions/release/v2.1.1
Updated to version v2.1.1
2 parents 362b0ac + 3e1abf7 commit 6ec8f5c

14 files changed

+234
-88
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2.1.1] - 2024-04-10
9+
10+
### Changed
11+
12+
- Changed order of CloudFormation parameters to emphasize the Security Control playbook
13+
- Changed default for all playbooks other than SC to 'no'
14+
- Updated descriptions of playbook parameters
15+
- Updated architecture diagram
16+
817
## [2.1.0] - 2024-03-28
918

1019
### Added

docs/architecture_diagram.png

-2.13 KB
Loading

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "automated_security_response_on_aws"
3-
version = "2.1.0"
3+
version = "2.1.1"
44

55
[tool.setuptools]
66
package-dir = {"" = "source"}

solution-manifest.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
id: SO0111
22
name: security-hub-automated-response-and-remediation
3-
version: 2.1.0
3+
version: 2.1.1
44
cloudformation_templates:
55
- template: aws-sharr-deploy.template
66
main_template: true

source/lib/__snapshots__/member-stack.test.ts.snap

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -103,15 +103,31 @@ exports[`member stack snapshot matches 1`] = `
103103
},
104104
{
105105
"Label": {
106-
"default": "Playbooks",
106+
"default": "Consolidated control finding Playbook",
107+
},
108+
"Parameters": [
109+
"LoadSCMemberStack",
110+
],
111+
},
112+
{
113+
"Label": {
114+
"default": "Security Standard Playbooks",
107115
},
108116
"Parameters": [
109117
"LoadAFSBPMemberStack",
110118
"LoadCIS120MemberStack",
111119
"LoadCIS140MemberStack",
112120
"LoadNIST80053MemberStack",
113121
"LoadPCI321MemberStack",
114-
"LoadSCMemberStack",
122+
],
123+
},
124+
{
125+
"Label": {
126+
"default": "Configuration",
127+
},
128+
"Parameters": [
129+
"CreateS3BucketForRedshiftAuditLogging",
130+
"SecHubAdminAccount",
115131
],
116132
},
117133
],
@@ -137,44 +153,44 @@ exports[`member stack snapshot matches 1`] = `
137153
"yes",
138154
"no",
139155
],
140-
"Default": "yes",
141-
"Description": "Load Playbook member stack for AFSBP?",
156+
"Default": "no",
157+
"Description": "Install the member components for automated remediation of AFSBP controls?",
142158
"Type": "String",
143159
},
144160
"LoadCIS120MemberStack": {
145161
"AllowedValues": [
146162
"yes",
147163
"no",
148164
],
149-
"Default": "yes",
150-
"Description": "Load Playbook member stack for CIS120?",
165+
"Default": "no",
166+
"Description": "Install the member components for automated remediation of CIS120 controls?",
151167
"Type": "String",
152168
},
153169
"LoadCIS140MemberStack": {
154170
"AllowedValues": [
155171
"yes",
156172
"no",
157173
],
158-
"Default": "yes",
159-
"Description": "Load Playbook member stack for CIS140?",
174+
"Default": "no",
175+
"Description": "Install the member components for automated remediation of CIS140 controls?",
160176
"Type": "String",
161177
},
162178
"LoadNIST80053MemberStack": {
163179
"AllowedValues": [
164180
"yes",
165181
"no",
166182
],
167-
"Default": "yes",
168-
"Description": "Load Playbook member stack for NIST80053?",
183+
"Default": "no",
184+
"Description": "Install the member components for automated remediation of NIST80053 controls?",
169185
"Type": "String",
170186
},
171187
"LoadPCI321MemberStack": {
172188
"AllowedValues": [
173189
"yes",
174190
"no",
175191
],
176-
"Default": "yes",
177-
"Description": "Load Playbook member stack for PCI321?",
192+
"Default": "no",
193+
"Description": "Install the member components for automated remediation of PCI321 controls?",
178194
"Type": "String",
179195
},
180196
"LoadSCMemberStack": {
@@ -183,7 +199,7 @@ exports[`member stack snapshot matches 1`] = `
183199
"no",
184200
],
185201
"Default": "yes",
186-
"Description": "Load Playbook member stack for SC?",
202+
"Description": "If the consolidated control findings feature is turned on in Security Hub, only enable the Security Control (SC) playbook. If the feature is not turned on, enable the playbooks for the security standards that are enabled in Security Hub. Enabling additional playbooks can result in reaching the quota for EventBridge Rules.",
187203
"Type": "String",
188204
},
189205
"LogGroupName": {

source/lib/admin-account-param.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { CfnParameter } from 'aws-cdk-lib';
44
import { Construct } from 'constructs';
55

66
export default class AdminAccountParam extends Construct {
7+
public readonly paramId: string;
78
public readonly value: string;
89

910
constructor(scope: Construct, id: string) {
@@ -16,6 +17,7 @@ export default class AdminAccountParam extends Construct {
1617
allowedPattern: accountIdRegex.source,
1718
});
1819
param.overrideLogicalId(`SecHubAdminAccount`);
20+
this.paramId = param.logicalId;
1921

2022
this.value = param.valueAsString;
2123
}

source/lib/admin-playbook.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { CfnCondition, CfnParameter, CfnResource, Fn, NestedStack, Stack } from 'aws-cdk-lib';
5+
import { Construct } from 'constructs';
6+
7+
export interface AdminPlaybookProps {
8+
name: string;
9+
stackDependencies?: CfnResource[];
10+
defaultState?: 'yes' | 'no';
11+
description?: string;
12+
}
13+
14+
export class AdminPlaybook {
15+
parameterName = '';
16+
playbookStack: Stack;
17+
18+
constructor(scope: Construct, props: AdminPlaybookProps) {
19+
const templateFile = `${props.name}Stack.template`;
20+
const illegalChars = /[\\._]/g;
21+
const playbookName = props.name.replace(illegalChars, '');
22+
this.parameterName = `Load${playbookName}AdminStack`;
23+
24+
//---------------------------------------------------------------------
25+
// Playbook Template Nested Stack
26+
//
27+
const stackOption = new CfnParameter(scope, `LoadAdminStack${playbookName}`, {
28+
type: 'String',
29+
description:
30+
props.description ?? `Install the admin components for automated remediation of ${props.name} controls?`,
31+
default: props.defaultState ?? 'no',
32+
allowedValues: ['yes', 'no'],
33+
});
34+
stackOption.overrideLogicalId(this.parameterName);
35+
36+
this.playbookStack = new NestedStack(scope, `PlaybookAdminStack${playbookName}`);
37+
const cfnStack = this.playbookStack.nestedStackResource as CfnResource;
38+
cfnStack.addPropertyOverride(
39+
'TemplateURL',
40+
'https://' +
41+
Fn.findInMap('SourceCode', 'General', 'S3Bucket') +
42+
'-reference.s3.amazonaws.com/' +
43+
Fn.findInMap('SourceCode', 'General', 'KeyPrefix') +
44+
'/playbooks/' +
45+
templateFile,
46+
);
47+
cfnStack.cfnOptions.condition = new CfnCondition(scope, `load${playbookName}Cond`, {
48+
expression: Fn.conditionEquals(stackOption, 'yes'),
49+
});
50+
props.stackDependencies?.forEach((dependency) => {
51+
cfnStack.node.addDependency(dependency);
52+
});
53+
cfnStack.overrideLogicalId(`PlaybookAdminStack${props.name}`);
54+
}
55+
}

source/lib/member-playbook.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { CfnCondition, CfnParameter, CfnResource, Fn, Stack } from 'aws-cdk-lib';
5+
import { Construct } from 'constructs';
6+
import { SerializedNestedStackFactory } from './cdk-helper/nested-stack';
7+
8+
export interface MemberPlaybookProps {
9+
readonly name: string;
10+
readonly nestedStackFactory: SerializedNestedStackFactory;
11+
readonly parameters?: { [_: string]: string };
12+
readonly stackDependencies?: CfnResource[];
13+
readonly defaultState?: 'yes' | 'no';
14+
readonly description?: string;
15+
}
16+
17+
export class MemberPlaybook {
18+
parameterName = '';
19+
playbookStack: Stack;
20+
21+
constructor(scope: Construct, props: MemberPlaybookProps) {
22+
const templateFile = `${props.name}MemberStack.template`;
23+
const illegalChars = /[\\._]/g;
24+
const playbookName = props.name.replace(illegalChars, '');
25+
this.parameterName = `Load${playbookName}MemberStack`;
26+
27+
//---------------------------------------------------------------------
28+
// Playbook Template Nested Stack
29+
//
30+
const stackOption = new CfnParameter(scope, `LoadMemberStack${playbookName}`, {
31+
type: 'String',
32+
description:
33+
props.description ?? `Install the member components for automated remediation of ${props.name} controls?`,
34+
default: props.defaultState ?? 'no',
35+
allowedValues: ['yes', 'no'],
36+
});
37+
stackOption.overrideLogicalId(this.parameterName);
38+
39+
this.playbookStack = props.nestedStackFactory.addNestedStack(`PlaybookMemberStack${playbookName}`, {
40+
templateRelativePath: `playbooks/${templateFile}`,
41+
parameters: props.parameters,
42+
condition: new CfnCondition(scope, `load${playbookName}Cond`, {
43+
expression: Fn.conditionEquals(stackOption, 'yes'),
44+
}),
45+
});
46+
47+
const cfnStack = this.playbookStack.nestedStackResource as CfnResource;
48+
cfnStack.overrideLogicalId(`PlaybookMemberStack${props.name}`);
49+
}
50+
}

source/lib/member-stack.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,6 @@ describe('member stack', function () {
348348

349349
const expectedTemplateParameterProperties = {
350350
AllowedValues: ['yes', 'no'],
351-
Default: 'yes',
352351
Type: 'String',
353352
};
354353

source/lib/member-stack.ts

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33
import { readdirSync } from 'fs';
4-
import { StackProps, Stack, App, CfnParameter, CfnCondition, Fn, CfnResource } from 'aws-cdk-lib';
4+
import { StackProps, Stack, App, CfnResource } from 'aws-cdk-lib';
55
import { Runtime } from 'aws-cdk-lib/aws-lambda';
66
import AdminAccountParam from './admin-account-param';
77
import { RedshiftAuditLogging } from './member/redshift-audit-logging';
@@ -11,6 +11,7 @@ import { MemberBucketEncryption } from './member/bucket-encryption';
1111
import { MemberVersion } from './member/version';
1212
import { SerializedNestedStackFactory } from './cdk-helper/nested-stack';
1313
import { WaitProvider } from './wait-provider';
14+
import { MemberPlaybook } from './member-playbook';
1415

1516
export interface SolutionProps extends StackProps {
1617
solutionId: string;
@@ -27,7 +28,7 @@ export class MemberStack extends Stack {
2728

2829
const adminAccountParam = new AdminAccountParam(this, 'AdminAccountParameter');
2930

30-
new RedshiftAuditLogging(this, 'RedshiftAuditLogging', { solutionId: props.solutionId });
31+
const redShiftLogging = new RedshiftAuditLogging(this, 'RedshiftAuditLogging', { solutionId: props.solutionId });
3132

3233
new MemberRemediationKey(this, 'MemberKey', { solutionId: props.solutionId });
3334

@@ -60,40 +61,40 @@ export class MemberStack extends Stack {
6061
this.nestedStacks.push(nestedStackNoRoles as Stack);
6162

6263
const playbookDirectory = `${__dirname}/../playbooks`;
63-
const ignore = ['.DS_Store', 'common', '.pytest_cache', 'NEWPLAYBOOK', '.coverage'];
64-
const illegalChars = /[\\._]/g;
64+
const ignore = ['.DS_Store', 'common', '.pytest_cache', 'NEWPLAYBOOK', '.coverage', 'SC'];
6565
const listOfPlaybooks: string[] = [];
6666
const items = readdirSync(playbookDirectory);
6767
items.forEach((file) => {
6868
if (!ignore.includes(file)) {
69-
const templateFile = `${file}MemberStack.template`;
70-
71-
const parmname = file.replace(illegalChars, '');
72-
const memberStackOption = new CfnParameter(this, `LoadMemberStack${parmname}`, {
73-
type: 'String',
74-
description: `Load Playbook member stack for ${file}?`,
75-
default: 'yes',
76-
allowedValues: ['yes', 'no'],
77-
});
78-
memberStackOption.overrideLogicalId(`Load${parmname}MemberStack`);
79-
listOfPlaybooks.push(memberStackOption.logicalId);
80-
81-
const nestedStack = nestedStackFactory.addNestedStack(`PlaybookMemberStack${file}`, {
82-
templateRelativePath: `playbooks/${templateFile}`,
69+
const playbook = new MemberPlaybook(this, {
70+
name: file,
71+
defaultState: 'no',
72+
nestedStackFactory,
8373
parameters: {
8474
SecHubAdminAccount: adminAccountParam.value,
8575
WaitProviderServiceToken: waitProvider.serviceToken,
8676
},
87-
condition: new CfnCondition(this, `load${file}Cond`, {
88-
expression: Fn.conditionEquals(memberStackOption, 'yes'),
89-
}),
9077
});
91-
const cfnResource = nestedStack.nestedStackResource as CfnResource;
92-
cfnResource.overrideLogicalId(`PlaybookMemberStack${file}`);
93-
this.nestedStacks.push(nestedStack as Stack);
78+
79+
listOfPlaybooks.push(playbook.parameterName);
80+
this.nestedStacks.push(playbook.playbookStack);
9481
}
9582
});
9683

84+
const scPlaybook = new MemberPlaybook(this, {
85+
name: 'SC',
86+
defaultState: 'yes',
87+
description:
88+
'If the consolidated control findings feature is turned on in Security Hub, only enable the Security Control (SC) playbook. If the feature is not turned on, enable the playbooks for the security standards that are enabled in Security Hub. Enabling additional playbooks can result in reaching the quota for EventBridge Rules.',
89+
nestedStackFactory,
90+
parameters: {
91+
SecHubAdminAccount: adminAccountParam.value,
92+
WaitProviderServiceToken: waitProvider.serviceToken,
93+
},
94+
});
95+
96+
this.nestedStacks.push(scPlaybook.playbookStack);
97+
9798
/********************
9899
** Metadata
99100
********************/
@@ -105,9 +106,17 @@ export class MemberStack extends Stack {
105106
Parameters: [memberLogGroup.paramId],
106107
},
107108
{
108-
Label: { default: 'Playbooks' },
109+
Label: { default: 'Consolidated control finding Playbook' },
110+
Parameters: [scPlaybook.parameterName],
111+
},
112+
{
113+
Label: { default: 'Security Standard Playbooks' },
109114
Parameters: listOfPlaybooks,
110115
},
116+
{
117+
Label: { default: 'Configuration' },
118+
Parameters: [redShiftLogging.paramId, adminAccountParam.paramId],
119+
},
111120
],
112121
ParameterLabels: {
113122
[memberLogGroup.paramId]: {

source/lib/member/redshift-audit-logging.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export interface RedshiftAuditLoggingProps {
1515
}
1616

1717
export class RedshiftAuditLogging extends Construct {
18+
public readonly paramId: string;
19+
1820
constructor(scope: Construct, id: string, props: RedshiftAuditLoggingProps) {
1921
super(scope, id);
2022

@@ -25,6 +27,7 @@ export class RedshiftAuditLogging extends Construct {
2527
allowedValues: [ChoiceParam.Yes, ChoiceParam.No],
2628
description: 'Create S3 Bucket For Redshift Cluster Audit Logging.',
2729
});
30+
this.paramId = templateParam.logicalId;
2831

2932
const condition = new CfnCondition(scope, 'EnableS3BucketForRedShift4', {
3033
expression: Fn.conditionEquals(templateParam.valueAsString, ChoiceParam.Yes),

0 commit comments

Comments
 (0)