Skip to content

Commit 8ec4279

Browse files
feature:(core): z120 move secrets to dynamo db (#372)
- Mergin this to start working on Moving SM input to DynamoDB
1 parent d3a7850 commit 8ec4279

File tree

11 files changed

+223
-103
lines changed

11 files changed

+223
-103
lines changed

src/core/cdk/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@
2323
"typescript": "3.8.3"
2424
},
2525
"dependencies": {
26+
"@aws-accelerator/accelerator-runtime": "workspace:^0.0.1",
27+
"@aws-accelerator/cdk-accelerator": "workspace:^0.0.1",
2628
"@aws-cdk/aws-cloudformation": "1.46.0",
2729
"@aws-cdk/aws-codebuild": "1.46.0",
2830
"@aws-cdk/aws-codepipeline": "1.46.0",
2931
"@aws-cdk/aws-codepipeline-actions": "1.46.0",
32+
"@aws-cdk/aws-dynamodb": "1.46.0",
3033
"@aws-cdk/aws-events": "1.46.0",
3134
"@aws-cdk/aws-events-targets": "1.46.0",
3235
"@aws-cdk/aws-iam": "1.46.0",
@@ -36,14 +39,11 @@
3639
"@aws-cdk/aws-s3": "1.46.0",
3740
"@aws-cdk/aws-s3-assets": "1.46.0",
3841
"@aws-cdk/aws-s3-deployment": "1.46.0",
42+
"@aws-cdk/aws-secretsmanager": "1.46.0",
3943
"@aws-cdk/aws-sns": "1.46.0",
4044
"@aws-cdk/aws-stepfunctions": "1.46.0",
4145
"@aws-cdk/aws-stepfunctions-tasks": "1.46.0",
42-
"@aws-cdk/aws-secretsmanager": "1.46.0",
43-
"@aws-cdk/aws-dynamodb": "1.46.0",
4446
"@aws-cdk/core": "1.46.0",
45-
"@aws-accelerator/accelerator-runtime": "workspace:^0.0.1",
46-
"@aws-accelerator/cdk-accelerator": "workspace:^0.0.1",
4747
"@types/cfn-response": "^1.0.3",
4848
"aws-sdk": "2.668.0",
4949
"cfn-response": "^1.0.1",

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

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -69,22 +69,14 @@ export namespace InitialSetup {
6969

7070
const stack = cdk.Stack.of(this);
7171

72-
const accountsSecret = new secrets.Secret(this, 'Accounts', {
73-
secretName: 'accelerator/accounts',
74-
description: 'This secret contains the information about the accounts that are used for deployment.',
75-
});
76-
setSecretValue(accountsSecret, '[]');
77-
78-
const limitsSecret = new secrets.Secret(this, 'Limits', {
79-
secretName: 'accelerator/limits',
80-
description: 'This secret contains a copy of the service limits of the Accelerator accounts.',
81-
});
82-
83-
const organizationsSecret = new secrets.Secret(this, 'Organizations', {
84-
secretName: 'accelerator/organizations',
85-
description: 'This secret contains the information about the organizations that are used for deployment.',
72+
const parametersTable = new dynamodb.Table(this, 'ParametersTable', {
73+
tableName: createName({
74+
name: 'Parameters',
75+
suffixLength: 0,
76+
}),
77+
partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
78+
encryption: dynamodb.TableEncryption.DEFAULT,
8679
});
87-
setSecretValue(organizationsSecret, '[]');
8880

8981
const outputsTable = new dynamodb.Table(this, 'Outputs', {
9082
tableName: createName({
@@ -135,9 +127,10 @@ export namespace InitialSetup {
135127
ACCELERATOR_EXECUTION_ROLE_NAME: props.stateMachineExecutionRole,
136128
CDK_PLUGIN_ASSUME_ROLE_NAME: props.stateMachineExecutionRole,
137129
CDK_PLUGIN_ASSUME_ROLE_DURATION: `${buildTimeout.toSeconds()}`,
138-
ACCOUNTS_SECRET_ID: accountsSecret.secretArn,
139-
LIMITS_SECRET_ID: limitsSecret.secretArn,
140-
ORGANIZATIONS_SECRET_ID: organizationsSecret.secretArn,
130+
ACCOUNTS_ITEM_ID: 'accounts',
131+
LIMITS_ITEM_ID: 'limits',
132+
ORGANIZATIONS_ITEM_ID: 'organizations',
133+
DYNAMODB_PARAMETERS_TABLE_NAME: parametersTable.tableName,
141134
},
142135
});
143136

@@ -314,7 +307,8 @@ export namespace InitialSetup {
314307
role: pipelineRole,
315308
},
316309
functionPayload: {
317-
organizationsSecretId: organizationsSecret.secretArn,
310+
parametersTableName: parametersTable.tableName,
311+
itemId: 'organizations',
318312
configRepositoryName: props.configRepositoryName,
319313
'configFilePath.$': '$.configuration.configFilePath',
320314
'configCommitId.$': '$.configuration.configCommitId',
@@ -329,7 +323,9 @@ export namespace InitialSetup {
329323
role: pipelineRole,
330324
},
331325
functionPayload: {
332-
accountsSecretId: accountsSecret.secretArn,
326+
parametersTableName: parametersTable.tableName,
327+
itemId: 'accounts',
328+
accountsItemsCountId: 'accounts-items-count',
333329
'configuration.$': '$.configuration',
334330
},
335331
resultPath: '$',
@@ -444,7 +440,8 @@ export namespace InitialSetup {
444440
'configRepositoryName.$': '$.configRepositoryName',
445441
'configFilePath.$': '$.configFilePath',
446442
'configCommitId.$': '$.configCommitId',
447-
limitsSecretId: limitsSecret.secretArn,
443+
parametersTableName: parametersTable.tableName,
444+
itemId: 'limits',
448445
assumeRoleName: props.stateMachineExecutionRole,
449446
'accounts.$': '$.accounts',
450447
},
@@ -462,8 +459,9 @@ export namespace InitialSetup {
462459
'configFilePath.$': '$.configuration.configFilePath',
463460
'configCommitId.$': '$.configuration.configCommitId',
464461
acceleratorPrefix: props.acceleratorPrefix,
465-
accountsSecretId: accountsSecret.secretArn,
466-
organizationsSecretId: organizationsSecret.secretArn,
462+
parametersTableName: parametersTable.tableName,
463+
organizationsItemId: 'organizations',
464+
accountsItemId: 'accounts',
467465
configBranch: props.configBranchName,
468466
'configRootFilePath.$': '$.configuration.configRootFilePath',
469467
},

src/core/runtime/src/load-accounts-step.ts

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import { Organizations } from '@aws-accelerator/common/src/aws/organizations';
2-
import { SecretsManager } from '@aws-accelerator/common/src/aws/secrets-manager';
32
import { Account } from '@aws-accelerator/common-outputs/src/accounts';
43
import { LoadConfigurationOutput, ConfigurationOrganizationalUnit } from './load-configuration-step';
54
import { equalIgnoreCase } from '@aws-accelerator/common/src/util/common';
5+
import { DynamoDB } from '@aws-accelerator/common/src/aws/dynamodb';
6+
import { getItemInput, getUpdateItemInput } from './utils/dynamodb-requests';
67

78
export interface LoadAccountsInput {
8-
accountsSecretId: string;
9+
accountsItemsCountId: string;
10+
parametersTableName: string;
11+
itemId: string;
912
configuration: LoadConfigurationOutput;
1013
}
1114

@@ -15,18 +18,26 @@ export interface LoadAccountsOutput {
1518
regions: string[];
1619
}
1720

21+
const dynamoDB = new DynamoDB();
22+
1823
export const handler = async (input: LoadAccountsInput): Promise<LoadAccountsOutput> => {
1924
console.log(`Loading accounts...`);
2025
console.log(JSON.stringify(input, null, 2));
2126

22-
const { accountsSecretId, configuration } = input;
27+
const { parametersTableName, configuration, itemId, accountsItemsCountId } = input;
2328

2429
// The first step is to load all the execution roles
2530
const organizations = new Organizations();
2631
const organizationAccounts = await organizations.listAccounts();
2732
const activeAccounts = organizationAccounts.filter(account => account.Status === 'ACTIVE');
2833

2934
const accounts = [];
35+
36+
const chunk = (totalAccounts: Account[], size: number) =>
37+
Array.from({ length: Math.ceil(totalAccounts.length / size) }, (v, i) =>
38+
totalAccounts.slice(i * size, i * size + size),
39+
);
40+
3041
for (const accountConfig of configuration.accounts) {
3142
let organizationAccount;
3243
organizationAccount = activeAccounts.find(a => {
@@ -68,12 +79,26 @@ export const handler = async (input: LoadAccountsInput): Promise<LoadAccountsOut
6879
});
6980
}
7081

71-
// Store the accounts configuration in the accounts secret
72-
const secrets = new SecretsManager();
73-
await secrets.putSecretValue({
74-
SecretId: accountsSecretId,
75-
SecretString: JSON.stringify(accounts),
76-
});
82+
const accountItemsCountItem = await dynamoDB.getItem(getItemInput(parametersTableName, accountsItemsCountId));
83+
const itemsCount = !accountItemsCountItem.Item ? 0 : Number(accountItemsCountItem.Item.value.S);
84+
85+
// Removing existing accounts from dynamodb table
86+
for (let index = 0; index < itemsCount; index++) {
87+
await dynamoDB.deleteItem(getItemInput(parametersTableName, `${itemId}/${index}`));
88+
}
89+
90+
// Splitting the accounts array to chunks of size 100
91+
const accountsChunk = chunk(accounts, 100);
92+
// Store the accounts configuration in the dynamodb
93+
for (const [index, accountChunk] of Object.entries(accountsChunk)) {
94+
await dynamoDB.updateItem(
95+
getUpdateItemInput(parametersTableName, `${itemId}/${index}`, JSON.stringify(accountChunk)),
96+
);
97+
}
98+
99+
await dynamoDB.updateItem(
100+
getUpdateItemInput(parametersTableName, accountsItemsCountId, JSON.stringify(accountsChunk.length)),
101+
);
77102

78103
// Find all relevant accounts in the organization
79104
return {

src/core/runtime/src/load-limits-step.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ import { ServiceQuotas } from '@aws-accelerator/common/src/aws/service-quotas';
22
import { Account, getAccountId } from '@aws-accelerator/common-outputs/src/accounts';
33
import { Limit, LimitOutput } from '@aws-accelerator/common-outputs/src/limits';
44
import { STS } from '@aws-accelerator/common/src/aws/sts';
5-
import { SecretsManager } from '@aws-accelerator/common/src/aws/secrets-manager';
65
import { loadAcceleratorConfig } from '@aws-accelerator/common-config/src/load';
76
import { LoadConfigurationInput } from './load-configuration-step';
7+
import { DynamoDB } from '@aws-accelerator/common/src/aws/dynamodb';
8+
import { getUpdateItemInput } from './utils/dynamodb-requests';
89

910
export interface LoadLimitsInput extends LoadConfigurationInput {
10-
limitsSecretId: string;
11+
parametersTableName: string;
12+
itemId: string;
1113
accounts: Account[];
1214
assumeRoleName: string;
1315
}
@@ -51,11 +53,21 @@ const LIMITS: { [limitKey: string]: LimitCode } = {
5153
},
5254
};
5355

56+
const dynamoDB = new DynamoDB();
57+
5458
export const handler = async (input: LoadLimitsInput) => {
5559
console.log(`Loading limits...`);
5660
console.log(JSON.stringify(input, null, 2));
5761

58-
const { configRepositoryName, configFilePath, limitsSecretId, accounts, assumeRoleName, configCommitId } = input;
62+
const {
63+
configRepositoryName,
64+
configFilePath,
65+
parametersTableName,
66+
accounts,
67+
assumeRoleName,
68+
configCommitId,
69+
itemId,
70+
} = input;
5971

6072
// Retrieve Configuration from Code Commit with specific commitId
6173
const config = await loadAcceleratorConfig({
@@ -147,10 +159,6 @@ export const handler = async (input: LoadLimitsInput) => {
147159
}
148160
}
149161

150-
// Store the limits in the secrets manager
151-
const secrets = new SecretsManager();
152-
await secrets.putSecretValue({
153-
SecretId: limitsSecretId,
154-
SecretString: JSON.stringify(limits, null, 2),
155-
});
162+
// Store the limits in the dynamodb
163+
await dynamoDB.updateItem(getUpdateItemInput(parametersTableName, itemId, JSON.stringify(limits, null, 2)));
156164
};

src/core/runtime/src/load-organizations-step.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,28 @@
11
import { OrganizationalUnit } from '@aws-accelerator/common-outputs/src/organizations';
2-
import { SecretsManager } from '@aws-accelerator/common/src/aws/secrets-manager';
32
import { LoadConfigurationInput } from './load-configuration-step';
43
import { loadAcceleratorConfig } from '@aws-accelerator/common-config/src/load';
54
import { Organizations } from '@aws-accelerator/common/src/aws/organizations';
5+
import { DynamoDB } from '@aws-accelerator/common/src/aws/dynamodb';
6+
import { getUpdateItemInput } from './utils/dynamodb-requests';
67

78
export interface LoadOrganizationsInput extends LoadConfigurationInput {
8-
organizationsSecretId: string;
9+
parametersTableName: string;
10+
itemId: string;
911
}
1012

1113
export type LoadOrganizationsOutput = {
1214
organizationalUnits: OrganizationalUnit[];
1315
};
1416

15-
const secrets = new SecretsManager();
1617
const organizations = new Organizations();
18+
const dynamoDB = new DynamoDB();
1719

1820
export const handler = async (input: LoadOrganizationsInput): Promise<OrganizationalUnit[]> => {
1921
console.log('Load Organizations ...');
2022
console.log(JSON.stringify(input, null, 2));
2123

2224
const organizationalUnits: OrganizationalUnit[] = [];
23-
const { organizationsSecretId, configCommitId, configFilePath, configRepositoryName } = input;
25+
const { configCommitId, configFilePath, configRepositoryName, parametersTableName, itemId } = input;
2426
// Retrieve Configuration from Code Commit with specific commitId
2527
const config = await loadAcceleratorConfig({
2628
repositoryName: configRepositoryName,
@@ -44,11 +46,8 @@ export const handler = async (input: LoadOrganizationsInput): Promise<Organizati
4446
});
4547
}
4648

47-
// Store the organizational units configuration in the accounts secret
48-
await secrets.putSecretValue({
49-
SecretId: organizationsSecretId,
50-
SecretString: JSON.stringify(organizationalUnits),
51-
});
49+
// Store the organizations into the dynamodb
50+
await dynamoDB.updateItem(getUpdateItemInput(parametersTableName, itemId, JSON.stringify(organizationalUnits)));
5251

5352
// Find all relevant accounts in the organization
5453
return organizationalUnits;

src/core/runtime/src/ou-validation.ts

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,26 @@ import { AcceleratorConfig, AcceleratorUpdateConfig, AccountsConfig } from '@aws
44
import { ServiceControlPolicy, FULL_AWS_ACCESS_POLICY_NAME } from '@aws-accelerator/common/src/scp';
55
import { Account } from '@aws-accelerator/common-outputs/src/accounts';
66
import { OrganizationalUnit as ConfigOrganizationalUnit } from '@aws-accelerator/common-outputs/src/organizations';
7-
import { SecretsManager } from '@aws-accelerator/common/src/aws/secrets-manager';
87
import { CodeCommit } from '@aws-accelerator/common/src/aws/codecommit';
98
import { LoadConfigurationInput } from './load-configuration-step';
109
import { FormatType, pretty } from '@aws-accelerator/common/src/util/perttier';
1110
import { getFormattedObject, getStringFromObject, equalIgnoreCase } from '@aws-accelerator/common/src/util/common';
1211
import { PutFileEntry } from 'aws-sdk/clients/codecommit';
1312
import { JSON_FORMAT, YAML_FORMAT } from '@aws-accelerator/common/src/util/constants';
1413
import { loadAcceleratorConfig } from '@aws-accelerator/common-config/src/load';
14+
import { DynamoDB } from '@aws-accelerator/common/src/aws/dynamodb';
15+
import { getItemInput } from './utils/dynamodb-requests';
1516

1617
export interface ValdationInput extends LoadConfigurationInput {
1718
acceleratorPrefix: string;
18-
accountsSecretId: string;
19-
organizationsSecretId: string;
19+
parametersTableName: string;
20+
organizationsItemId: string;
21+
accountsItemId: string;
2022
configBranch: string;
2123
}
2224

2325
const organizations = new Organizations();
24-
const secrets = new SecretsManager();
26+
const dynamoDB = new DynamoDB();
2527
const codecommit = new CodeCommit();
2628

2729
/**
@@ -42,8 +44,9 @@ export const handler = async (input: ValdationInput): Promise<string> => {
4244
configRepositoryName,
4345
configCommitId,
4446
acceleratorPrefix,
45-
accountsSecretId,
46-
organizationsSecretId,
47+
parametersTableName,
48+
organizationsItemId,
49+
accountsItemId,
4750
configBranch,
4851
configRootFilePath,
4952
} = input;
@@ -60,8 +63,8 @@ export const handler = async (input: ValdationInput): Promise<string> => {
6063
let rootConfig = getFormattedObject(rootConfigString, format);
6164

6265
let config = previousConfig;
63-
const previousAccounts = await loadAccounts(accountsSecretId);
64-
const previousOrganizationalUnits = await loadOrganizations(organizationsSecretId);
66+
const previousAccounts = await loadAccounts(parametersTableName, accountsItemId);
67+
const previousOrganizationalUnits = await loadOrganizations(parametersTableName, organizationsItemId);
6568
const organizationAdminRole = config['global-options']['organization-admin-role'];
6669
const scps = new ServiceControlPolicy(acceleratorPrefix, organizationAdminRole, organizations);
6770

@@ -294,20 +297,27 @@ function updateAccountConfig(accountConfig: any, accountInfo: UpdateAccountOutpu
294297
}
295298
return accountConfig;
296299
}
297-
async function loadAccounts(accountsSecretId: string): Promise<Account[]> {
298-
const secret = await secrets.getSecret(accountsSecretId);
299-
if (!secret) {
300-
throw new Error(`Cannot find secret with ID "${accountsSecretId}"`);
300+
async function loadAccounts(tableName: string, itemId: string): Promise<Account[]> {
301+
let index = 0;
302+
const accounts: Account[] = [];
303+
while (true) {
304+
const item = await dynamoDB.getItem(getItemInput(tableName, `${itemId}/${index}`));
305+
if (!item.Item) {
306+
break;
307+
}
308+
accounts.push(...JSON.parse(item.Item.value.S!));
309+
index++;
301310
}
302-
return JSON.parse(secret.SecretString!);
311+
return accounts;
303312
}
304313

305-
async function loadOrganizations(organizationsSecretId: string): Promise<ConfigOrganizationalUnit[]> {
306-
const secret = await secrets.getSecret(organizationsSecretId);
307-
if (!secret) {
308-
throw new Error(`Cannot find secret with ID "${organizationsSecretId}"`);
314+
async function loadOrganizations(tableName: string, itemId: string): Promise<ConfigOrganizationalUnit[]> {
315+
const organizationalUnits: ConfigOrganizationalUnit[] = [];
316+
const organizationsOutput = await dynamoDB.getItem(getItemInput(tableName, itemId));
317+
if (!organizationsOutput.Item) {
318+
return organizationalUnits;
309319
}
310-
return JSON.parse(secret.SecretString!);
320+
return JSON.parse(organizationsOutput.Item.value.S!);
311321
}
312322

313323
interface UpdateAccountsOutput {

0 commit comments

Comments
 (0)