constructor()

in deployment/custom-deployment/lib/smart-product-owner-web-app.ts [31:192]


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

    //=============================================================================================
    // Resources
    //=============================================================================================
    // S3 Bucket
    const smartProductWebsiteBucket = new s3.Bucket(this, 'WebsiteBucket', {
      websiteErrorDocument: 'index.html',
      websiteIndexDocument: 'index.html'
    })

    // Helper Function Policy
    const helperS3Policy = new iam.Policy(this, 'HelperS3Policy', {
      statements: [
        new iam.PolicyStatement({
          actions: ['s3:PutObject'],
          resources: [`${smartProductWebsiteBucket.bucketArn}/*`]
        }),
        new iam.PolicyStatement({
          actions: [
            's3:GetObject'
          ],
          resources: [`arn:aws:s3:::${process.env.BUILD_OUTPUT_BUCKET}/*`]
        })
      ]
    })
    const helperS3PolicyResource = helperS3Policy.node.findChild('Resource') as iam.CfnPolicy;
    helperS3PolicyResource.cfnOptions.metadata = {
      cfn_nag: {
        rules_to_suppress: [{
          id: 'W12',
          reason: `The * resource allows ${props.helperFunctionRole.roleName} to get and put objects to S3.`
        }]
      }
    }
    helperS3Policy.attachToRole(props.helperFunctionRole);

    const _copyS3Assets = new cfn.CustomResource(this, 'CopyS3Assets', {
      provider: props.helperFunction,
      resourceType: 'Custom::CopyS3Assets',
      properties: {
        Region: `${cdk.Aws.REGION}`,
        ManifestKey: `smart-product-solution/${props.solutionVersion}/site-manifest.json`,
        SourceS3Bucket: `${process.env.BUILD_OUTPUT_BUCKET}`,
        SourceS3key: `smart-product-solution/${props.solutionVersion}/console`,
        DestS3Bucket: smartProductWebsiteBucket.bucketName,
      }
    })
    _copyS3Assets.node.addDependency(helperS3Policy.node.findChild('Resource') as cdk.Resource)

    const _putConfig = new cfn.CustomResource(this, 'UploadWebConfig', {
      provider: props.helperFunction,
      resourceType: 'Custom::PutConfigFile',
      properties: {
        Region: `${cdk.Aws.REGION}`,
        CustomAction: 'putConfigFile',
        DestS3Bucket: smartProductWebsiteBucket.bucketName,
        DestS3key: 'assets/smart_product_config.js',
        ConfigItem: {
          aws_user_pools_id: props.userPool.userPoolId,
          aws_user_pools_web_client_id: props.userPoolClient.userPoolClientId,
          endpoint: props.apiEndpoint,
          region: `${cdk.Aws.REGION}`
        }
      }
    })
    _putConfig.node.addDependency(_copyS3Assets.node.findChild('Default') as cdk.Resource)
    _putConfig.node.addDependency(helperS3Policy.node.findChild('Resource') as cdk.Resource)

    const consoleOriginAccessIdentity = new cloudfront.OriginAccessIdentity(this, 'ConsoleOriginAccessIdentity', {

      comment: `access-identity-${smartProductWebsiteBucket.bucketName}`

    })

    const consoleDistribution = new cloudfront.CloudFrontWebDistribution(this, 'ConsoleDistribution', {
      comment: 'Website distribution for smart product console',
      originConfigs: [{
        s3OriginSource: {
          s3BucketSource: smartProductWebsiteBucket,
          originAccessIdentity: consoleOriginAccessIdentity
        },
        behaviors: [{
          isDefaultBehavior: true,
          allowedMethods: cloudfront.CloudFrontAllowedMethods.GET_HEAD_OPTIONS,
          cachedMethods: cloudfront.CloudFrontAllowedCachedMethods.GET_HEAD_OPTIONS,
          defaultTtl: cdk.Duration.seconds(0),
          maxTtl: cdk.Duration.seconds(0),
          minTtl: cdk.Duration.seconds(0)
        }]
      }],
      viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
      errorConfigurations: [
        {
          errorCode: 404,
          responseCode: 200,
          responsePagePath: '/index.html'
        },
        {
          errorCode: 403,
          responseCode: 200,
          responsePagePath: '/index.html'
        }
      ],
      enableIpV6: true,
      httpVersion: cloudfront.HttpVersion.HTTP2,
      defaultRootObject: 'index.html',
      priceClass: cloudfront.PriceClass.PRICE_CLASS_ALL
    })

    //=============================================================================================
    // Permissions and Policies
    //=============================================================================================
    // S3 Bucket Policy
    smartProductWebsiteBucket.addToResourcePolicy(new iam.PolicyStatement({
      actions: ['s3:GetObject'],
      effect: iam.Effect.ALLOW,
      resources: [`${smartProductWebsiteBucket.bucketArn}/*`],
      principals: [new iam.CanonicalUserPrincipal(consoleOriginAccessIdentity.cloudFrontOriginAccessIdentityS3CanonicalUserId)]
    }))

    // CFN_NAG Annotations
    const websiteBucketResource = smartProductWebsiteBucket.node.findChild('Resource') as s3.CfnBucket;
    websiteBucketResource.cfnOptions.metadata = {
      cfn_nag: {
        rules_to_suppress: [
          {
            id: 'W35',
            reason: `SmartProductWebsiteBucket validated and does not require access logging to be configured.`
          },
          {
            id: 'W41',
            reason: 'SmartProductWebsiteBucket does not contain sensitive data so encryption is not needed.'
          }
        ]
      }
    }

    const websiteDistributionResource = consoleDistribution.node.findChild('CFDistribution') as cloudfront.CfnDistribution;
    websiteDistributionResource.cfnOptions.metadata = {
      cfn_nag: {
        rules_to_suppress: [{
          id: 'W10',
          reason: `ConsoleDistribution validated and does not require access logging to be configured.`
        }]
      }
    }

    //=============================================================================================
    // Output
    //=============================================================================================
    new cdk.CfnOutput(this, 'OwnerWebAppConsole', {
      description: 'Smart Product Owner Web App Console URL',
      value: `https://${consoleDistribution.domainName}`
    })

    new cdk.CfnOutput(this, 'BucketName', {
      description: 'Smart Product Web Site Bucket',
      value: smartProductWebsiteBucket.bucketName
    })
  }