constructor()

in source/resources/lib/cl-demo-stack.ts [72:319]


  constructor(scope: Construct, 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)

    //=============================================================================================
    // Parameters
    //=============================================================================================
    /**
     * @description parameter for CW Logs Destination Arn
     * @type {CfnParameter}
     */
    const cwLogsDestinationArn: CfnParameter = new CfnParameter(
      this,
      "CWDestinationParm",
      {
        type: "String",
      }
    );

    //=============================================================================================
    // Metadata
    //=============================================================================================
    this.templateOptions.metadata = {
      "AWS::CloudFormation::Interface": {
        ParameterGroups: [
          {
            Label: { default: "Destination Configuration" },
            Parameters: [cwLogsDestinationArn.logicalId],
          },
        ],
        ParameterLabels: {
          [cwLogsDestinationArn.logicalId]: {
            default: "CloudWatch Logs Destination Arn for Log Streaming",
          },
        },
      },
    };

    this.templateOptions.description = `(${manifest.solutionId}D) - The AWS CloudFormation template for deployment of the ${manifest.solutionName}. Version ${manifest.solutionVersion}`;
    this.templateOptions.templateFormatVersion = manifest.templateVersion;

    //=============================================================================================
    // Map
    //=============================================================================================
    new CfnMapping(this, "EC2", {
      mapping: { Instance: { Type: "t3.micro" } },
    });

    new CfnMapping(this, "FilterPatternLookup", {
      mapping: {
        Common: {
          Pattern:
            "[host, ident, authuser, date, request, status, bytes, referrer, agent]",
        },
        CloudTrail: {
          Pattern: "",
        },
        FlowLogs: {
          Pattern:
            '[version, account_id, interface_id, srcaddr != "-", dstaddr != "-", srcport != "-", dstport != "-", protocol, packets, bytes, start, end, action, log_status]',
        },
        Lambda: {
          Pattern: '[timestamp=*Z, request_id="*-*", event]',
        },
        SpaceDelimited: {
          Pattern: "[]",
        },
        Other: {
          Pattern: "",
        },
      },
    });

    //=============================================================================================
    // Resources
    //=============================================================================================

    /**
     * @description demo vpc with 1 public subnet
     * @type {Vpc}
     */
    const demoVPC: Vpc = new Vpc(this, "DemoVPC", {
      cidr: "10.0.1.0/26",
      natGateways: 0,
      vpnGateway: false,
      subnetConfiguration: [
        {
          cidrMask: 28,
          name: "PublicSubnet",
          subnetType: SubnetType.PUBLIC,
        },
      ],
    });
    demoVPC.publicSubnets.forEach((subnet) => {
      applyCfnNagSuppressRules(subnet.node.defaultChild as CfnResource, [
        cfn_suppress_rules.W33,
      ]);
    });

    //===================
    // FlowLog resources
    //===================
    /**
     * @description log group for VPC flow logs
     * @type {LogGroup}
     */
    const flowLg: LogGroup = new LogGroup(this, "VPCFlowLogGroup", {
      removalPolicy: RemovalPolicy.DESTROY,
      retention: RetentionDays.ONE_WEEK,
    });

    /**
     * @description iam role for flow logs
     * @type {Role}
     */
    const flowRole: Role = new Role(this, "flowRole", {
      assumedBy: new ServicePrincipal("vpc-flow-logs.amazonaws.com"),
    });

    /**
     * @description demo flow logs
     * @type {FlowLog}
     */
    new FlowLog(this, "DemoFlowLog", {
      resourceType: FlowLogResourceType.fromVpc(demoVPC),
      trafficType: FlowLogTrafficType.ALL,
      destination: FlowLogDestination.toCloudWatchLogs(flowLg, flowRole),
    });

    /**
     * @description subscription filter for flow logs
     * @type {SubscriptionFilter}
     */
    new CfnSubscriptionFilter(this, "FlowLogSubscription", {
      destinationArn: cwLogsDestinationArn.valueAsString,
      filterPattern: Fn.findInMap("FilterPatternLookup", "FlowLogs", "Pattern"),
      logGroupName: flowLg.logGroupName,
    });

    //====================
    // WebServer resources
    //====================
    /**
     * @description ec2 web server resources
     * @type {EC2Demo}
     */
    const ec2: EC2Demo = new EC2Demo(this, "WebServer", {
      destination: cwLogsDestinationArn.valueAsString,
      demoVpc: demoVPC,
    });

    //=====================
    // CloudTrail resources
    //=====================
    /**
     * @description log group for CloudTrail
     * @type {LogGroup}
     */
    const cloudtrailLg: LogGroup = new LogGroup(this, "CloudTrailLogGroup", {
      removalPolicy: RemovalPolicy.DESTROY,
      retention: RetentionDays.ONE_WEEK,
    });

    /**
     * @description bucket for CloudTrail
     * @type {Bucket}
     */
    const trailBucket: Bucket = new Bucket(this, "TrailBucket", {
      encryption: BucketEncryption.S3_MANAGED,
      blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
    });
    trailBucket.addToResourcePolicy(
      new PolicyStatement({
        effect: Effect.ALLOW,
        principals: [new ServicePrincipal("cloudtrail.amazonaws.com")],
        sid: "CloudTrailRead",
        actions: ["s3:GetBucketAcl"],
        resources: [trailBucket.bucketArn],
      })
    );
    trailBucket.addToResourcePolicy(
      new PolicyStatement({
        effect: Effect.ALLOW,
        principals: [new ServicePrincipal("cloudtrail.amazonaws.com")],
        sid: "CloudTrailWrite",
        actions: ["s3:PutObject"],
        resources: [`${trailBucket.bucketArn}/AWSLogs/${this.account}/*`],
      })
    );

    /**
     * @description demo trail
     * @type {Trail}
     */
    new Trail(this, "demoTrail", {
      bucket: trailBucket,
      cloudWatchLogGroup: cloudtrailLg,
      isMultiRegionTrail: false,
      sendToCloudWatchLogs: true,
      includeGlobalServiceEvents: true,
    });

    /**
     * @description subscription filter for cloudtrail logs
     * @type {SubscriptionFilter}
     */
    new CfnSubscriptionFilter(this, "CloudTrailSubscription", {
      destinationArn: cwLogsDestinationArn.valueAsString,
      filterPattern: Fn.findInMap(
        "FilterPatternLookup",
        "CloudTrail",
        "Pattern"
      ),
      logGroupName: cloudtrailLg.logGroupName,
    });

    //=============================================================================================
    // cfn_nag suppress rules
    //=============================================================================================
    applyCfnNagSuppressRules(trailBucket.node.defaultChild as CfnResource, [
      cfn_suppress_rules.W35,
    ]);

    applyCfnNagSuppressRules(flowLg.node.findChild("Resource") as CfnResource, [
      cfn_suppress_rules.W84,
    ]);

    applyCfnNagSuppressRules(
      cloudtrailLg.node.findChild("Resource") as CfnResource,
      [cfn_suppress_rules.W84]
    );

    //=============================================================================================
    // Output
    //=============================================================================================
    new CfnOutput(this, "Destination Arn", {
      description: "CloudWatch Logs destination arn",
      value: cwLogsDestinationArn.valueAsString,
    });

    new CfnOutput(this, "URL", {
      description: "URL for demo web server",
      value: `http://${ec2.publicIp}`,
    });
  }