constructor()

in src/alb-fargate-svcs.ts [16:177]


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

    // create the access log bucket
    const accessLogBucket = new cdknag.AccessLogDeliveryBucket(this, 'AccessLogBucket').bucket;

    if (this.hasExternalLoadBalancer) {
      this.externalAlb = new elbv2.ApplicationLoadBalancer(this, 'ExternalAlb', {
        vpc: this.vpc,
        internetFacing: true,
      });
      this.externalAlb.logAccessLogs(accessLogBucket, `${id}-extalblog`);
    }

    if (this.hasInternalLoadBalancer) {
      this.internalAlb = new elbv2.ApplicationLoadBalancer(this, 'InternalAlb', {
        vpc: this.vpc,
        internetFacing: false,
      });
      this.internalAlb.logAccessLogs(accessLogBucket, `${id}-intalblog`);
    }

    props.tasks.forEach((t, index )=> {
      const defaultContainerName = t.task.defaultContainer?.containerName;

      // default scaling policy
      const scaling = this.service[index].autoScaleTaskCount({ maxCapacity: t.scalingPolicy?.maxCapacity ?? 10 });
      scaling.scaleOnCpuUtilization('CpuScaling', {
        targetUtilizationPercent: t.scalingPolicy?.targetCpuUtilization ?? 50,
      });

      if (t.accessibility != LoadBalancerAccessibility.INTERNAL_ONLY) {
        const exttg = new elbv2.ApplicationTargetGroup(this, `${defaultContainerName}ExtTG`, {
          protocol: elbv2.ApplicationProtocol.HTTP,
          vpc: this.vpc,
        });
        // listener for the external ALB
        new elbv2.ApplicationListener(this, `ExtAlbListener${t.listenerPort}`, {
          loadBalancer: this.externalAlb!,
          open: true,
          port: t.listenerPort,
          protocol: elbv2.ApplicationProtocol.HTTP,
          defaultTargetGroups: [exttg],
        });
        scaling.scaleOnRequestCount('RequestScaling', {
          requestsPerTarget: t.scalingPolicy?.requestPerTarget ?? 1000,
          targetGroup: exttg,
        });
        exttg.addTarget(this.service[index]);
      }

      if (t.accessibility != LoadBalancerAccessibility.EXTERNAL_ONLY) {
        const inttg = new elbv2.ApplicationTargetGroup(this, `${defaultContainerName}IntTG`, {
          protocol: elbv2.ApplicationProtocol.HTTP,
          vpc: this.vpc,
        });

        // listener for the internal ALB
        new elbv2.ApplicationListener(this, `IntAlbListener${t.listenerPort}`, {
          loadBalancer: this.internalAlb!,
          open: true,
          port: t.listenerPort,
          protocol: elbv2.ApplicationProtocol.HTTP,
          defaultTargetGroups: [inttg],
        });

        // extra scaling policy
        scaling.scaleOnRequestCount('RequestScaling2', {
          requestsPerTarget: t.scalingPolicy?.requestPerTarget ?? 1000,
          targetGroup: inttg,
        });
        inttg.addTarget(this.service[index]);
      }

    });

    // Route53
    const externalAlbRecordName = props.route53Ops?.externalElbRecordName ?? 'external';
    const internalAlbRecordName = props.route53Ops?.internalElbRecordName ?? 'internal';
    const zone = new route53.PrivateHostedZone(this, 'HostedZone', {
      zoneName: this.zoneName,
      vpc: this.vpc,
    });

    if (this.hasInternalLoadBalancer) {
      new route53.ARecord(this, 'InternalAlbAlias', {
        zone,
        recordName: internalAlbRecordName,
        target: route53.RecordTarget.fromAlias(new targets.LoadBalancerTarget(this.internalAlb!)),
      });
    }


    if (this.hasExternalLoadBalancer) {
      new route53.ARecord(this, 'ExternalAlbAlias', {
        zone,
        recordName: externalAlbRecordName,
        target: route53.RecordTarget.fromAlias(new targets.LoadBalancerTarget(this.externalAlb!)),
      });
    }

    if (this.hasExternalLoadBalancer) {
      new cdk.CfnOutput(this, 'ExternalEndpoint', { value: `http://${this.externalAlb!.loadBalancerDnsName}` });
      new cdk.CfnOutput(this, 'ExternalEndpointPrivate', { value: `http://${externalAlbRecordName}.${this.zoneName}` });
    }
    if (this.hasInternalLoadBalancer) {
      new cdk.CfnOutput(this, 'InternalEndpoint', { value: `http://${this.internalAlb!.loadBalancerDnsName}` });
      new cdk.CfnOutput(this, 'InternalEndpointPrivate', { value: `http://${internalAlbRecordName}.${this.zoneName}` });
    }

    // ensure the dependency
    const cp = this.node.tryFindChild('Cluster') as ecs.CfnClusterCapacityProviderAssociations;
    this.service.forEach(s => {
      s.node.addDependency(cp);
    });

    // add solution ID for the stack
    if (!cdk.Stack.of(this).templateOptions.description) {
      cdk.Stack.of(this).templateOptions.description = '(SO8030) - AWS CDK stack with serverless-container-constructs';
    }

    /**
     * suppress the cdk-nag rules
     */
    if (this.externalAlb) {
      let sg: ec2.CfnSecurityGroup;
      sg = this.externalAlb.node.tryFindChild('SecurityGroup') as ec2.CfnSecurityGroup;
      cdknag.Suppress.securityGroup(sg, [
        {
          id: 'AwsSolutions-EC23',
          reason: 'public ALB requires 0.0.0.0/0 inbound access',
        },
      ]);
    }
    if (this.internalAlb) {
      let sg: ec2.CfnSecurityGroup;
      sg = this.internalAlb.node.tryFindChild('SecurityGroup') as ec2.CfnSecurityGroup;
      cdknag.Suppress.securityGroup(sg, [
        {
          id: 'AwsSolutions-EC23',
          reason: 'internal ALB requires 0.0.0.0/0 inbound access',
        },
      ]);
    }
    props.tasks.forEach(t => {
      let cfnPolicy = t.task.executionRole?.node.tryFindChild('DefaultPolicy') as iam.Policy;
      cdknag.Suppress.iamPolicy(cfnPolicy, [
        {
          id: 'AwsSolutions-IAM5',
          reason: 'ecr:GetAuthorizationToken requires wildcard resource',
        },
      ]);
      cfnPolicy = t.task.taskRole?.node.tryFindChild('DefaultPolicy') as iam.Policy;
      cdknag.Suppress.iamPolicy(cfnPolicy, [
        {
          id: 'AwsSolutions-IAM5',
          reason: 'task role with ECS exec support requires wildcard resource for ssmmessages. see https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-exec.html',
        },
      ]);
    });

  }