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.
(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.