constructor()

in source/lib/events/code_build_events_construct.ts [33:221]


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

    const parentStack = props.callingStack

    /**
     * Create CodeBuild Event Parser Lambda: Transform Cloudwatch metrics for CodeBuild within Kinesis Firehose
     */
    const cwLogsPS = new iam.PolicyStatement({
      actions: ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"],
      effect: iam.Effect.ALLOW,
      resources: [parentStack.formatArn({service: 'logs', resource: 'log-group', sep: ':', resourceName: '/aws/lambda/*'})],
      sid: 'CreateCWLogs'
    })

    const codeBuildEventParserLambdaPolicyName = 'CodeBuildEventParserLambdaPolicy-' + props.uuid

    const codeBuildEventParserLambdaRole = new iam.Role(this, 'CodeBuildEventParserLambdaRole', {
      assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
      path: '/',
      inlinePolicies: {
        [codeBuildEventParserLambdaPolicyName]: new iam.PolicyDocument({
          statements: [
            cwLogsPS
          ]
        })
      }
    });

    const codeBuildEventParserLambda = new lambda.Function(this, 'CodeBuildEventParser', {
      description: 'AWS DevOps Monitoring Dashboard Solution - This function performs lambda transformation within kinesis firehose. It parses CloudWatch metrics for CodeBuild, sends relevant data to S3 for downstream operation',
      environment: {
        LOG_LEVEL: 'INFO',
        UserAgentExtra: props.userAgentExtra
      },
      runtime: props.lambdaRunTime,
      code: lambda.Code.fromAsset(`${__dirname}/../../lambda/event_parser`),
      handler: 'codebuild_index.handler',
      role: codeBuildEventParserLambdaRole,
      timeout: cdk.Duration.seconds(900)
    })

    const refCodeBuildEventParserLambda =  codeBuildEventParserLambda.node.findChild('Resource') as lambda.CfnFunction;
    const lambdaCfnNag = {
      cfn_nag: {
          rules_to_suppress: [
              {
                  id: 'W89',
                  reason: 'There is no need to run this lambda in a VPC'
              },
              {
                  id: 'W92',
                  reason: 'There is no need for Reserved Concurrency'
              }
          ]
      }
    };
    refCodeBuildEventParserLambda.cfnOptions.metadata = lambdaCfnNag;

    /**
     * Create Kinesis Data Firehose using KinesisFirehoseToS3 construct
     */
    const firehoseToS3Construct = new KinesisFirehoseToS3(this, 'CodeBuild', {
      existingBucketObj: props.metricsBucket
    });

    const refFirehoseLogGroup = firehoseToS3Construct.kinesisFirehoseLogGroup.node.findChild('Resource') as logs.CfnLogGroup
    refFirehoseLogGroup.cfnOptions.metadata = {
      cfn_nag: {
        rules_to_suppress: [{
          id: 'W84',
          reason: 'The CloudWatch log group does not need to be encrypted.'
        },
        {
          id: 'W86',
          reason: 'The log data in CloudWatch log group does not need to be expired.'
        }]
      }
    };

    /* Add more configurations to property ExtendedS3DestinationConfiguration */
    const firehoseObj = firehoseToS3Construct.kinesisFirehose
    const firehoseRole = firehoseToS3Construct.kinesisFirehoseRole
    firehoseObj.addPropertyOverride('ExtendedS3DestinationConfiguration', {
      CompressionFormat: 'UNCOMPRESSED',
      Prefix: 'CodeBuildEvents/created_at=!{timestamp:yyyy-MM-dd}/',
      ErrorOutputPrefix: 'CodeBuildEventsProcessingErrorlogs/result=!{firehose:error-output-type}/created_at=!{timestamp:yyyy-MM-dd}/',
      ProcessingConfiguration: {
        Enabled: true,
        Processors: [{
          Type: 'Lambda',
          Parameters: [{
            ParameterName: 'LambdaArn',
            ParameterValue: codeBuildEventParserLambda.functionArn
          }]
        }]
      },
      BufferingHints: {
        IntervalInSeconds: 300,
        SizeInMBs: 64
      },
      DataFormatConversionConfiguration: {
        Enabled: true,
        InputFormatConfiguration: {
          Deserializer: {
            OpenXJsonSerDe: {
              CaseInsensitive: true
            }
          }
        },
        OutputFormatConfiguration: {
          Serializer: {
            ParquetSerDe: {
              Compression: 'SNAPPY'
            }
          }
        },
        SchemaConfiguration: {
          DatabaseName: props.metricsGlueDBName,
          TableName: props.codeBuildMetricsGlueTableName,
          RoleARN: firehoseRole.roleArn
        }
      }
    })

    /* Add more configurations to property DeliveryStreamEncryptionConfigurationInput */
    firehoseObj.addPropertyOverride('DeliveryStreamEncryptionConfigurationInput', {
      KeyType: 'AWS_OWNED_CMK'
    })

    /* Add necessary permissions to firehose role */
    const invokeLambdaPS = new iam.PolicyStatement()
    invokeLambdaPS.sid = 'InvokeLambda'
    invokeLambdaPS.effect = iam.Effect.ALLOW
    invokeLambdaPS.addActions("lambda:InvokeFunction", "lambda:GetFunctionConfiguration")
    invokeLambdaPS.addResources(codeBuildEventParserLambda.functionArn, codeBuildEventParserLambda.functionArn + ':$LATEST')

    const glueAccessPSForFirehose = new iam.PolicyStatement({
      actions: ['glue:GetTable', 'glue:GetTableVersion', 'glue:GetTableVersions'],
      effect: iam.Effect.ALLOW,
      resources: [parentStack.formatArn({ service: 'glue', resource: 'catalog', sep: '', resourceName: '' }),
      parentStack.formatArn({ service: 'glue', resource: 'database', sep: '/', resourceName: props.metricsGlueDBName }),
      parentStack.formatArn({ service: 'glue', resource: 'table', sep: '/', resourceName: `${props.metricsGlueDBName}/${props.codeBuildMetricsGlueTableName}` }),
      parentStack.formatArn({ service: 'glue', resource: 'table', sep: '/', resourceName: `${props.metricsGlueDBName}/*` })],
      sid: 'glueAccessPSForCodeBuildEventsFirehose'
    })

    let firehoseRolePolicy = firehoseToS3Construct.node.findChild('KinesisFirehosePolicy') as iam.Policy;
    if (firehoseRolePolicy !== undefined) {
      firehoseRolePolicy.addStatements(invokeLambdaPS)
      firehoseRolePolicy.addStatements(glueAccessPSForFirehose)
    }

    firehoseObj.node.addDependency(firehoseRolePolicy)

    /**
     * Create CloudWatch Metric Stream
     */
    const firehosePutRecordPS = new iam.PolicyStatement()
    firehosePutRecordPS.sid = 'FirehosePutRecordPS'
    firehosePutRecordPS.effect = iam.Effect.ALLOW
    firehosePutRecordPS.addActions("firehose:PutRecord", "firehose:PutRecordBatch")
    firehosePutRecordPS.addResources(firehoseObj.attrArn)

    const firehosePutRecordPolicyName = 'FirehosePutRecord-' + props.uuid

    const cloudWatchMetricStreamRole = new iam.Role(this, 'CloudWatchMetricStreamRole', {
      assumedBy: new iam.ServicePrincipal('streams.metrics.cloudwatch.amazonaws.com'),
      path: '/',
      inlinePolicies: {
        [firehosePutRecordPolicyName]: new iam.PolicyDocument({
          statements: [
            firehosePutRecordPS
          ]
        })
      }
    });

    new CfnMetricStream(this, "CloudWatchMetricStream", {
      firehoseArn: firehoseObj.attrArn,
      roleArn: cloudWatchMetricStreamRole.roleArn,
      includeFilters: [
        { namespace: 'AWS/CodeBuild' }
      ],
      outputFormat: 'json'
    });


  }