in src/deployments/cdk/src/deployments/config/create.ts [47:251]
export async function createRule(props: CreateRuleProps) {
const { acceleratorExecutionRoleName, config, accountStacks, accounts, outputs, defaultRegion } = props;
const awsConfigConf = config['global-options']['aws-config'];
if (!awsConfigConf) {
return;
}
const configRules = awsConfigConf.rules;
const configRuleDefaults = awsConfigConf.defaults;
let configRuleArtifact: ConfigRuleArtifactsOutput | undefined;
const configRuleArtifactOutputs: ConfigRuleArtifactsOutput[] = getStackJsonOutput(outputs, {
accountKey: config.getMandatoryAccountKey('master'),
outputType: 'ConfigRulesArtifactsOutput',
});
if (configRuleArtifactOutputs.length > 0) {
configRuleArtifact = configRuleArtifactOutputs[0];
}
const customRules =
config['global-options']['aws-config']?.rules
.filter(r => r.type === 'custom')
.map(r => r['runtime-path'] || r.name.toLowerCase())
.map(r => (r.endsWith('.zip') ? r : r + '.zip')) || [];
if (configRuleArtifact) {
await downloadCustomRules(
getAccountId(accounts, config.getMandatoryAccountKey('master'))!,
acceleratorExecutionRoleName,
customRules,
configRuleArtifact.bucketName,
configRuleArtifact.keyPrefix,
);
}
for (const [ouKey, ouConfig] of config.getOrganizationalUnits()) {
if (!ouConfig['aws-config']) {
continue;
}
const ouAwsConfigRuleConfigs = ouConfig['aws-config'];
for (const [accountKey, accountConfig] of config.getAccountConfigsForOu(ouKey)) {
console.warn(`Creating config rules in account ${accountKey}`);
const awsAccountConfigRuleConfig = accountConfig['aws-config'];
for (const awsConfigRuleConfig of ouAwsConfigRuleConfigs) {
for (const ruleName of awsConfigRuleConfig.rules) {
console.log(`Creating config rule ${ruleName}`);
const awsConfigRule = configRules.find(cr => cr.name === ruleName);
if (!awsConfigRule) {
console.warn(`Config rule ${ruleName} is not found in Accelerator Configuration global-options`);
continue;
}
console.debug(`Config rule configuration`);
console.debug(JSON.stringify(awsConfigRule, null, 2));
const remediation =
awsConfigRule.remediation === undefined ? configRuleDefaults.remediation : awsConfigRule.remediation;
const remediationAttempts =
awsConfigRule['remediation-attempts'] || configRuleDefaults['remediation-attempts'];
const remediationRetrySeconds =
awsConfigRule['remediation-retry-seconds'] || configRuleDefaults['remediation-retry-seconds'];
const remediationConcurrency =
awsConfigRule['remediation-concurrency'] || configRuleDefaults['remediation-concurrency'];
for (const region of config['global-options']['supported-regions']) {
console.warn(`Creating config rule ${ruleName} in region ${region}`);
if (awsConfigRuleConfig['excl-regions'].includes(region)) {
console.warn(`Skipping creation in excluded region ${region}`);
continue;
}
const isRuleIgnored = awsAccountConfigRuleConfig.find(
ac => ac['excl-rules'].includes(ruleName) && ac.regions.includes(region),
);
if (isRuleIgnored) {
console.warn(`Skipping creation as config rule is excluded for region ${region}`);
continue;
}
const accountStack = accountStacks.tryGetOrCreateAccountStack(accountKey, region);
if (!accountStack) {
console.warn(`Cannot find account stack ${accountKey} in region ${region}`);
continue;
}
const configRuleName = createName({
name: ruleName,
suffixLength: 0,
});
const configParams = getConfigRuleParameters({
ruleParams: awsConfigRule.parameters,
config,
outputs,
accountKey,
defaultRegion,
});
let configRule;
if (awsConfigRule.type === 'managed') {
console.warn(`Creating rule as managed rule`);
configRule = new awsConfig.ManagedRule(accountStack, `ConfigRule-${ruleName}`, {
identifier: ruleName,
configRuleName,
description: configRuleName,
inputParameters: configParams,
});
} else {
console.warn(`Creating rule as custom resource`);
if (!configRuleArtifact) {
console.error('ConfigRuleArtifact is not found to create Custom ConfigRule');
continue;
}
const configRuleRuntime = awsConfigRule['runtime-path'] || awsConfigRule.name.toLowerCase();
const ruleProps: CustomRuleProps = {
roleArn: `arn:${cdk.Aws.PARTITION}:iam::${cdk.Aws.ACCOUNT_ID}:role/${acceleratorExecutionRoleName}`,
configRuleName,
description: configRuleName,
inputParameters: configParams,
ruleScope: {
resourceTypes: awsConfigRule['resource-types'].map(r => awsConfig.ResourceType.of(r)),
},
maximumExecutionFrequency: awsConfigRule['max-frequency']
? (awsConfigRule['max-frequency'] as awsConfig.MaximumExecutionFrequency)
: undefined,
periodic: !!awsConfigRule['max-frequency'],
configurationChanges: !!awsConfigRule['resource-types'].length,
runtimeFileLocation: path.join(configRulesTempDir, configRuleRuntime),
lambdaRuntime: awsConfigRule.runtime!,
};
configRule = new CustomRule(accountStack, `ConfigRule-${ruleName}`, ruleProps).resource;
}
if (!awsConfigRuleConfig['remediate-regions']?.includes(region)) {
continue;
}
if (!remediation || !awsConfigRule['remediation-action']) {
continue;
}
const remediationAction = awsConfigRule['remediation-action'];
const ssmDocumentsConfig = config['global-options']['ssm-automation'];
const remediationActionName = createName({
name: remediationAction,
suffixLength: 0,
});
let targetId = remediationActionName;
const ssmDocInGlobalOptions = ssmDocumentsConfig.find(
d =>
d.documents.find(dc => dc.name === remediationAction) &&
d.regions.includes(region) &&
d.accounts.includes(accountKey),
);
if (!ssmDocInGlobalOptions) {
const ssmDocInAccount = accountConfig['ssm-automation'].find(d =>
d.documents.includes(remediationAction),
);
if (ssmDocInAccount) {
targetId = `arn:${cdk.Aws.PARTITION}:ssm:${cdk.Aws.REGION}:${getAccountId(
accounts,
ssmDocInAccount.account,
)}:document/${remediationActionName}`;
} else {
const ssmDocInOu = ouConfig['ssm-automation'].find(d => d.documents.includes(remediationAction));
if (ssmDocInOu) {
targetId = `arn:${cdk.Aws.PARTITION}:ssm:${cdk.Aws.REGION}:${getAccountId(
accounts,
ssmDocInOu.account,
)}:document/${remediationActionName}`;
} else if (config['global-options']['default-ssm-documents'].includes(remediationAction)) {
targetId = remediationAction;
} else {
console.warn(
`No Remediation "${remediationAction}"is Created in account "${accountKey}" and region "${region}"`,
);
continue;
}
}
} else if (ssmDocInGlobalOptions) {
targetId = remediationActionName;
} else if (config['global-options']['default-ssm-documents'].includes(remediationAction)) {
targetId = remediationAction;
} else {
console.warn(
`Invalid SSM-Document given in "remediation-action" for AWS Config Rule ${awsConfigRule.name}, ${accountKey}`,
);
continue;
}
const remediationParams = getRemediationParameters({
outputs,
remediationParams: awsConfigRule['remediation-params'],
roleName: acceleratorExecutionRoleName,
config,
accountKey,
defaultRegion,
});
new awsConfig.CfnRemediationConfiguration(accountStack, `ConfigRuleRemediation-${ruleName}`, {
configRuleName: configRule.configRuleName,
targetId,
targetType: 'SSM_DOCUMENT',
parameters: remediationParams,
automatic: true,
maximumAutomaticAttempts: remediationAttempts,
retryAttemptSeconds: remediationRetrySeconds,
});
}
}
}
}
}
}