Skip to content

Commit 400f2a0

Browse files
feat(core): Save outputs to SSM Parameter Store (#404)
Co-authored-by: nachundu <nachundu@amazon.com>
1 parent 03d509d commit 400f2a0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+3067
-23
lines changed

reference-artifacts/config.example.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"workloadaccounts-prefix": "config",
1212
"workloadaccounts-param-filename": "config.json",
1313
"ignored-ous": [],
14+
"additional-global-output-regions": [],
1415
"supported-regions": [
1516
"ap-northeast-1",
1617
"ap-northeast-2",

src/core/cdk/src/initial-setup.ts

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { RunAcrossAccountsTask } from './tasks/run-across-accounts-task';
2121
import * as fs from 'fs';
2222
import * as sns from '@aws-cdk/aws-sns';
2323
import { StoreOutputsTask } from './tasks/store-outputs-task';
24+
import { StoreOutputsToSSMTask } from './tasks/store-outputs-to-ssm-task';
2425

2526
export namespace InitialSetup {
2627
export interface CommonProps {
@@ -90,6 +91,18 @@ export namespace InitialSetup {
9091
encryption: dynamodb.TableEncryption.DEFAULT,
9192
});
9293

94+
const outputUtilsTable = new dynamodb.Table(this, 'OutputUtils', {
95+
tableName: createName({
96+
name: 'Output-Utils',
97+
suffixLength: 0,
98+
}),
99+
partitionKey: {
100+
name: 'id',
101+
type: dynamodb.AttributeType.STRING,
102+
},
103+
encryption: dynamodb.TableEncryption.DEFAULT,
104+
});
105+
93106
// This is the maximum time before a build times out
94107
// The role used by the build should allow this session duration
95108
const buildTimeout = cdk.Duration.hours(4);
@@ -499,7 +512,36 @@ export namespace InitialSetup {
499512
resultPath: 'DISCARD',
500513
});
501514

502-
const pass = new sfn.Pass(this, 'Success');
515+
const storeOutputsToSsmStateMachine = new sfn.StateMachine(
516+
this,
517+
`${props.acceleratorPrefix}StoreOutputsToSsm_sm`,
518+
{
519+
stateMachineName: `${props.acceleratorPrefix}StoreOutputsToSsm_sm`,
520+
definition: new StoreOutputsToSSMTask(this, 'StoreOutputsToSSM', {
521+
lambdaCode,
522+
role: pipelineRole,
523+
}),
524+
},
525+
);
526+
527+
const storeAllOutputsToSsmTask = new sfn.Task(this, 'Store Outputs to SSM', {
528+
// tslint:disable-next-line: deprecation
529+
task: new tasks.StartExecution(storeOutputsToSsmStateMachine, {
530+
integrationPattern: sfn.ServiceIntegrationPattern.SYNC,
531+
input: {
532+
'accounts.$': '$.accounts',
533+
'regions.$': '$.regions',
534+
acceleratorPrefix: props.acceleratorPrefix,
535+
assumeRoleName: props.stateMachineExecutionRole,
536+
outputsTableName: outputsTable.tableName,
537+
configRepositoryName: props.configRepositoryName,
538+
'configFilePath.$': '$.configFilePath',
539+
'configCommitId.$': '$.configCommitId',
540+
outputUtilsTableName: outputUtilsTable.tableName,
541+
},
542+
}),
543+
resultPath: 'DISCARD',
544+
});
503545

504546
const detachQuarantineScpTask = new CodeTask(this, 'Detach Quarantine SCP', {
505547
functionProps: {
@@ -513,7 +555,7 @@ export namespace InitialSetup {
513555
},
514556
resultPath: 'DISCARD',
515557
});
516-
detachQuarantineScpTask.next(pass);
558+
detachQuarantineScpTask.next(storeAllOutputsToSsmTask);
517559

518560
const enableTrustedAccessForServicesTask = new CodeTask(this, 'Enable Trusted Access For Services', {
519561
functionProps: {
@@ -790,7 +832,7 @@ export namespace InitialSetup {
790832

791833
const baseLineCleanupChoice = new sfn.Choice(this, 'Baseline Clean Up?')
792834
.when(sfn.Condition.stringEquals('$.baseline', 'ORGANIZATIONS'), detachQuarantineScpTask)
793-
.otherwise(pass);
835+
.otherwise(storeAllOutputsToSsmTask);
794836

795837
const commonStep1 = addScpTask.startState
796838
.next(deployPhase1Task)
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import * as cdk from '@aws-cdk/core';
2+
import * as iam from '@aws-cdk/aws-iam';
3+
import * as lambda from '@aws-cdk/aws-lambda';
4+
import * as sfn from '@aws-cdk/aws-stepfunctions';
5+
import { CodeTask } from '@aws-accelerator/cdk-accelerator/src/stepfunction-tasks';
6+
7+
export namespace StoreOutputsToSSMTask {
8+
export interface Props {
9+
role: iam.IRole;
10+
lambdaCode: lambda.Code;
11+
functionPayload?: { [key: string]: unknown };
12+
waitSeconds?: number;
13+
}
14+
}
15+
16+
export class StoreOutputsToSSMTask extends sfn.StateMachineFragment {
17+
readonly startState: sfn.State;
18+
readonly endStates: sfn.INextable[];
19+
20+
constructor(scope: cdk.Construct, id: string, props: StoreOutputsToSSMTask.Props) {
21+
super(scope, id);
22+
23+
const { role, lambdaCode, functionPayload, waitSeconds = 10 } = props;
24+
25+
role.addToPrincipalPolicy(
26+
new iam.PolicyStatement({
27+
effect: iam.Effect.ALLOW,
28+
resources: ['*'],
29+
actions: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents'],
30+
}),
31+
);
32+
33+
const storeAccountOutputs = new sfn.Map(this, `Store Account Outputs To SSM`, {
34+
itemsPath: `$.accounts`,
35+
resultPath: 'DISCARD',
36+
maxConcurrency: 10,
37+
parameters: {
38+
'accountId.$': '$$.Map.Item.Value',
39+
'regions.$': '$.regions',
40+
'acceleratorPrefix.$': '$.acceleratorPrefix',
41+
'assumeRoleName.$': '$.assumeRoleName',
42+
'outputsTableName.$': '$.outputsTableName',
43+
'configRepositoryName.$': '$.configRepositoryName',
44+
'configFilePath.$': '$.configFilePath',
45+
'configCommitId.$': '$.configCommitId',
46+
'outputUtilsTableName.$': '$.outputUtilsTableName',
47+
},
48+
});
49+
50+
const getAccountInfoTask = new CodeTask(scope, `Get Account Details`, {
51+
comment: 'Get Account Info',
52+
resultPath: '$.account',
53+
functionPayload,
54+
functionProps: {
55+
role,
56+
code: lambdaCode,
57+
handler: 'index.getAccountInfo',
58+
},
59+
});
60+
61+
const storeAccountRegionOutputs = new sfn.Map(this, `Store Account Region Outputs To SSM`, {
62+
itemsPath: `$.regions`,
63+
resultPath: 'DISCARD',
64+
maxConcurrency: 10,
65+
parameters: {
66+
'account.$': '$.account',
67+
'region.$': '$$.Map.Item.Value',
68+
'acceleratorPrefix.$': '$.acceleratorPrefix',
69+
'assumeRoleName.$': '$.assumeRoleName',
70+
'outputsTableName.$': '$.outputsTableName',
71+
'configRepositoryName.$': '$.configRepositoryName',
72+
'configFilePath.$': '$.configFilePath',
73+
'configCommitId.$': '$.configCommitId',
74+
'outputUtilsTableName.$': '$.outputUtilsTableName',
75+
},
76+
});
77+
78+
getAccountInfoTask.next(storeAccountRegionOutputs);
79+
const storeOutputsTask = new CodeTask(scope, `Store Outputs To SSM`, {
80+
resultPath: '$.storeOutputsOutput',
81+
functionPayload,
82+
functionProps: {
83+
role,
84+
code: lambdaCode,
85+
handler: 'index.saveOutputsToSSM',
86+
},
87+
});
88+
89+
const pass = new sfn.Pass(this, 'Store Outputs To SSM Success');
90+
storeAccountOutputs.iterator(getAccountInfoTask);
91+
storeAccountRegionOutputs.iterator(storeOutputsTask);
92+
const chain = sfn.Chain.start(storeAccountOutputs).next(pass);
93+
94+
this.startState = chain.startState;
95+
this.endStates = chain.endStates;
96+
}
97+
}

src/core/runtime/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export { handler as verifyFilesStep } from './verify-files-step';
2323
export { handler as notifySMFailure } from './notify-statemachine-failure';
2424
export { handler as notifySMSuccess } from './notify-statemachine-success';
2525
export { handler as getAccountInfo } from './get-account-info';
26+
export { handler as saveOutputsToSSM } from './save-outputs-to-ssm';
2627

2728
// TODO Replace with
2829
// export * as codebuild from './codebuild';

0 commit comments

Comments
 (0)