Using AWS CloudFormation to Deploy a WebSite with AWS S3 and AWS CloudFront

This will use AWS CloudFormation (ie, Infrastructure as Code) to create an S3 Bucket with a good bucket policy and CORS configuration. Also, it will setup the CloudFront distribution to point at this S3 Bucket. Lastly, it will create an AWS Route 53 record set for the website. You will need to put content into the S3 bucket, but I would recommend doing so as part of your DevOps process (see DevOps: Invalidating AWS CloudFront Cache during AWS CodeBuild)

Here is the CloudFormation Template written in YAML.

AWSTemplateFormatVersion: 2010-09-09
Description: Web Applications CloudFront Edge servers
Parameters:
  BucketName:
    Type: String
    Description: The S3 Bucket Name to store the web applications artifacts
    MinLength: 1
    MaxLength: 255
  WebsiteHostname:
    Type: String
    Description: CloudFront edge server hostname.
  CertificateArn:
    Type: String
    Description: The certificate ARN to associate with CloudFront Deployment
  Route53StackName:
    Type: String
    Description: Name of the CloudFormation Stack that contains the Route53 configuration
    MinLength: 1
    MaxLength: 255
Resources:
  WebsiteBucket:
    Type: 'AWS::S3::Bucket'
    Properties:
      AccessControl: 'PublicRead'
      BucketName: !Ref BucketName
      VersioningConfiguration:
        Status: Enabled
      WebsiteConfiguration:
        IndexDocument: index.html
      CorsConfiguration:
        CorsRules:
          - AllowedMethods:
              - GET
              - HEAD
            AllowedOrigins:
              - '*'
  WebsiteBucketPolicy:
    Type: 'AWS::S3::BucketPolicy'
    Properties:
      Bucket: !Ref WebsiteBucket
      PolicyDocument:
        Statement:
          - Action:
              - 's3:GetObject'
            Effect: Allow
            Resource: !Join 
              - ''
              - - 'arn:aws:s3:::'
                - !Ref WebsiteBucket
                - /*
            Principal: '*'
  OriginAccessIdentity:
    Type: 'AWS::CloudFront::CloudFrontOriginAccessIdentity'
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: Web Applications
  CloudFrontDistribution:
    Type: 'AWS::CloudFront::Distribution'
    DependsOn:
      - WebsiteBucket
    Properties:
      DistributionConfig:
        Aliases:
          - !Join 
            - ''
            - - !Ref WebsiteHostname
        Comment: Web Application CloudFront Distribution
        Enabled: true
        HttpVersion: http2
        DefaultRootObject: index.html
        Origins:
          - Id: S3
            DomainName: !GetAtt 
              - WebsiteBucket
              - DomainName
            S3OriginConfig:
              OriginAccessIdentity: !Join 
                - ''
                - - origin-access-identity/cloudfront/
                  - !Ref OriginAccessIdentity
        DefaultCacheBehavior:
          AllowedMethods:
            - GET
            - HEAD
            - OPTIONS
          CachedMethods:
            - GET
            - HEAD
            - OPTIONS
          MinTTL: '60'
          MaxTTL: '60'
          DefaultTTL: '60'
          Compress: 'true'
          TargetOriginId: S3
          ForwardedValues:
            Cookies:
              Forward: none
            QueryString: 'false'
            Headers:
              - Origin
              - Access-Control-Request-Headers
              - Access-Control-Request-Method
          ViewerProtocolPolicy: allow-all
        PriceClass: PriceClass_100
        ViewerCertificate:
          AcmCertificateArn: !Ref CertificateArn
          SslSupportMethod: sni-only
        CustomErrorResponses:
          - ErrorCachingMinTTL: '60'
            ErrorCode: '404'
            ResponseCode: '200'
            ResponsePagePath: '/index.html'
  Route53RecordSet:
    Type: 'AWS::Route53::RecordSet'
    Properties:
      HostedZoneName: !Join 
        - ''
        - - !ImportValue 
            'Fn::Sub': '${Route53StackName}-DNSZone'
          - .
      Comment: CloudFront Web Applications DNS Record
      Name: !Join 
        - ''
        - - !Ref WebsiteHostname
      Type: CNAME
      TTL: '300'
      ResourceRecords:
        - !GetAtt 
          - CloudFrontDistribution
          - DomainName
Outputs:
  Url:
    Description: Web Applications Url
    Value: !Join 
      - ''
      - - 'https://'
        - !Ref WebsiteHostname
    Export:
      Name: !Sub '${AWS::StackName}-Url'
  BucketName:
    Description: The bucket name so it can be used by following pipeline steps
    Value: !Ref BucketName

Leave a Reply

Your email address will not be published. Required fields are marked *