constructor()

in source/lib/limit-monitor-spoke-stack.ts [26:357]


    constructor(scope: cdk.Construct, id: string, props: LimitMonitorStackProps) {
        super(scope, id, props)

        const primaryAccount = new cdk.CfnParameter(this, 'MasterAccount', {
            description: 'Account Id for the master account, eg. 111111111111',
            type: 'String',
            allowedPattern: '^\\d{12}$'
        })

        const metricsMap = new cdk.CfnMapping(this, 'MetricsMap')
        metricsMap.setValue('Send-Data', 'SendAnonymousData', 'Yes')

        const refreshRate = new cdk.CfnMapping(this, 'RefreshRate')
        refreshRate.setValue('CronSchedule', 'Default', 'rate(1 day)')

        const eventsMap = new cdk.CfnMapping(this, 'EventsMap')
        eventsMap.setValue('Checks', 'Services', '"AutoScaling","CloudFormation","DynamoDB","EBS","EC2","ELB","IAM","Kinesis","RDS","Route53","SES","VPC"')

        const eventBusTarget = `arn:${cdk.Aws.PARTITION}:events:us-east-1:${primaryAccount.valueAsString}:event-bus/default`

        const eventOkTarget: events.IRuleTarget = {
            bind: () => ({
                id: 'SpokeOkTarget',
                arn: eventBusTarget
            })
        }

        const eventOkRule = new events.Rule(this, 'TAOkRule', {
            description: 'Limit Monitor Solution - Spoke - Rule for TA OK events',
            enabled: true,
            schedule: events.Schedule.expression('rate(24 hours)'),
            targets: [eventOkTarget]
        })
        const eventOKRule_cfn_ref = eventOkRule.node.defaultChild as events.CfnRule
        eventOKRule_cfn_ref.overrideLogicalId('TAOkRule')
        eventOKRule_cfn_ref.addOverride('Properties.EventPattern', {
            "Fn::Join": [
                "",
                [
                    "{\"account\":[\"",
                    {
                        "Ref": "AWS::AccountId"
                    },
                    "\"],",
                    "\"source\":[\"aws.trustedadvisor\", \"limit-monitor-solution\"],",
                    "\"detail-type\":[\"Trusted Advisor Check Item Refresh Notification\", \"Limit Monitor Checks\"],",
                    "\"detail\":{",
                    "\"status\":[",
                    "\"OK\"",
                    "],",
                    "\"check-item-detail\":{",
                    "\"Service\":[",
                    {
                        "Fn::FindInMap": [
                            "EventsMap",
                            "Checks",
                            "Services"
                        ]
                    },
                    "]",
                    "}",
                    "}",
                    "}"
                ]
            ]
        })
        eventOKRule_cfn_ref.addPropertyDeletionOverride('ScheduleExpression')

        const eventWarnTarget: events.IRuleTarget = {
            bind: () => ({
                id: 'SpokeWarnTarget',
                arn: eventBusTarget
            })
        }

        const eventWarnRule = new events.Rule(this, 'TAWarnRule', {
            description: 'Limit Monitor Solution - Spoke - Rule for TA WARN events',
            enabled: true,
            schedule: events.Schedule.expression('rate(24 hours)'),
            targets: [eventWarnTarget]
        })

        const eventWarnRule_cfn_ref = eventWarnRule.node.defaultChild as events.CfnRule
        eventWarnRule_cfn_ref.overrideLogicalId('TAWarnRule')
        eventWarnRule_cfn_ref.addOverride('Properties.EventPattern', {
            "Fn::Join": [
                "",
                [
                    "{\"account\":[\"",
                    {
                        "Ref": "AWS::AccountId"
                    },
                    "\"],",
                    "\"source\":[\"aws.trustedadvisor\", \"limit-monitor-solution\"],",
                    "\"detail-type\":[\"Trusted Advisor Check Item Refresh Notification\", \"Limit Monitor Checks\"],",
                    "\"detail\":{",
                    "\"status\":[",
                    "\"WARN\"",
                    "],",
                    "\"check-item-detail\":{",
                    "\"Service\":[",
                    {
                        "Fn::FindInMap": [
                            "EventsMap",
                            "Checks",
                            "Services"
                        ]
                    },
                    "]",
                    "}",
                    "}",
                    "}"
                ]
            ]
        })
        eventWarnRule_cfn_ref.addPropertyDeletionOverride('ScheduleExpression')

        const eventErrorTarget: events.IRuleTarget = {
            bind: () => ({
                id: 'SpokeErrorTarget',
                arn: eventBusTarget
            })
        }

        const eventErrorRule = new events.Rule(this, 'TASErrorRule', {
            description: 'Limit Monitor Solution - Spoke - Rule for TA ERROR events',
            enabled: true,
            schedule: events.Schedule.expression('rate(24 hours)'),
            targets: [eventErrorTarget]
        })

        const eventErrorRule_cfn_ref = eventErrorRule.node.defaultChild as events.CfnRule
        eventErrorRule_cfn_ref.overrideLogicalId('TASErrorRule')
        eventErrorRule_cfn_ref.addOverride('Properties.EventPattern', {
            "Fn::Join": [
                "",
                [
                    "{\"account\":[\"",
                    {
                        "Ref": "AWS::AccountId"
                    },
                    "\"],",
                    "\"source\":[\"aws.trustedadvisor\", \"limit-monitor-solution\"],",
                    "\"detail-type\":[\"Trusted Advisor Check Item Refresh Notification\", \"Limit Monitor Checks\"],",
                    "\"detail\":{",
                    "\"status\":[",
                    "\"ERROR\"",
                    "],",
                    "\"check-item-detail\":{",
                    "\"Service\":[",
                    {
                        "Fn::FindInMap": [
                            "EventsMap",
                            "Checks",
                            "Services"
                        ]
                    },
                    "]",
                    "}",
                    "}",
                    "}"
                ]
            ]
        })
        eventErrorRule_cfn_ref.addPropertyDeletionOverride('ScheduleExpression')



        const solutionsLambdaCodeBucket = s3.Bucket.fromBucketAttributes(this, 'SolutionsBucket', {
            bucketName: props.solutionBucket + '-' + Aws.REGION
        });

        const eventRuleLambda = new eventLambda.EventsRuleToLambda(this, 'TARefreshSchedule', {
            eventRuleProps: {
                description: 'Schedule to refresh TA checks',
                schedule: events.Schedule.expression(refreshRate.findInMap('CronSchedule', 'Default')),
                enabled: true,
            },
            lambdaFunctionProps: {
                runtime: lambda.Runtime.NODEJS_12_X,
                code: lambda.Code.fromBucket(solutionsLambdaCodeBucket, props.solutionName + '/' + props.solutionVersion + '/limtr-refresh-service.zip'),
                description: 'Serverless Limit Monitor - Lambda function to summarize service limits',
                timeout: cdk.Duration.seconds(300),
                handler: 'index.handler',
                environment: {
                    LOG_LEVEL: 'INFO',
                    AWS_SERVICES: eventsMap.findInMap('Checks', 'Services')
                }
            }
        })

        const logsPolicy: PolicyStatement = new PolicyStatement({
            actions: [
                'logs:CreateLogGroup',
                'logs:CreateLogStream',
                'logs:PutLogEvents'
            ],
            effect: Effect.ALLOW,
            resources: [
                `arn:${cdk.Aws.PARTITION}:logs:${this.region}:${this.account}:log-group:/aws/lambda/*`
            ]
        })

        const supportPolicy: PolicyStatement = new PolicyStatement({
            actions: [
                'support:*'
            ],
            effect: Effect.ALLOW,
            resources: [
                '*'
            ]
        })

        const serviceQuotasPolicy: PolicyStatement = new PolicyStatement({
            actions: [
                'servicequotas:GetAWSDefaultServiceQuota'
            ],
            effect: Effect.ALLOW,
            resources: ['*']
        })

        eventRuleLambda.lambdaFunction.addToRolePolicy(logsPolicy)
        eventRuleLambda.lambdaFunction.addToRolePolicy(supportPolicy)
        eventRuleLambda.lambdaFunction.addToRolePolicy(serviceQuotasPolicy)

        const cfnLambdaFunctionDefPolicy = eventRuleLambda.lambdaFunction.role?.node.tryFindChild('DefaultPolicy')?.node.findChild('Resource') as iam.CfnPolicy;
        const cfnLambdaFunction = eventRuleLambda.lambdaFunction.node.findChild("Resource") as CfnResource;
        // Add the CFN NAG suppress to allow for "Resource": "*" for AWS X-Ray and support * actions.
        cfnLambdaFunctionDefPolicy.cfnOptions.metadata = {
            cfn_nag: {
                rules_to_suppress: [{
                    id: 'W12',
                    reason: `Lambda needs the following minimum required permissions to send trace data to X-Ray.`
                }, {
                    id: 'F4',
                    reason: `Lambda needs the support * to perform the functions for monitoring resources.`
                }
            ]}};
            cfnLambdaFunction.cfnOptions.metadata = {
                cfn_nag: {
                  rules_to_suppress: [
                    {
                      id: "W89",
                      reason: "Not a valid use case to deploy in VPC",
                    },
                    {
                      id: "W92",
                      reason: "ReservedConcurrentExecutions not needed",
                    }
                  ],
                },
              };


        //End TARefresherLambda and Event Rule Resource.

        //START Limit Monitor Helper Lambda and Role.

        const limtrHelperRole = new iam.Role(this, 'LimtrHelperRole', {
            assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
            path: '/',
            inlinePolicies: {
                'Custom_Limtr_Helper_Permissions': new PolicyDocument({
                    statements: [
                        logsPolicy
                    ]
                })
            }
        })

        const cfn_ref_limtrHelperRole = limtrHelperRole.node.defaultChild as iam.CfnRole
        cfn_ref_limtrHelperRole.overrideLogicalId('LimtrHelperRole')

        const limtrHelperFunction = new lambda.Function(this, 'LimtrHelperFunction', {
                    runtime: lambda.Runtime.NODEJS_12_X,
                    description: 'This function generates UUID, establishes cross account trust on CloudWatch Event Bus and sends anonymous metric',
                    handler: 'index.handler',
                    code: lambda.Code.fromBucket(solutionsLambdaCodeBucket, props.solutionName + '/' + props.solutionVersion + '/limtr-helper-service.zip'),
                    timeout: cdk.Duration.seconds(300),
                    environment: {
                        LOG_LEVEL: 'INFO'
                    },
                    role: limtrHelperRole
                })
        //END Limit Monitor Helper Lambda and Role

        const cfn_ref_limtrFunction = limtrHelperFunction.node.defaultChild as lambda.CfnFunction
        cfn_ref_limtrFunction.overrideLogicalId('LimtrHelperFunction')
        cfn_ref_limtrFunction.cfnOptions.metadata = {
            cfn_nag: {
              rules_to_suppress: [
                {
                  id: "W89",
                  reason: "Not a valid use case to deploy in VPC",
                },
                {
                  id: "W92",
                  reason: "ReservedConcurrentExecutions not needed",
                }
              ],
            },
          };



        const customUUID = new cdk.CustomResource(this, 'CreateUUID', {
                    resourceType: 'Custom::UUID',
                    serviceToken: limtrHelperFunction.functionArn
                })

        new cdk.CustomResource(this, 'DeploymentData', {
                    resourceType: 'Custom::DeploymentData',
                    serviceToken: limtrHelperFunction.functionArn,
                    properties: {
                        SOLUTION: props.solutionId + 's',
                        UUID: customUUID.getAtt('UUID'),
                        VERSION: props.solutionVersion,
                        ANONYMOUS_DATA: metricsMap.findInMap('Send-Data', 'SendAnonymousData')
                    }
                })

        new CfnStack(this, 'limitCheckStack', {
                    templateUrl: 'https://s3.amazonaws.com/' + props.solutionTemplateBucket + '/' + props.solutionName + '/' + props.solutionVersion + '/' + 'service-quotas-checks.template'
                })

        //stack outputs
        new cdk.CfnOutput(this, 'ServiceChecks', {
                    value: eventsMap.findInMap('Checks', 'Services'),
                    description: 'service limit checks monitored in the account'
                })

            }