Search code examples
amazon-web-servicesaws-cloudformationamazon-cloudfrontaws-cdkinfrastructure-as-code

Configuring CloudFront distribution with S3 Origin and Custom Origin (ELB) using AWS CDK


I have an app that uses Django and Vue.js. Currently the API is served on api.mydomain.com that sends traffic to an Application Load Balancer which routes to Fargate services and the Vue.js static site is served on mydomain.com which sends traffic to a CloudFront distribution in front of an S3 bucket where the site's static assets are stored.

I would like to serve the API on mydomain.com/api/*, without using a subdomain, and continue to serve the static site on mydomain.com.

The ALB is working just fine, I can go to the ALB's auto-generated AWS URL and get the correct response from my Fargate service.

Here is the CDK code for my CloudFront distribution:

import os

from aws_cdk import (
    aws_certificatemanager as acm,
    aws_s3 as s3,
    aws_cloudfront as cloudfront,
    aws_route53 as route53,
    aws_iam as iam,
    aws_route53_targets as targets,
    core,
)


class StaticSite(core.Construct):
    def __init__(
        self,
        scope: core.Construct,
        id: str,
        hosted_zone: route53.IHostedZone,
        certificate: acm.ICertificate,
        alb: str,
        **kwargs,
    ) -> None:
        super().__init__(scope, id, **kwargs)

        self.static_site_bucket = s3.Bucket(
            self,
            "StaticSiteBucket",
            access_control=s3.BucketAccessControl.PUBLIC_READ,
            bucket_name=os.environ.get("DOMAIN_NAME", "mysite.com"),
            removal_policy=core.RemovalPolicy.DESTROY,
        )

        self.policy_statement = iam.PolicyStatement(
            actions=["s3:GetObject"],
            resources=[f"{self.static_site_bucket.bucket_arn}/*"],
        )

        self.policy_statement.add_any_principal()

        self.static_site_policy_document = iam.PolicyDocument(
            statements=[self.policy_statement]
        )

        self.static_site_bucket.add_to_resource_policy(self.policy_statement)

        self.distribution = cloudfront.CloudFrontWebDistribution(
            self,
            "CloudFrontDistribution",
            origin_configs=[
                cloudfront.SourceConfiguration(
                    s3_origin_source=cloudfront.S3OriginConfig(
                        s3_bucket_source=self.static_site_bucket
                    ),
                    behaviors=[cloudfront.Behavior(is_default_behavior=True)],
                ),
                cloudfront.SourceConfiguration(
                    # origin_path="/test",
                    custom_origin_source=cloudfront.CustomOriginConfig(
                        domain_name=alb,
                    ),
                    behaviors=[
                        cloudfront.Behavior(
                            path_pattern="/test",
                            # forwarded_values={"headers": ["*"], "query_string": True},
                        )
                    ],
                ),
            ],
            alias_configuration=cloudfront.AliasConfiguration(
                acm_cert_ref=certificate.certificate_arn,
                names=[
                    os.environ.get("DOMAIN_NAME", "mysite.com"),
                    f"*.{os.environ.get('DOMAIN_NAME', 'mysite.com')}",
                ],
            ),
            error_configurations=[
                {
                    "errorCode": 403,
                    "errorCachingMinTtl": 0,
                    "responseCode": 200,
                    "responsePagePath": "/index.html",
                },
                {
                    "errorCode": 404,
                    "errorCachingMinTtl": 0,
                    "responseCode": 200,
                    "responsePagePath": "/index.html",
                },
            ],
        )

        route53.ARecord(
            self,
            "AliasRecord1",
            target=route53.AddressRecordTarget.from_alias(
                targets.CloudFrontTarget(self.distribution)
            ),
            zone=hosted_zone.hosted_zone,
            # don't forget the '.' at the end of the record name!
            record_name=f"{os.environ.get('DOMAIN_NAME', 'mysite.com')}.",
        )

Here is the code for my Application Load Balancer:

from aws_cdk import (
    aws_iam as iam,
    aws_ec2 as ec2,
    aws_route53 as route53,
    aws_certificatemanager as acm,
    aws_elasticloadbalancingv2 as elbv2,
    core,
)


class ApplicationLoadBalancer(core.Construct):
    def __init__(
        self,
        scope: core.Construct,
        id: str,
        hosted_zone: route53.IHostedZone,
        certificate: acm.ICertificate,
        vpc: ec2.IVpc,
        **kwargs
    ) -> None:
        super().__init__(scope, id, **kwargs)

        self.alb = elbv2.ApplicationLoadBalancer(
            self, "ALB", internet_facing=True, vpc=vpc
        )

        self.alb.connections.allow_from_any_ipv4(
            ec2.Port.tcp(80), "Internet access ALB 80"
        )

        self.alb.connections.allow_from_any_ipv4(
            ec2.Port.tcp(443), "Internet access ALB 443"
        )

        # redirect_listener = elbv2.CfnListener(
        #     self,
        #     "RedirectListener",
        #     protocol="HTTP",
        #     port=80,
        #     load_balancer_arn=self.alb.load_balancer_arn,
        #     default_actions=[
        #         {
        #             "type": "redirect",
        #             "redirectConfig": {
        #                 "host": "#{host}",
        #                 "path": "/#{path}",
        #                 "port": "443",
        #                 "protocol": "HTTPS",
        #                 "query": "#{query}",
        #                 "statusCode": "HTTP_301",
        #             },
        #         }
        #     ],
        # )

        # I think this part is incorrect
        self.redirect_response = elbv2.RedirectResponse(
            status_code="HTTP_301",
            host="#{host}",
            path="/#{path}",
            port="80",
            protocol="HTTPS",
            query="#{query}",
        )

        self.https_listener = elbv2.ApplicationListener(
            self,
            "HTTPSListener",
            load_balancer=self.alb,
            port=443,
            certificates=[
                elbv2.ListenerCertificate(certificate.certificate_arn)
            ],
        )

        self.default_target_group = elbv2.ApplicationTargetGroup(
            self,
            "DefaultTargetGroup",
            port=80,
            protocol=elbv2.ApplicationProtocol.HTTP,
            vpc=vpc,
        )

        self.https_listener.add_target_groups(
            "DefaultTargetGroup", target_groups=[self.default_target_group]
        )

I'm not sure how SSL termination should work with CloudFront and ALB. Should the ALB only accept connections from the CloudFront distribution?

Here is the output of cdk synth:

Resources:
  SiteCert6025247C:
    Type: AWS::CertificateManager::Certificate
    Properties:
      DomainName: mydomain.com
      DomainValidationOptions:
        - DomainName: mydomain.com
          ValidationDomain: mydomain.com
        - DomainName: "*.mydomain.com"
          ValidationDomain: mydomain.com
      SubjectAlternativeNames:
        - "*.mydomain.com"
      ValidationMethod: DNS
    Metadata:
      aws:cdk:path: awscdk/SiteCert/Resource
  VpcC3027511:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: awscdk/Vpc/Vpc
    Metadata:
      aws:cdk:path: awscdk/Vpc/Vpc/Resource
  VpcPublicSubnet1Subnet8E8DEDC0:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.0.0/24
      VpcId:
        Ref: VpcC3027511
      AvailabilityZone:
        Fn::Select:
          - 0
          - Fn::GetAZs: ""
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: awscdk/Vpc/Vpc/PublicSubnet1
        - Key: aws-cdk:subnet-name
          Value: Public
        - Key: aws-cdk:subnet-type
          Value: Public
    Metadata:
      aws:cdk:path: awscdk/Vpc/Vpc/PublicSubnet1/Subnet
  VpcPublicSubnet1RouteTable431DD755:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: VpcC3027511
      Tags:
        - Key: Name
          Value: awscdk/Vpc/Vpc/PublicSubnet1
    Metadata:
      aws:cdk:path: awscdk/Vpc/Vpc/PublicSubnet1/RouteTable
  VpcPublicSubnet1RouteTableAssociationBBCB7AA1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: VpcPublicSubnet1RouteTable431DD755
      SubnetId:
        Ref: VpcPublicSubnet1Subnet8E8DEDC0
    Metadata:
      aws:cdk:path: awscdk/Vpc/Vpc/PublicSubnet1/RouteTableAssociation
  VpcPublicSubnet1DefaultRoute0F5C6C43:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId:
        Ref: VpcPublicSubnet1RouteTable431DD755
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId:
        Ref: VpcIGW488B0FEB
    DependsOn:
      - VpcVPCGW42EC8516
    Metadata:
      aws:cdk:path: awscdk/Vpc/Vpc/PublicSubnet1/DefaultRoute
  VpcPublicSubnet2SubnetA811849C:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.1.0/24
      VpcId:
        Ref: VpcC3027511
      AvailabilityZone:
        Fn::Select:
          - 1
          - Fn::GetAZs: ""
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: awscdk/Vpc/Vpc/PublicSubnet2
        - Key: aws-cdk:subnet-name
          Value: Public
        - Key: aws-cdk:subnet-type
          Value: Public
    Metadata:
      aws:cdk:path: awscdk/Vpc/Vpc/PublicSubnet2/Subnet
  VpcPublicSubnet2RouteTable77FB35FC:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: VpcC3027511
      Tags:
        - Key: Name
          Value: awscdk/Vpc/Vpc/PublicSubnet2
    Metadata:
      aws:cdk:path: awscdk/Vpc/Vpc/PublicSubnet2/RouteTable
  VpcPublicSubnet2RouteTableAssociation3AFE92E6:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: VpcPublicSubnet2RouteTable77FB35FC
      SubnetId:
        Ref: VpcPublicSubnet2SubnetA811849C
    Metadata:
      aws:cdk:path: awscdk/Vpc/Vpc/PublicSubnet2/RouteTableAssociation
  VpcPublicSubnet2DefaultRouteD629179A:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId:
        Ref: VpcPublicSubnet2RouteTable77FB35FC
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId:
        Ref: VpcIGW488B0FEB
    DependsOn:
      - VpcVPCGW42EC8516
    Metadata:
      aws:cdk:path: awscdk/Vpc/Vpc/PublicSubnet2/DefaultRoute
  VpcIsolatedSubnet1SubnetDC3C6AF8:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.2.0/24
      VpcId:
        Ref: VpcC3027511
      AvailabilityZone:
        Fn::Select:
          - 0
          - Fn::GetAZs: ""
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: awscdk/Vpc/Vpc/IsolatedSubnet1
        - Key: aws-cdk:subnet-name
          Value: Isolated
        - Key: aws-cdk:subnet-type
          Value: Isolated
    Metadata:
      aws:cdk:path: awscdk/Vpc/Vpc/IsolatedSubnet1/Subnet
  VpcIsolatedSubnet1RouteTableF057227C:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: VpcC3027511
      Tags:
        - Key: Name
          Value: awscdk/Vpc/Vpc/IsolatedSubnet1
    Metadata:
      aws:cdk:path: awscdk/Vpc/Vpc/IsolatedSubnet1/RouteTable
  VpcIsolatedSubnet1RouteTableAssociation0FC379C3:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: VpcIsolatedSubnet1RouteTableF057227C
      SubnetId:
        Ref: VpcIsolatedSubnet1SubnetDC3C6AF8
    Metadata:
      aws:cdk:path: awscdk/Vpc/Vpc/IsolatedSubnet1/RouteTableAssociation
  VpcIsolatedSubnet2SubnetB479B99C:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.3.0/24
      VpcId:
        Ref: VpcC3027511
      AvailabilityZone:
        Fn::Select:
          - 1
          - Fn::GetAZs: ""
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: awscdk/Vpc/Vpc/IsolatedSubnet2
        - Key: aws-cdk:subnet-name
          Value: Isolated
        - Key: aws-cdk:subnet-type
          Value: Isolated
    Metadata:
      aws:cdk:path: awscdk/Vpc/Vpc/IsolatedSubnet2/Subnet
  VpcIsolatedSubnet2RouteTableBAB510EF:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: VpcC3027511
      Tags:
        - Key: Name
          Value: awscdk/Vpc/Vpc/IsolatedSubnet2
    Metadata:
      aws:cdk:path: awscdk/Vpc/Vpc/IsolatedSubnet2/RouteTable
  VpcIsolatedSubnet2RouteTableAssociation8E8989F5:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: VpcIsolatedSubnet2RouteTableBAB510EF
      SubnetId:
        Ref: VpcIsolatedSubnet2SubnetB479B99C
    Metadata:
      aws:cdk:path: awscdk/Vpc/Vpc/IsolatedSubnet2/RouteTableAssociation
  VpcIGW488B0FEB:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: awscdk/Vpc/Vpc
    Metadata:
      aws:cdk:path: awscdk/Vpc/Vpc/IGW
  VpcVPCGW42EC8516:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId:
        Ref: VpcC3027511
      InternetGatewayId:
        Ref: VpcIGW488B0FEB
    Metadata:
      aws:cdk:path: awscdk/Vpc/Vpc/VPCGW
  ApplicationLoadBalancerALBE88818A8:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Scheme: internet-facing
      SecurityGroups:
        - Fn::GetAtt:
            - ApplicationLoadBalancerALBSecurityGroup0D676F12
            - GroupId
      Subnets:
        - Ref: VpcPublicSubnet1Subnet8E8DEDC0
        - Ref: VpcPublicSubnet2SubnetA811849C
      Type: application
    DependsOn:
      - VpcPublicSubnet1DefaultRoute0F5C6C43
      - VpcPublicSubnet2DefaultRouteD629179A
    Metadata:
      aws:cdk:path: awscdk/ApplicationLoadBalancer/ALB/Resource
  ApplicationLoadBalancerALBSecurityGroup0D676F12:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Automatically created Security Group for ELB awscdkApplicationLoadBalancerALB81FD6B77
      SecurityGroupIngress:
        - CidrIp: 0.0.0.0/0
          Description: Internet access ALB 80
          FromPort: 80
          IpProtocol: tcp
          ToPort: 80
        - CidrIp: 0.0.0.0/0
          Description: Internet access ALB 443
          FromPort: 443
          IpProtocol: tcp
          ToPort: 443
      VpcId:
        Ref: VpcC3027511
    Metadata:
      aws:cdk:path: awscdk/ApplicationLoadBalancer/ALB/SecurityGroup/Resource
  ApplicationLoadBalancerALBSecurityGrouptoawscdkBackendBackendServiceSecurityGroupD69D8DD280A0C3942C:
    Type: AWS::EC2::SecurityGroupEgress
    Properties:
      GroupId:
        Fn::GetAtt:
          - ApplicationLoadBalancerALBSecurityGroup0D676F12
          - GroupId
      IpProtocol: tcp
      Description: Load balancer to target
      DestinationSecurityGroupId:
        Fn::GetAtt:
          - BackendBackendServiceSecurityGroupA039445A
          - GroupId
      FromPort: 80
      ToPort: 80
    Metadata:
      aws:cdk:path: awscdk/ApplicationLoadBalancer/ALB/SecurityGroup/to awscdkBackendBackendServiceSecurityGroupD69D8DD2:80
  ApplicationLoadBalancerHTTPSListenerC96D73F5:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - TargetGroupArn:
            Ref: ApplicationLoadBalancerDefaultTargetGroupF1B3D7D1
          Type: forward
      LoadBalancerArn:
        Ref: ApplicationLoadBalancerALBE88818A8
      Port: 443
      Protocol: HTTPS
      Certificates:
        - CertificateArn:
            Ref: SiteCert6025247C
    Metadata:
      aws:cdk:path: awscdk/ApplicationLoadBalancer/HTTPSListener/Resource
  ApplicationLoadBalancerHTTPSListenerBackendTargetGroupA4042837:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Port: 80
      Protocol: HTTP
      TargetType: ip
      VpcId:
        Ref: VpcC3027511
    Metadata:
      aws:cdk:path: awscdk/ApplicationLoadBalancer/HTTPSListener/BackendTargetGroup/Resource
  ApplicationLoadBalancerHTTPSListenerBackendTargetRuleA3A291E2:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Actions:
        - TargetGroupArn:
            Ref: ApplicationLoadBalancerHTTPSListenerBackendTargetGroupA4042837
          Type: forward
      Conditions:
        - Field: path-pattern
          Values:
            - "*"
      ListenerArn:
        Ref: ApplicationLoadBalancerHTTPSListenerC96D73F5
      Priority: 1
    Metadata:
      aws:cdk:path: awscdk/ApplicationLoadBalancer/HTTPSListener/BackendTargetRule/Resource
  ApplicationLoadBalancerDefaultTargetGroupF1B3D7D1:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Port: 80
      Protocol: HTTP
      VpcId:
        Ref: VpcC3027511
    Metadata:
      aws:cdk:path: awscdk/ApplicationLoadBalancer/DefaultTargetGroup/Resource
  StaticSiteStaticSiteBucket442CE34F:
    Type: AWS::S3::Bucket
    Properties:
      AccessControl: PublicRead
      BucketName: mydomain.com
    UpdateReplacePolicy: Delete
    DeletionPolicy: Delete
    Metadata:
      aws:cdk:path: awscdk/StaticSite/StaticSiteBucket/Resource
  StaticSiteStaticSiteBucketPolicyC8E62485:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket:
        Ref: StaticSiteStaticSiteBucket442CE34F
      PolicyDocument:
        Statement:
          - Action: s3:GetObject
            Effect: Allow
            Principal: "*"
            Resource:
              Fn::Join:
                - ""
                - - Fn::GetAtt:
                      - StaticSiteStaticSiteBucket442CE34F
                      - Arn
                  - /*
        Version: "2012-10-17"
    Metadata:
      aws:cdk:path: awscdk/StaticSite/StaticSiteBucket/Policy/Resource
  StaticSiteCloudFrontDistributionCFDistributionA70E78CD:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Aliases:
          - mydomain.com
          - "*.mydomain.com"
        CacheBehaviors:
          - AllowedMethods:
              - GET
              - HEAD
            CachedMethods:
              - GET
              - HEAD
            Compress: true
            ForwardedValues:
              Headers:
                - "*"
              QueryString: true
            PathPattern: /test
            TargetOriginId: origin2
            ViewerProtocolPolicy: redirect-to-https
        CustomErrorResponses:
          - ErrorCachingMinTTL: 0
            ErrorCode: 403
            ResponseCode: 200
            ResponsePagePath: /index.html
          - ErrorCachingMinTTL: 0
            ErrorCode: 404
            ResponseCode: 200
            ResponsePagePath: /index.html
        DefaultCacheBehavior:
          AllowedMethods:
            - GET
            - HEAD
          CachedMethods:
            - GET
            - HEAD
          Compress: true
          ForwardedValues:
            Cookies:
              Forward: none
            QueryString: false
          TargetOriginId: origin1
          ViewerProtocolPolicy: redirect-to-https
        DefaultRootObject: index.html
        Enabled: true
        HttpVersion: http2
        IPV6Enabled: true
        Origins:
          - DomainName:
              Fn::GetAtt:
                - StaticSiteStaticSiteBucket442CE34F
                - RegionalDomainName
            Id: origin1
            S3OriginConfig: {}
          - CustomOriginConfig:
              HTTPPort: 80
              HTTPSPort: 443
              OriginKeepaliveTimeout: 5
              OriginProtocolPolicy: https-only
              OriginReadTimeout: 30
              OriginSSLProtocols:
                - TLSv1.2
            DomainName:
              Fn::GetAtt:
                - ApplicationLoadBalancerALBE88818A8
                - DNSName
            Id: origin2
        PriceClass: PriceClass_100
        ViewerCertificate:
          AcmCertificateArn:
            Ref: SiteCert6025247C
          SslSupportMethod: sni-only
    Metadata:
      aws:cdk:path: awscdk/StaticSite/CloudFrontDistribution/CFDistribution
  StaticSiteAliasRecord4F27A661:
    Type: AWS::Route53::RecordSet
    Properties:
      Name: "*.mydomain.com."
      Type: A
      AliasTarget:
        DNSName:
          Fn::GetAtt:
            - StaticSiteCloudFrontDistributionCFDistributionA70E78CD
            - DomainName
        HostedZoneId: Z2FDTNDATAQYW2
      HostedZoneId: Z1EJVU8DMBV0XG
    Metadata:
      aws:cdk:path: awscdk/StaticSite/AliasRecord/Resource
  StaticSiteAliasRecord1B2F1F710:
    Type: AWS::Route53::RecordSet
    Properties:
      Name: mydomain.com.
      Type: A
      AliasTarget:
        DNSName:
          Fn::GetAtt:
            - StaticSiteCloudFrontDistributionCFDistributionA70E78CD
            - DomainName
        HostedZoneId: Z2FDTNDATAQYW2
      HostedZoneId: Z1EJVU8DMBV0XG
    Metadata:
      aws:cdk:path: awscdk/StaticSite/AliasRecord1/Resource
  ElasticContainerRepo2908E7AA:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: mydomain.com/backend
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
    Metadata:
      aws:cdk:path: awscdk/ElasticContainerRepo/Resource
  EcsEcsCluster51C39CA0:
    Type: AWS::ECS::Cluster
    Metadata:
      aws:cdk:path: awscdk/Ecs/EcsCluster/Resource
  BackendBackendTaskTaskRoleD7BBECAE:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
        Version: "2012-10-17"
    Metadata:
      aws:cdk:path: awscdk/Backend/BackendTask/TaskRole/Resource
  BackendBackendTask22B2DD1D:
    Type: AWS::ECS::TaskDefinition
    Properties:
      ContainerDefinitions:
        - Essential: true
          Image: nginx:alpine
          Name: nginx
          PortMappings:
            - ContainerPort: 80
              Protocol: tcp
      Cpu: "256"
      Family: awscdkBackendBackendTask594F440A
      Memory: "512"
      NetworkMode: awsvpc
      RequiresCompatibilities:
        - FARGATE
      TaskRoleArn:
        Fn::GetAtt:
          - BackendBackendTaskTaskRoleD7BBECAE
          - Arn
    Metadata:
      aws:cdk:path: awscdk/Backend/BackendTask/Resource
  BackendBackendService9DB18AD9:
    Type: AWS::ECS::Service
    Properties:
      Cluster:
        Ref: EcsEcsCluster51C39CA0
      DeploymentConfiguration:
        MaximumPercent: 200
        MinimumHealthyPercent: 50
      DesiredCount: 1
      EnableECSManagedTags: false
      HealthCheckGracePeriodSeconds: 60
      LaunchType: FARGATE
      LoadBalancers:
        - ContainerName: nginx
          ContainerPort: 80
          TargetGroupArn:
            Ref: ApplicationLoadBalancerHTTPSListenerBackendTargetGroupA4042837
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: ENABLED
          SecurityGroups:
            - Fn::GetAtt:
                - BackendBackendServiceSecurityGroupA039445A
                - GroupId
          Subnets:
            - Ref: VpcPublicSubnet1Subnet8E8DEDC0
            - Ref: VpcPublicSubnet2SubnetA811849C
      TaskDefinition:
        Ref: BackendBackendTask22B2DD1D
    DependsOn:
      - ApplicationLoadBalancerHTTPSListenerBackendTargetRuleA3A291E2
    Metadata:
      aws:cdk:path: awscdk/Backend/BackendService/Service
  BackendBackendServiceSecurityGroupA039445A:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: awscdk/Backend/BackendService/SecurityGroup
      SecurityGroupEgress:
        - CidrIp: 0.0.0.0/0
          Description: Allow all outbound traffic by default
          IpProtocol: "-1"
      VpcId:
        Ref: VpcC3027511
    Metadata:
      aws:cdk:path: awscdk/Backend/BackendService/SecurityGroup/Resource
  BackendBackendServiceSecurityGroupfromawscdkApplicationLoadBalancerALBSecurityGroup5E233E2F80CC189352:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      IpProtocol: tcp
      Description: Load balancer to target
      FromPort: 80
      GroupId:
        Fn::GetAtt:
          - BackendBackendServiceSecurityGroupA039445A
          - GroupId
      SourceSecurityGroupId:
        Fn::GetAtt:
          - ApplicationLoadBalancerALBSecurityGroup0D676F12
          - GroupId
      ToPort: 80
    Metadata:
      aws:cdk:path: awscdk/Backend/BackendService/SecurityGroup/from awscdkApplicationLoadBalancerALBSecurityGroup5E233E2F:80
  CDKMetadata:
    Type: AWS::CDK::Metadata
    Properties:
      ...

Here is the full repo with the branch that I am trying to implement IaC with CDK https://gitlab.com/verbose-equals-true/django-postgres-vue-gitlab-ecs/-/tree/feature-aws-cdk. I'm trying to move this project from CloudFormation to CDK


Solution

  • You need to forward all cookies, headers, and query strings in any behavior pointing at your ALB origin. You probably want to allow all methods as well if you intend on sending data to this backend.

    Here's an example in TypeScript that I'm currently working on for a project (it looks slightly different than yours but should be simple to adapt)

    const cdn = new cf.CloudFrontWebDistribution(this, 'SPACloudFrontDistribution', {
          originConfigs: [
            {
              customOriginSource: {
                domainName: alb.loadBalancerDnsName,
                originProtocolPolicy: cf.OriginProtocolPolicy.MATCH_VIEWER
              },
              behaviors : [ 
                { 
                  isDefaultBehavior: true,
                  allowedMethods: cf.CloudFrontAllowedMethods.ALL,
                  forwardedValues: {
                    queryString: true,
                    cookies: {
                      forward: 'all'
                    },
                    headers: ['*']
                  }
                } 
              ]
            },
            {
              s3OriginSource: {
                s3BucketSource: bucket
              },
              behaviors: [
                {
                  pathPattern: '/static/*',
                  allowedMethods: cf.CloudFrontAllowedMethods.GET_HEAD,
                  cachedMethods: cf.CloudFrontAllowedCachedMethods.GET_HEAD
                }
              ]
            }
          ],
          aliasConfiguration: {
            acmCertRef: sslCert.certificateArn,
            names: domains
          },
          defaultRootObject: ''
        });