Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to provision a CloudFront distribution with an ACM Certificate using Cloud Formation

I am attempting to set a certificate in my CloudFrontDistribution using Cloud Formation.

My certificate has been issued via Certificate Manager. It has been approved, and I have validated that the certificate works by manual configuration directly through the CloudFront console.

Within my CloudFormation template, I have attempted to use both the Identifier and ARN values associated with the certificate in the IamCertificateId property:

"ViewerCertificate" : {
  "IamCertificateId" : "********",
  "SslSupportMethod": "sni-only"
}

But in both cases I receive the following error:

The specified SSL certificate doesn't exist, isn't valid, or doesn't include a valid certificate chain.

Reading the docs for the DistributionConfig Complex Type it looks like there is a 'ACMCertificateArn' property, but this does not seem to work via CloudFormation.

Any help would be appreciated.

like image 208
Toby Hede Avatar asked Mar 10 '16 10:03

Toby Hede


People also ask

What is ACM CloudFront?

ACM stands for AWS Certificate Manager. It provides the free SSL certificates which can be integrated with AWS development services such as ELB and cloudfront etc. Using the public certificates generated from ACM, You can secure your domain names and the sub-domains.

How do you use Amazon CloudFront to distribute your content?

To create a CloudFront distributionOpen the CloudFront console at https://console.aws.amazon.com/cloudfront/v3/home . Choose Create Distribution, and then choose Get Started. Under Origin Settings, for Origin Domain Name, choose the Amazon S3 bucket that you created earlier.

Can I use self signed certificate with CloudFront?

CloudFront supports the same certificate authorities that Mozilla does. For the current list, see Mozilla Included CA Certificate List . You can't use a self-signed certificate for HTTPS communication between CloudFront and your origin.

When creating an AWS CloudFront distribution Which of the following is not an origin?

The correct answer is option A (CloudFront cannot serve content from a non-AWS origin server).


2 Answers

Cloudformation added this property but it is not documented. You can use like this easily:

"ViewerCertificate": {
            "SslSupportMethod": "sni-only",
            "AcmCertificateArn": "CERTIFICATE_ARN"
}

Be aware that the certificate must be created in us-east-1 region, if not it won't be accepted.

like image 107
Çağatay Gürtürk Avatar answered Sep 21 '22 08:09

Çağatay Gürtürk


(Update: As of Aug 9 2016, AWS CloudFormation now supports ACM using the AcmCertificateArn property, so the custom resource described below is no longer needed.)


Although the AWS::CloudFront::Distribution resource hasn't been updated to support the ACMCertificateArn property yet, it is currently possible to use a custom CloudFormation resource to implement the functionality needed using the AWS API directly until the official resource is updated.

See Ryan S. Brown's post, CloudFormation To Build A CDN With (Free) Custom SSL where he describes his implementation of a Custom::CloudFrontAcmAssociation resource that associates an ACM certificate with a CloudFront distribution. The code is available at ryansb/acm-certs-cloudformation.

To use it, you need to make the CloudFormation resource's implementation available through an AWS Lambda function. Ryan's implementation is already published to a public S3 bucket, so you can reference this directly for testing purposes in your CloudFormation template like so:

"AcmAssociationFunction": {
  "Type": "AWS::Lambda::Function",
  "Properties": {
    "Handler": "cloudfront_associator.handler",
    "MemorySize": 128,
    "Runtime": "python2.7",
    "Code": {
      "S3Bucket": "demos.serverlesscode.com",
      "S3Key": "acm-certificate-resource-functions.zip"
    },
    "Role": {"Fn::GetAtt": ["ExecRole", "Arn"]},
    "Timeout": 300
  }
},

The Lambda::Function resource has a dependency on an IAM service Role and associated Policy to delegate the necessary permissions to the lambda function (the ExecRole reference above), so you need to add that too:

"ExecRolePolicies": {
  "Type": "AWS::IAM::Policy",
  "Properties": {
    "PolicyName": "ExecRolePolicy",
    "PolicyDocument": {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Action": [
            "acm:*",
            "cloudfront:List*",
            "cloudfront:Get*",
            "cloudfront:UpdateDistribution"
          ],
          "Resource": [ "*" ],
          "Effect": "Allow"
        },
        {
          "Action": [ "logs:*" ],
          "Resource": "arn:aws:logs:*:*:*",
          "Effect": "Allow"
        }
      ]
    },
    "Roles": [{"Ref": "ExecRole"}]
  }
},
"ExecRole": {
  "Type": "AWS::IAM::Role",
  "Properties": {
    "AssumeRolePolicyDocument": {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Action": ["sts:AssumeRole"],
          "Effect": "Allow",
          "Principal": {"Service": ["lambda.amazonaws.com"]}
        }
      ]
    }
  }
},

With the lambda function in place, finally add the Custom::CloudFrontAcmAssociation resource, providing the distribution ID, certificate ARN, and the custom resource lambda function's ARN:

"DistributionCertificateSetting": {
  "Type": "Custom::CloudFrontAcmAssociation",
  "Properties": {
    "DistributionId": {
      "Ref": "SiteCDN"
    },
    "CertificateArn": {
      "Ref": "AcmCertificate"
    },
    "ServiceToken": {
      "Fn::GetAtt": [
        "AcmAssociationFunction",
        "Arn"
      ]
    }
  }
},

tldr: copy all the code above into your CloudFormation template, set the appropriate SiteCDN and AcmCertificate properties (or edit the template with hard-coded values), and you should have a custom resource workaround until Amazon updates the official CloudFront resource.

like image 36
wjordan Avatar answered Sep 18 '22 08:09

wjordan