private alb()

in packages/amplify-category-api/src/provider-utils/awscloudformation/ecs-alb-stack.ts [42:298]


  private alb() {
    const {
      domainName,
      hostedZoneId,
      exposedContainer: { name: containerName, port },
      restrictAccess,
    } = this.ecsProps;

    const sharedSecretHeaderName = 'x-cf-token';
    const sharedSecretHeader = uuid();

    const userPoolDomain = this.userPoolDomain;

    const vpcId = this.vpcId;
    const subnets = <string[]>this.subnets;

    const userPoolArn = cdk.Fn.join('', [
      'arn:',
      cdk.Aws.PARTITION,
      ':cognito-idp:',
      cdk.Aws.REGION,
      ':',
      cdk.Aws.ACCOUNT_ID,
      ':userpool/',
      this.userPoolId,
    ]);

    const [distributionDomainName, , domainNameSuffix] = domainName.match(/([^\.]+)\.(.*)/);
    const lbPrefix = `lb-${this.envName}`;
    const albDomainName = `${lbPrefix}.${domainNameSuffix}`;
    const wildcardDomainName = `*.${domainNameSuffix}`;

    const wildcardCertificate = new acm.CfnCertificate(this, 'Certificate', {
      domainName: wildcardDomainName,
      validationMethod: hostedZoneId ? acm.ValidationMethod.DNS : acm.ValidationMethod.EMAIL,
      domainValidationOptions: [
        {
          domainName: wildcardDomainName,
          validationDomain: hostedZoneId === undefined ? domainNameSuffix : undefined,
          hostedZoneId,
        },
      ],
    });

    const userPoolClient = restrictAccess
      ? new cognito.CfnUserPoolClient(this, 'UserPoolClient', {
          userPoolId: this.userPoolId,
          allowedOAuthFlows: [
            // 'implicit',
            'code',
          ],
          allowedOAuthFlowsUserPoolClient: true,
          allowedOAuthScopes: ['profile', 'phone', 'email', 'openid', 'aws.cognito.signin.user.admin'],
          generateSecret: true,
          supportedIdentityProviders: ['COGNITO'],
          callbackUrLs: [`https://${distributionDomainName}/oauth2/idpresponse`],
          logoutUrLs: [`https://${distributionDomainName}/oauth2/idpresponse`],
        })
      : undefined;

    const targetGroup = new elb2.CfnTargetGroup(this, 'TargetGroup', {
      healthCheckIntervalSeconds: cdk.Duration.seconds(90).toSeconds(),
      healthCheckPath: '/',
      healthCheckTimeoutSeconds: cdk.Duration.minutes(1).toSeconds(),
      healthyThresholdCount: 2,
      port,
      protocol: elb2.Protocol.HTTP,
      targetType: elb2.TargetType.IP,
      unhealthyThresholdCount: 2,
      vpcId,
    });

    const albSecurityGroup = new ec2.CfnSecurityGroup(this, 'AlbSecurityGroup', {
      vpcId,
      groupDescription: 'ALB Security Group',
      securityGroupEgress: [
        {
          description: 'Allow all outbound traffic by default',
          ipProtocol: '-1',
          cidrIp: '0.0.0.0/0',
        },
      ],
      securityGroupIngress: [
        {
          description: 'Allow from anyone on port 443',
          ipProtocol: ec2.Protocol.TCP,
          cidrIp: '0.0.0.0/0',
          fromPort: 443,
          toPort: 443,
        },
      ],
    });

    const loadBalancer = new elb2.CfnLoadBalancer(this, 'LoadBalancer', {
      type: 'application',
      securityGroups: [albSecurityGroup.attrGroupId],
      loadBalancerAttributes: [
        {
          key: 'deletion_protection.enabled',
          value: 'false',
        },
      ],
      scheme: 'internet-facing',
      subnets,
    });

    (<ecs.CfnService.LoadBalancerProperty[]>this.ecsService.loadBalancers) = [
      {
        containerName,
        containerPort: port,
        targetGroupArn: targetGroup.ref,
      },
    ];
    (<ec2.CfnSecurityGroup.IngressProperty[]>this.ecsServiceSecurityGroup.securityGroupIngress).push({
      ipProtocol: ec2.Protocol.TCP,
      fromPort: port,
      toPort: port,
      sourceSecurityGroupId: albSecurityGroup.attrGroupId,
    });

    const listener = new elb2.CfnListener(this, 'AlbListener', {
      defaultActions: [
        {
          fixedResponseConfig: {
            statusCode: '403',
          },
          type: 'fixed-response',
        },
      ],
      loadBalancerArn: loadBalancer.ref,
      port: 443,
      protocol: elb2.Protocol.HTTPS,
      certificates: [{ certificateArn: wildcardCertificate.ref }],
    });

    this.ecsService.addDependsOn(listener);

    let actionsOrderCounter = 1;
    const listenerRule = new elb2.CfnListenerRule(this, 'AlbListenerRule', {
      priority: 1,
      listenerArn: listener.ref,
      actions: [].concat(
        restrictAccess
          ? {
              order: actionsOrderCounter++,
              type: 'authenticate-cognito',
              authenticateCognitoConfig: {
                userPoolArn,
                userPoolClientId: userPoolClient.ref,
                userPoolDomain,
              },
            }
          : undefined,
        {
          order: actionsOrderCounter++,
          type: 'forward',
          targetGroupArn: targetGroup.ref,
        },
      ),
      conditions: [
        {
          field: 'host-header',
          hostHeaderConfig: {
            values: [distributionDomainName],
          },
        },
        {
          field: 'http-header',
          httpHeaderConfig: {
            httpHeaderName: sharedSecretHeaderName,
            values: [sharedSecretHeader],
          },
        },
      ],
    });

    this.ecsService.addDependsOn(listenerRule);

    const originId = `${loadBalancer.logicalId}-origin`;

    const distribution = new cloudfront.CfnDistribution(this, 'Distribution', {
      distributionConfig: {
        enabled: true,
        httpVersion: 'http2',
        ipv6Enabled: true,
        aliases: [distributionDomainName],
        defaultCacheBehavior: {
          forwardedValues: {
            cookies: { forward: 'all' },
            headers: ['*'],
            queryString: true,
          },
          targetOriginId: originId,
          viewerProtocolPolicy: 'redirect-to-https',
        },
        origins: [
          {
            customOriginConfig: {
              originProtocolPolicy: 'https-only',
            },
            domainName: albDomainName,
            id: originId,
            originCustomHeaders: [
              {
                headerName: sharedSecretHeaderName,
                headerValue: sharedSecretHeader,
              },
            ],
          },
        ],
        viewerCertificate: {
          acmCertificateArn: wildcardCertificate.ref,
          minimumProtocolVersion: 'TLSv1.2_2019',
          sslSupportMethod: 'sni-only',
        },
      },
    });

    if (hostedZoneId) {
      new route53.CfnRecordSetGroup(this, 'RecordSetGroup', {
        hostedZoneId,
        recordSets: [
          {
            name: albDomainName,
            type: route53.RecordType.A,
            aliasTarget: {
              hostedZoneId: loadBalancer.attrCanonicalHostedZoneId,
              dnsName: loadBalancer.attrDnsName,
            },
          },
          {
            name: distributionDomainName,
            type: route53.RecordType.A,
            aliasTarget: {
              hostedZoneId: route53targets.CloudFrontTarget.CLOUDFRONT_ZONE_ID,
              dnsName: distribution.attrDomainName,
            },
          },
        ],
      });
    }

    new cdk.CfnOutput(this, 'PipelineUrl', {
      value: cdk.Fn.join('', [
        'https://',
        cdk.Aws.REGION,
        '.console.aws.amazon.com/codesuite/codepipeline/pipelines/',
        this.getPipelineName(),
        '/view',
      ]),
    });

    new cdk.CfnOutput(this, 'LoadBalancerAliasDomainName', { value: loadBalancer.attrDnsName });
    new cdk.CfnOutput(this, 'LoadBalancerCnameDomainName', { value: albDomainName });
    new cdk.CfnOutput(this, 'CloudfrontDistributionAliasDomainName', { value: distribution.attrDomainName });
    new cdk.CfnOutput(this, 'CloudfrontDistributionCnameDomainName', { value: distributionDomainName });
  }