2

The following code is my attempt at creating a SPA (Single Page Application) CloudFormation template. I understand there are probably many flaws but I can't conceptually understand how to break the circular dependency error I am getting. In my head, it only makes sense that Route53 depends on CloudFront because it needs to know the AliasTarget, it also makes sense that CloudFront needs to depend on Certificate Manager because it needs AcmCertificateArn and CertificateManager needs to depend on Route53 (for obvious reasons but I have a feeling someone is going to tell me this is where I break the chain).

AWSTemplateFormatVersion: '2010-09-09'
Description: Creates an S3 bucket configured for hosting a static website, and a Route
  53 DNS record pointing to the bucket
Parameters:
  DomainName:
    Type: String
    Description: The DNS name of an existing Amazon Route 53 hosted zone e.g. jevsejev.io
    AllowedPattern: (?!-)[a-zA-Z0-9-.]{1,63}(?<!-)
    ConstraintDescription: must be a valid DNS zone name.
  FullDomainName:
    Type: String
    Description: The full domain name e.g. development.jevsejev.io
    AllowedPattern: (?!-)[a-zA-Z0-9-.]{1,63}(?<!-)
    ConstraintDescription: must be a valid DNS zone name.
Resources:
  Route53:
    Type: AWS::Route53::RecordSet
    DependsOn:
      - Cloudfront
    Properties:
      HostedZoneName: !Ref 'DomainName'
      RecordSets:
        Name: !Ref 'FullDomainName'
        Type: A
        AliasTarget: 
          DNSName: !GetAtt [Cloudfront, WebsiteURL]
  CertificateManager:
    Type: AWS::CertificateManager::Certificate
    DependsOn:
      - Route53
    Properties:
      DomainName: !Ref 'FullDomainName'
  Cloudfront:
    Type: AWS::CloudFront::Distribution
    DependsOn:
    - S3
    Properties:
      DistributionConfig:
        Origins:
        - DomainName: !GetAtt [S3, WebsiteURL]
          Id: S3Origin
          CustomOriginConfig:
            HTTPPort: '80'
            HTTPSPort: '443'
            OriginProtocolPolicy: http-only
        Enabled: true
        HttpVersion: 'http2'
        DefaultRootObject: index.html
        Aliases:
        - !Ref 'FullDomainName'
        DefaultCacheBehavior:
          AllowedMethods:
          - GET
          - HEAD
          Compress: true
          TargetOriginId: S3Origin
          ForwardedValues:
            QueryString: true
            Cookies:
              Forward: none
          ViewerProtocolPolicy: redirect-to-https
        PriceClass: PriceClass_All
        ViewerCertificate:
          AcmCertificateArn: !Ref CertificateManager
          SslSupportMethod: sni-only
  S3:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref 'FullDomainName'
      AccessControl: PublicRead
      WebsiteConfiguration:
        IndexDocument: index.html
Robert
  • 181
  • 1
  • 10
  • 3
    Certificate manager does not depend on Route 53. It just needs a template parameter. – Vikyol Oct 04 '19 at 14:22
  • So ignoring CloudFormation, if I wanted to create a Certificate for a subdomain that I have not created with Route53 that would be fine? – Robert Oct 04 '19 at 14:23
  • Yes, you can create certificates for custom domains as long as you can prove domain ownership via email or DNS records. – Vikyol Oct 04 '19 at 14:30

1 Answers1

2

I also have this problem and just get fixed. Hope this it's helpful for people

The Certificate part once you have this property DomainValidationOptions and use your own HostedZone Id as Value of HostedZoneId, Certificate will get created without hanging there.

Below are my code for reference:

CldoudFrontHostedZoneId is a fixed value from AWS documents https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-route53-aliastarget.html

DefaultHostedZoneId is my HostedZone Id

Parameters:

  DefaultHostedZoneId:
    Description: Default own HostedZone Id
    Type: String
    Default: xxxxxxxxxxxxxx

  CldoudFrontHostedZoneId:
    Description: AWS only use this hosted zone Id for cloudfront
    Type: String
    Default: Z2FDTNDATAQYW2
Resources:
  StagingCloudFrontCertificate:
    Type: AWS::CertificateManager::Certificate
    Properties:
      DomainName: yyyyy.xxxxxxxx.company.com
      ValidationMethod: DNS
      DomainValidationOptions:
        - DomainName: yyyyy.xxxxxxxx.company.com
          HostedZoneId: !Ref DefaultHostedZoneId

StagingCloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Origins:
        - DomainName: !GetAtt StagingPublishedS3.DomainName
          Id: !Ref StagingPublishedS3
          S3OriginConfig:
            OriginAccessIdentity: !Join ['', ['origin-access-identity/cloudfront/', !Ref StagingCloudFrontOriginAccessIdentity]]
        Enabled: 'true'
        Comment: Staging CloudFront Distribution
        HttpVersion: http2
        IPV6Enabled: true
        PriceClass: PriceClass_100
        Aliases: 
        - yyyyy.xxxxxxxx.company.com
        ViewerCertificate:
          MinimumProtocolVersion: TLSv1.2_2021
          AcmCertificateArn: !Ref StagingCloudFrontCertificate           
          SslSupportMethod: sni-only
        DefaultCacheBehavior:
          AllowedMethods:
            - GET
            - HEAD
            - OPTIONS
          CachePolicyId: !Ref AWSmanagedCachePolicy
          # ForwardedValues:
          #   QueryString: false
          ViewerProtocolPolicy: 'redirect-to-https'
          TargetOriginId: !Ref StagingPublishedS3
          TrustedKeyGroups:
          - !Ref StagingCloudFrontKeyGroup
        CacheBehaviors:
          - AllowedMethods:
            - GET
            - HEAD
            PathPattern: /favicon.ico
            TargetOriginId: !Ref StagingPublishedS3
            ViewerProtocolPolicy: 'redirect-to-https'
            CachePolicyId: !Ref AWSmanagedCachePolicy
        
  StagingCloudfrontDNS:
    Type: AWS::Route53::RecordSetGroup
    Properties:
      HostedZoneName: xxxxxxxx.company.com.
      Comment: Zone apex alias targeted to CloudFront
      RecordSets:
      - Name: yyyyy.xxxxxxxx.company.com.
        Type: A
        AliasTarget:
          HostedZoneId: !Ref CldoudFrontHostedZoneId
          DNSName: !GetAtt StagingCloudFrontDistribution.DomainName
      - Name: yyyyy.xxxxxxxx.company.com.
        Type: AAAA
        AliasTarget:
          HostedZoneId: !Ref CldoudFrontHostedZoneId
          DNSName: !GetAtt StagingCloudFrontDistribution.DomainName

Yvette Lau
  • 191
  • 1
  • 7