Stelligent Amazon Pollycast
Voiced by Amazon Polly

Traditionally, managing transport layer security (TLS) digital certificates that are used for encrypting data in transit between clients and servers has been a very manual process. In 2016, Amazon Web Services began offering the AWS Certificate Manager (ACM) – a service for managing these digital certificates. By using AWS CloudFormation and AWS CodePipeline, you can automate the end-to-end process for deploying these TLS certificates to the websites that need TLS encryption.

Here is a list of the core AWS services covered in this post:

  • Amazon CloudFront is a managed cloud distribution network (CDN) that helps improve performance of static and dynamic content through the use of edge locations across the world. What’s more, when configured properly, you get built-in security features such as reducing the chance of a distributed denial of service attack.
  • AWS Certificate Manager (ACM) is a service that lets you easily provision, manage, and deploy Secure Sockets Layer/Transport Layer Security (SSL/TLS) certificates for use with AWS services. With ACM, you can encrypt data in transit.
  • AWS CloudFormation is a service for creating and managing AWS resources with templates.
  • AWS CodePipeline is a fully-managed service for releasing software using Continuous Delivery.

In this post, you will see an example static web application in which all of the AWS infrastructure resources are defined as code in AWS CloudFormation and versioned in an AWS CodeCommit version repository. Furthermore, you will automate the continuous delivery workflow of this solution using AWS CodePipeline.

Creating a Pipeline to Deploy an ACM Certificate

In this example, you will see how you can create a CloudFormation template that automatically provisions CodePipeline, a CodeCommit private Git repository, a CodeBuild project to deploy the static website, and a CloudFormation deploy provider for CodePipeline to launch CloudFormation stacks that deploy CloudFront and ACM resources.

This way you can deploy changes to your ACM and CloudFront configurations as code without needing to manually provision and run a new CloudFormation stack every time.

Deployment Steps

There are four main steps in launching this solution: prepare an AWS account, create and store source files, launch the CloudFormation stack, and test the deployment. Each is described in more detail in this section. Please note that you are responsible for any fees incurred while creating and launching your solution.

Step 1. Prerequisites

This example assumes you have access to an AWS account and have established the necessary permissions. In order to show specific directory names, it also assumes you are using AWS Cloud9 for your IDE. If you are not, you should be able to simply modify the directory names accordingly.

Step 2. Create and Store Source Files

Next, you will create a few source files that will be stored in S3 and then uploaded to AWS CodeCommit when launching the CloudFormation stack. You will also download the files for an example static website provided by AWS that you will be deployed as part of the CodeBuild and CodePipeline configuration in the CloudFormation template.

From your AWS Cloud9 terminal, type the following to setup your directory structure:

sudo rm -rf ~/environment/tls 
mkdir ~/environment/tls
aws s3 mb s3://ceoa-tls-$(aws sts get-caller-identity --output text --query 'Account')
cd ~/environment/tls
wget https://docs.aws.amazon.com/codepipeline/latest/userguide/samples/sample-website.zip
unzip sample-website.zip

Create and save three empty source files:

touch tls-acm.yml
touch tls-cloudfront.yml
touch tls-pipeline.yml
tls-acm.yml

Copy the contents below into your local tls-acm.yml file in Cloud9 and save it. This CloudFormation template provisions an ACM certificate for the specified domain that you own.

You will need to modify the DnsDomainName parameter (currently listed as encryptaws.com) to the domain name that you are managing in Amazon Route 53.

---
AWSTemplateFormatVersion: '2010-09-09'
Description: Creates the ACM Certificate for domain
Parameters:
  DnsDomainName:
    Description: Domain to secure with the certificate (e.g. encryptaws.com)
    Type: String
    Default: "encryptaws.com"
  DnsSubDomainName:
    Description: Sub domain (default is '*') to secure with certificate
    Type: String
    Default: "*"
Resources:
  Certificate:
    Type: AWS::CertificateManager::Certificate
    Properties:
      DomainName: !Join
        - '.'
        - - !Ref DnsSubDomainName
          - !Ref DnsDomainName
      DomainValidationOptions:
        - DomainName: !Join
            - '.'
            - - !Ref DnsSubDomainName
              - !Ref DnsDomainName
          ValidationDomain: !Ref DnsDomainName
      ValidationMethod: "DNS"
Outputs:
  CertificateArn:
    Value: !Ref Certificate
    Export:
      Name: !Sub '${AWS::Region}-CertificateArn'
tls-cloudfront.yml

Copy the contents below into your local tls-cloudfront.yml file in Cloud9 and save it. This CloudFormation template provisions a CloudFront distribution and links it to the ACM certificate you provisioned in the tls-acm.yml template.

You will need to modify the following parameters:

  • BucketName – Change the Default value (currently listed as www.encryptaws.com) to the globally unique S3 bucket name you wish for the CloudFormation stack to create.
  • DomainName – Change the Default value (currently listed as www.encryptaws.com.s3.amazonaws.com) to the globally unique S3 bucket name automatically generated based on the BucketName.
  • CloudFrontCName – Change the Default value (currently listed as www.encryptaws.com) to a name that will be used in Route 53.
  • HostedZoneId – Change the Default value (currently listed as Z3DG6IL3SJCGPX) to the Hosted Zone ID you configured in Route 53. To do this, go to the Route 53 console and click on Hosted Zones. There will be a column called Hosted Zone ID next to the domain you are managing in Route 53. Copy that value to the parameter default in this CloudFormation template.
---
AWSTemplateFormatVersion: '2010-09-09'
Description: CloudFront configuration for S3 bucket
Parameters:
  BucketName:
    Type: String
    Description: "Bucket Name"
    Default: "www.encryptaws.com"
  DomainName:
    Type: String
    Description: "Domain Name"
    Default: "www.encryptaws.com.s3.amazonaws.com"
  CloudFrontDomainName:
    Type: String
    Description: "CloudFront DomainName"
    Default: ""
  CloudFrontCName:
    Type: String
    Description: "CloudFront CNames"
    Default: "www.encryptaws.com"
  HostedZoneId:
    Type: String
    Description: "Hosted Zone Id"
    Default: "Z3DG6IL3SJCGPX"

Resources:
  CloudFrontOriginAccessIdentity:
    Type: "AWS::CloudFront::CloudFrontOriginAccessIdentity"
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: CloudFrontOriginAccessIdentity for TLS Solution

  CloudFront:
    Type: "AWS::CloudFront::Distribution"
    Properties:
      DistributionConfig:
        Aliases: 
          - !Ref CloudFrontCName
        Enabled: true
        Comment: "created by CloudFormation"
        ViewerCertificate:
          AcmCertificateArn:
            Fn::ImportValue: "us-east-1-CertificateArn"
          MinimumProtocolVersion: TLSv1.1_2016
          SslSupportMethod: sni-only
        Enabled: true
        Origins:
          - Id: S3-Origin
            DomainName: !Ref 'DomainName'
            S3OriginConfig:
              OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}"
        DefaultRootObject: index.html
        DefaultCacheBehavior:
          TargetOriginId: S3-Origin
          ViewerProtocolPolicy: redirect-to-https
          DefaultTTL: 600
          ForwardedValues:
            QueryString: True
            Cookies:
              Forward: all
          Compress: True

  StaticSiteDomainName:
    Type: "AWS::Route53::RecordSet"
    Properties:
      Type: A
      HostedZoneId: !Ref 'HostedZoneId'
      Name: !Ref 'CloudFrontCName'
      AliasTarget:
        DNSName: !GetAtt CloudFront.DomainName  #CloudFront.DomainName = d2y1cxx7cc6yud.cloudfront.net.
        HostedZoneId: Z2FDTNDATAQYW2 #CloudFront Default HostedZoneId
tls-pipeline.yml

Copy the source contents from the tls-pipeline.yml file and save it to your local file of the same name in your Cloud9 environment. The file is a 400-line CloudFormation template and provisions CodeCommit, IAM, CodeBuild, and CodePipeline to deploy the CloudFormation templates for CloudFront and ACM.

Sync the files with your S3 bucket

Here, you will zip and upload all of the source files to S3 so that they can be committed to the CodeCommit repository that is automatically provisioned by the stack generated by the tls-pipeline.yml template.

From your AWS Cloud9 environment, type the following:

cd ~/environment/tls
zip ceoa-tls-examples.zip *.*
aws s3 sync ~/environment/tls s3://ceoa-tls-$(aws sts get-caller-identity --output text --query 'Account')

Step 3. Launch the Stack

From your AWS Cloud9 environment, type the following (replacing CloudFrontCName with the value you used for the parameter of the same name in the tls-cloudfront.yml template and replacing DnsDomainName with the value you used for the parameter of the same name in the tls-acm.yml template ):
aws cloudformation create-stack --stack-name ceoa-tls-pipeline --template-body file:///home/ec2-user/environment/tls/tls-pipeline.yml --parameters ParameterKey=CodeCommitS3Bucket,ParameterValue=ceoa-tls-$(aws sts get-caller-identity --output text --query 'Account') ParameterKey=CodeCommitS3Key,ParameterValue=ceoa-tls-examples.zip ParameterKey=SiteBucketName,ParameterValue=CloudFrontCName ParameterKey=DnsDomainName,ParameterValue=DnsDomainName --capabilities CAPABILITY_NAMED_IAM --disable-rollback

Step 4. Test the Deployment

Verify the CloudFormation template has launched by going to the CloudFormation dashboard.

Once the stack is CREATE_COMPLETE, select it and click on the Outputs tab. Then, click on the PipelineUrl Output. This will launch the pipeline you automatically provisioned in CodePipeline – as shown below.

Once it has successfully run through all the stages in the pipeline, you will verify the TLS certificate is configured in the AWS Certificate Manager by opening your web browser and typing the URL you entered for the CloudFrontCName parameter value. Verify that the website launches and that encryption has been enabled in transit by looking for the TLS certificate next to the URL in the browser.

What’s Next?

In this post, you learned how to automate the provisioning of a pipeline deploys changes to CloudFront and the ACM whenever there is a modification to the CloudFormation templates that manage the provisioning of these services.

As demonstrated in Deploy Managed Config Rules using CloudFormation and CodePipeline, this is an approach you can use in deploying changes to multiple CloudFormation templates as part of a deployment pipeline in CodePipeline.

Let us know how you choose to implement this by reaching out to us @stelligent or @mphasis.