in source/resources/lib/workload.infra.ts [49:517]
constructor(scope: Stack, id: string, props?: NestedStackProps) {
super(scope, id, props);
const stack = Stack.of(this);
this.account = stack.account; // Returns the AWS::AccountId for this stack (or the literal value if known)
this.region = stack.region; // Returns the AWS::Region for this stack (or the literal value if known)
this.partn = stack.partition; // Returns the AWS::Partition for this stack
//=============================================================================================
// Parameters
//=============================================================================================
const workload = new CfnParameter(this, "WorkloadName", {
description: "Name of the workload",
type: "String",
allowedValues: ["Apache", "Nginx", "Puma"],
});
const tag = new CfnParameter(this, "TagSchema", {
description:
'EC2 tag schema to identify workload instances. for eg. {"Key":"CW-Dashboard","Value":"Apache"}',
type: "String",
});
const dashboard = new CfnParameter(this, "DashboardName", {
description:
"Region will be appended to the name. for eg. ApacheDashboard-us-east-1",
type: "String",
});
const accesslog = new CfnParameter(this, "AccessLogGroup", {
description:
"CloudWatch Log Group where instances push their access logs",
type: "String",
});
const ssmparam = new CfnParameter(this, "SSMParameterName", {
description: "SSM parameter used for maintaining workload instance list",
type: "String",
});
//=============================================================================================
// Metadata
//=============================================================================================
this.templateOptions.metadata = {
"AWS::CloudFormation::Interface": {
ParameterGroups: [
{
Label: { default: "Workload Configuration" },
Parameters: [
workload.logicalId,
tag.logicalId,
dashboard.logicalId,
accesslog.logicalId,
ssmparam.logicalId,
],
},
],
ParameterLabels: {
[workload.logicalId]: {
default: "Workload Name",
},
[tag.logicalId]: {
default: "Tag Schema",
},
[dashboard.logicalId]: {
default: "CW Dashboard Name",
},
[accesslog.logicalId]: {
default: "Access Log Group",
},
[ssmparam.logicalId]: {
default: "SSM Parameter Name",
},
},
},
};
this.templateOptions.description = `(${manifest.solutionId}) - AWS CloudFormation template for deployment of the ${manifest.solutionName} workload infrastructure. Version ${manifest.solutionVersion}`;
this.templateOptions.templateFormatVersion = manifest.templateVersion;
//=============================================================================================
// Map
//=============================================================================================
const map = new CfnMapping(this, "StackMap", {
mapping: {
Metric: {
SendAnonymousMetric: manifest.sendMetric,
MetricsEndpoint: manifest.metricsEndpoint, // aws-solutions metrics endpoint
},
Solution: {
SolutionId: manifest.solutionId,
SolutionVersion: manifest.solutionVersion,
},
},
});
//=============================================================================================
// Resources
//=============================================================================================
/**
* @description solution helper function
*/
const helperFunction = new Function(this, "Helper", {
description:
"DO NOT DELETE - CloudWatch Monitoring Framework - helper function",
runtime: Runtime.NODEJS_14_X,
code: Code.fromAsset(
`${path.dirname(__dirname)}/../services/helper/dist/helperFunction.zip`
),
handler: "index.handler",
memorySize: 512,
environment: {
METRICS_ENDPOINT: map.findInMap("Metric", "MetricsEndpoint"),
SEND_METRIC: map.findInMap("Metric", "SendAnonymousMetric"),
LOG_LEVEL: LogLevel.INFO, //change as needed
CUSTOM_SDK_USER_AGENT: `AwsSolution/${map.findInMap(
"Solution",
"SolutionId"
)}/${map.findInMap("Solution", "SolutionVersion")}`,
},
});
/**
* @description iam policy for helper function
* @type {Policy}
*/
const helperPolicy: Policy = new Policy(this, "helperPolicy", {
roles: [helperFunction.role as IRole],
});
/**
* @description iam policy statement for deleting dashboard
* @type {PolicyStatement}
*/
const h0: PolicyStatement = new PolicyStatement({
effect: Effect.ALLOW,
sid: "CWWrite",
actions: ["cloudwatch:DeleteDashboards"],
resources: [
`arn:${this.partn}:cloudwatch::${this.account}:dashboard/${dashboard.valueAsString}-${this.region}`,
],
});
helperPolicy.addStatements(h0);
/**
* @description custom resource for helper functions
* @type {Provider}
*/
const helperProvider: Provider = new Provider(this, "helperProvider", {
onEventHandler: helperFunction,
});
/**
* Get UUID for deployment
*/
const uuid = new CustomResource(this, "CreateUUID", {
resourceType: "Custom::CreateUUID",
serviceToken: helperProvider.serviceToken,
});
/**
* Send launch data to aws-solutions
*/
new CustomResource(this, "LaunchData", {
resourceType: "Custom::LaunchData",
serviceToken: helperProvider.serviceToken,
properties: {
SolutionId: map.findInMap("Solution", "SolutionId"), // solution id for the stack
SolutionVersion: map.findInMap("Solution", "SolutionVersion"),
SolutionUuid: uuid.getAttString("UUID"),
Stack: `${workload.valueAsString}-Stack`,
},
});
/**
* Delete dashboard with delete
*/
new CustomResource(this, "DeleteDeployment", {
resourceType: "Custom::DeleteDeployment",
serviceToken: helperProvider.serviceToken,
properties: {
DashboardName: `${dashboard.valueAsString}-${this.region}`,
},
});
/**
* @description SSM Parameter to maintain list of workload instances
* @type {StringListParameter}
*/
const ssmParameter: StringListParameter = new StringListParameter(
this,
"SSMParameter",
{
stringListValue: ["NOP"],
parameterName: ssmparam.valueAsString,
tier: ParameterTier.ADVANCED,
simpleName: false,
}
);
/**
* @description dead letter queue for lambda
* @type {Queue}
*/
const dlq: Queue = new Queue(this, `dlq`, {
encryption: QueueEncryption.KMS_MANAGED,
});
/**
* @description lambda to handle workload tag events
*/
const tagHandler = new Function(this, "TagHandler", {
description: `DO NOT DELETE - CloudWatch Monitoring Framework - ${workload.valueAsString} tag handler function`,
runtime: Runtime.NODEJS_14_X,
code: Code.fromAsset(
`${path.dirname(__dirname)}/../services/tagHandler/dist/tagHandler.zip`
),
handler: "index.handler",
memorySize: 512,
environment: {
METRICS_ENDPOINT: map.findInMap("Metric", "MetricsEndpoint"),
SEND_METRIC: map.findInMap("Metric", "SendAnonymousMetric"),
LOG_LEVEL: LogLevel.INFO, //change as needed
SSM_PARAMETER: ssmParameter.parameterName,
TAG_SCHEMA: tag.valueAsString,
CUSTOM_SDK_USER_AGENT: `AwsSolution/${map.findInMap(
"Solution",
"SolutionId"
)}/${map.findInMap("Solution", "SolutionVersion")}`,
},
deadLetterQueue: dlq,
});
/**
* @description iam policy for tag handler role
* @type {Policy}
*/
const po: Policy = new Policy(this, "tagHandlerPolicy", {
roles: [tagHandler.role as IRole],
});
/**
* @description iam policy statement for read/write ssm parameter
* @type {PolicyStatement}
*/
const po0: PolicyStatement = new PolicyStatement({
effect: Effect.ALLOW,
sid: "SSMWrite",
actions: ["ssm:PutParameter", "ssm:GetParameter"],
resources: [ssmParameter.parameterArn],
});
po.addStatements(po0);
/**
* @description iam policy statement for reading instance tags
* @type {PolicyStatement}
*/
const po1: PolicyStatement = new PolicyStatement({
effect: Effect.ALLOW,
sid: "EC2Describe",
actions: ["ec2:DescribeTags"],
resources: ["*"],
});
po.addStatements(po1);
/**
* @description lambda for dashboard CRUD operations
*/
const dashboardHandler = new Function(this, "DashboardHandler", {
description: `DO NOT DELETE - CloudWatch Monitoring Framework - ${workload.valueAsString} dashboard handler function`,
runtime: Runtime.NODEJS_14_X,
code: Code.fromAsset(
`${path.dirname(
__dirname
)}/../services/dashboardHandler/dist/dashboardHandler.zip`
),
handler: "index.handler",
memorySize: 512,
environment: {
METRICS_ENDPOINT: map.findInMap("Metric", "MetricsEndpoint"),
SEND_METRIC: map.findInMap("Metric", "SendAnonymousMetric"),
LOG_LEVEL: LogLevel.INFO, //change as needed
SSM_PARAMETER: ssmParameter.parameterName,
ACCESS_LOG_GROUP: accesslog.valueAsString,
DASHBOARD_NAME: `${dashboard.valueAsString}-${this.region}`,
TAG_SCHEMA: tag.valueAsString,
WORKLOAD: workload.valueAsString, // env variable to identify workload
START_TIME: "-PT12H",
CUSTOM_SDK_USER_AGENT: `AwsSolution/${map.findInMap(
"Solution",
"SolutionId"
)}/${map.findInMap("Solution", "SolutionVersion")}`,
},
deadLetterQueue: dlq,
});
/**
* @description iam policy for dashboard handler role
* @type {Policy}
*/
const dh: Policy = new Policy(this, "dashboardHandlerPolicy", {
roles: [dashboardHandler.role as IRole],
});
/**
* @description iam policy statement for reading ssm parameter
* @type {PolicyStatement}
*/
const dh0: PolicyStatement = new PolicyStatement({
effect: Effect.ALLOW,
sid: "SSMRead",
actions: ["ssm:GetParameter"],
resources: [ssmParameter.parameterArn],
});
dh.addStatements(dh0);
/**
* @description iam policy statement for putting/deleting dashboard
* @type {PolicyStatement}
*/
const dh1: PolicyStatement = new PolicyStatement({
effect: Effect.ALLOW,
sid: "CWWrite",
actions: ["cloudwatch:PutDashboard", "cloudwatch:DeleteDashboards"],
resources: [
`arn:${this.partn}:cloudwatch::${this.account}:dashboard/${dashboard.valueAsString}-${this.region}`,
],
});
dh.addStatements(dh1);
/**
* @description Events Rule for SSM Parameter update
* @type {Rule}
*/
new Rule(this, `SSMRule`, {
ruleName: `${workload.valueAsString}-SSMRule`,
eventPattern: {
source: ["aws.ssm"],
detailType: ["Parameter Store Change"],
detail: {
name: [ssmParameter.parameterName],
},
},
targets: [new LambdaFunction(dashboardHandler)],
});
/**
* @description Events Rule for EC2 Tag update
* @type {Rule}
*/
new Rule(this, `EC2TagRule`, {
ruleName: `${workload.valueAsString}-EC2TagRule`,
schedule: Schedule.rate(Duration.minutes(5)),
targets: [new LambdaFunction(tagHandler)],
});
//=============================================================================================
// cfn_nag suppress rules
//=============================================================================================
const _po = po.node.findChild("Resource") as CfnPolicy;
_po.cfnOptions.metadata = {
cfn_nag: {
rules_to_suppress: [
{
id: "W12",
reason:
"* is required for ec2:DescribeTags, as it does not support resource level permissions",
},
],
},
};
const tH = tagHandler.node.findChild("Resource") as CfnFunction;
tH.cfnOptions.metadata = {
cfn_nag: {
rules_to_suppress: [
{
id: "W58",
reason:
"CloudWatch logs write permissions added with managed role AWSLambdaBasicExecutionRole",
},
{
id: "W89",
reason: "Not valid use case to deploy Lambda in VPC",
},
{
id: "W92",
reason: "Not valid use case for ReservedConcurrentExecutions",
},
],
},
};
const dH = dashboardHandler.node.findChild("Resource") as CfnFunction;
dH.cfnOptions.metadata = {
cfn_nag: {
rules_to_suppress: [
{
id: "W58",
reason:
"CloudWatch logs write permissions added with managed role AWSLambdaBasicExecutionRole",
},
{
id: "W89",
reason: "Not valid use case to deploy Lambda in VPC",
},
{
id: "W92",
reason: "Not valid use case for ReservedConcurrentExecutions",
},
],
},
};
const hF = helperFunction.node.findChild("Resource") as CfnFunction;
hF.cfnOptions.metadata = {
cfn_nag: {
rules_to_suppress: [
{
id: "W58",
reason:
"CloudWatch logs write permissions added with managed role AWSLambdaBasicExecutionRole",
},
{
id: "W89",
reason: "Not valid use case to deploy Lambda in VPC",
},
{
id: "W92",
reason: "Not valid use case for ReservedConcurrentExecutions",
},
],
},
};
const hpP = helperProvider.node.children[0].node.findChild(
"Resource"
) as CfnFunction;
hpP.cfnOptions.metadata = {
cfn_nag: {
rules_to_suppress: [
{
id: "W58",
reason:
"CloudWatch logs write permissions added with managed role AWSLambdaBasicExecutionRole",
},
{
id: "W89",
reason: "Not valid use case to deploy Lambda in VPC",
},
{
id: "W92",
reason: "Not valid use case for ReservedConcurrentExecutions",
},
],
},
};
//=============================================================================================
// Output
//=============================================================================================
new CfnOutput(this, "UUID", {
description: "UUID for deployment",
value: uuid.getAttString("UUID"),
});
new CfnOutput(this, `Dashboard`, {
description: `CloudWatch Dashboard for the workload`,
value: `${dashboard.valueAsString}-${this.region}`,
});
}