constructor()

in source/resources/lib/common.ts [67:460]


  constructor(scope: Construct, id: string) {
    super(scope, id);
    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
    //=============================================================================================
    const complianceGeneratorParam = new CfnParameter(
      this,
      "DeployComplianceGenerator",
      {
        type: "String",
        allowedValues: ["Yes", "No"],
        default: "Yes",
      }
    );

    //=============================================================================================
    // Metadata
    //=============================================================================================
    this.templateOptions.metadata = {
      "AWS::CloudFormation::Interface": {
        ParameterGroups: [
          {
            Label: {
              default:
                "Do you want to generate compliance reports for your FMS policies?",
            },
            Parameters: [complianceGeneratorParam.logicalId],
          },
        ],
        ParameterLabels: {
          [complianceGeneratorParam.logicalId]: {
            default: "Compliance Reporting",
          },
        },
      },
    };
    this.templateOptions.description = `(${manifest.solution.primarySolutionId}) - The AWS CloudFormation template for deployment of the ${manifest.solution.name}. Version ${manifest.solution.solutionVersion}`;
    this.templateOptions.templateFormatVersion =
      manifest.solution.templateVersion;

    //=============================================================================================
    // Map
    //=============================================================================================
    const map = new CfnMapping(this, "CommonResourceStackMap", {
      mapping: {
        Metric: {
          SendAnonymousMetric: manifest.solution.sendMetric,
          MetricsEndpoint: manifest.solution.metricsEndpoint, // aws-solutions metrics endpoint
        },
        Solution: {
          SolutionId: manifest.solution.primarySolutionId,
          SolutionVersion: manifest.solution.solutionVersion,
        },
      },
    });

    //=============================================================================================
    // Condition
    //=============================================================================================
    const complianceReportingCheck = new CfnCondition(this, "reportingCheck", {
      expression: Fn.conditionEquals(
        complianceGeneratorParam.valueAsString,
        "Yes"
      ),
    });

    //=============================================================================================
    // Resources
    //=============================================================================================
    /**
     * @description lambda backed custom resource to validate and install pre-reqs
     * @type {Function}
     */
    const helperFunction: Function = new Function(this, "HelperFunction", {
      description: `${map.findInMap(
        "Solution",
        "SolutionId"
      )} - Function to help with FMS solution installation (DO NOT DELETE)`,
      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: LOG_LEVEL.INFO, //change as needed
        CUSTOM_SDK_USER_AGENT: `AwsSolution/${map.findInMap(
          "Solution",
          "SolutionId"
        )}/${map.findInMap("Solution", "SolutionVersion")}`,
      },
    });

    /**
     * @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,
    });

    /**
     * Check deployment account
     */
    new CustomResource(this, "FMSAdminCheck", {
      resourceType: "Custom::FMSAdminCheck",
      serviceToken: helperProvider.serviceToken,
      properties: {
        Stack: "FMSStack",
        Account: this.account,
        Region: this.region,
      },
    });

    /**
     * Send launch data to aws-solutions
     */
    new CustomResource(this, "LaunchData", {
      resourceType: "Custom::LaunchData",
      serviceToken: helperProvider.serviceToken,
      properties: {
        SolutionId: map.findInMap("Solution", "SolutionId"),
        SolutionVersion: map.findInMap("Solution", "SolutionVersion"),
        SolutionUuid: uuid.getAttString("UUID"),
        Stack: "FMSStack",
      },
    });

    /**
     * @description dynamodb table for policy items
     * @type {Table}
     */
    const table: Table = new Table(this, "FMSTable", {
      partitionKey: {
        name: "PolicyName",
        type: AttributeType.STRING,
      },
      sortKey: {
        name: "Region",
        type: AttributeType.STRING,
      },
      pointInTimeRecovery: true,
      serverSideEncryption: true,
      removalPolicy: RemovalPolicy.DESTROY,
      billingMode: BillingMode.PAY_PER_REQUEST,
    });

    /**
     * @description dead letter queue for lambda
     * @type {Queue}
     */
    const dlq: Queue = new Queue(this, `MetricsDLQ`, {
      encryption: QueueEncryption.KMS_MANAGED,
    });
    const metricsQueue: Queue = new Queue(this, `MetricsQueue`, {
      encryption: QueueEncryption.KMS_MANAGED,
      deadLetterQueue: {
        maxReceiveCount: 5,
        queue: Queue.fromQueueArn(this, "dlq", dlq.queueArn),
      },
    });

    /**
     * @description SQS queue policy to enforce only encrypted connections over HTTPS,
     * adding aws:SecureTransport in conditions
     * @type {QueuePolicy}
     */
    const queuePolicy: QueuePolicy = new QueuePolicy(this, "QueuePolicy", {
      queues: [dlq, metricsQueue],
    });
    queuePolicy.document.addStatements(
      new PolicyStatement({
        sid: "AllowPublishThroughSSLOnly01",
        actions: ["sqs:*"],
        effect: Effect.DENY,
        resources: [dlq.queueArn],
        conditions: {
          ["Bool"]: {
            "aws:SecureTransport": "false",
          },
        },
        principals: [new AnyPrincipal()],
      }),
      new PolicyStatement({
        sid: "AllowPublishThroughSSLOnly02",
        actions: ["sqs:*"],
        effect: Effect.DENY,
        resources: [metricsQueue.queueArn],
        conditions: {
          ["Bool"]: {
            "aws:SecureTransport": "false",
          },
        },
        principals: [new AnyPrincipal()],
      })
    );

    /**
     * @description lambda function to publish metrics
     * @type {Function}
     */
    const metricsManager: Function = new Function(this, "MetricsManager", {
      description: `${map.findInMap(
        "Solution",
        "SolutionId"
      )} - Function to publish FMS solution metrics to aws-solutions`,
      runtime: Runtime.NODEJS_14_X,
      code: Code.fromAsset(
        `${path.dirname(
          __dirname
        )}/../services/metricsManager/dist/metricsManager.zip`
      ),
      handler: "index.handler",
      memorySize: 128,
      reservedConcurrentExecutions: 1,
      environment: {
        METRICS_ENDPOINT: map.findInMap("Metric", "MetricsEndpoint"),
        LOG_LEVEL: LOG_LEVEL.INFO, //change as needed
      },
      timeout: Duration.seconds(15),
    });
    metricsManager.addEventSource(
      new SqsEventSource(metricsQueue, {
        batchSize: 1,
      })
    );

    /**
     * @description iam permissions for the helper lambda function
     * @type {Policy}
     */
    const po: Policy = new Policy(this, "HelperPolicy", {
      policyName: manifest.commonResourceStack.helperPolicy,
      roles: [helperFunction.role!],
    });

    /**
     * @description iam policy for the helper lambda function
     * @type {PolicyStatement}
     */
    const po0: PolicyStatement = new PolicyStatement({
      effect: Effect.ALLOW,
      sid: "FMSRead",
      actions: ["fms:GetAdminAccount"],
      resources: ["*"],
    });
    po.addStatements(po0);

    /**
     * @description compliance generator stack
     * @type {NestedStack}
     */
    const complianceStack: NestedStack = new ComplianceGeneratorStack(
      this,
      "ComplianceGeneratorStack",
      {
        parameters: {
          ["MetricsQueue"]: metricsQueue.queueName,
          ["UUID"]: uuid.getAttString("UUID"),
        },
      }
    );
    complianceStack.nestedStackResource!.cfnOptions.condition =
      complianceReportingCheck;

    /**
     * @description nested stack for default policy
     * @type {NestedStack}
     */
    PolicyIdentifiers.forEach((policyId) => {
      new PolicyStack(this, `PolicyStack-${policyId}`, {
        parameters: {
          ["PolicyTable"]: table.tableName,
          ["MetricsQueue"]: metricsQueue.queueName,
          ["UUID"]: uuid.getAttString("UUID"),
          ["PolicyIdentifier"]: policyId,
        },
      });
    });

    //=============================================================================================
    // cfn_nag suppress rules
    //=============================================================================================
    const cfn_nag_w89_w92 = [
      {
        id: "W89",
        reason:
          "Not a valid use case for Lambda functions to be deployed inside a VPC",
      },
      {
        id: "W92",
        reason: "Lambda ReservedConcurrentExecutions not needed",
      },
    ];

    const prRole = po.node.findChild("Resource") as CfnPolicy;
    prRole.cfnOptions.metadata = {
      cfn_nag: {
        rules_to_suppress: [
          {
            id: "W12",
            reason:
              "Resource * is required for IAM actions that do not support resource level permissions",
          },
        ],
      },
    };

    const mm = metricsManager.node.findChild("Resource") as CfnFunction;
    mm.cfnOptions.metadata = {
      cfn_nag: {
        rules_to_suppress: [
          {
            id: "W58",
            reason:
              "CloudWatch logs write permissions added with managed role AWSLambdaBasicExecutionRole",
          },
          {
            id: "W89",
            reason:
              "Not a valid use case for Lambda functions to be deployed inside a VPC",
          },
        ],
      },
    };

    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",
          },
          ...cfn_nag_w89_w92,
        ],
      },
    };

    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",
          },
          ...cfn_nag_w89_w92,
        ],
      },
    };

    //=============================================================================================
    // Output
    //=============================================================================================
    new CfnOutput(this, "UUID", {
      description: "UUID for deployment",
      value: uuid.getAttString("UUID"),
    });

    new CfnOutput(this, "Policy Table", {
      description: "Table for FMS policies metadata",
      value: table.tableName,
    });

    new CfnOutput(this, "Metrics SQS Queue", {
      description: "SQS queue for solution anonymous metric",
      value: metricsQueue.queueName,
    });

    new CfnOutput(this, "Compliance Reporting", {
      description: "Generate compliance reports on FMS policies",
      value: complianceGeneratorParam.valueAsString,
    });
  }