How to provision a CloudFront distribution with an

2019-03-24 18:20发布

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.

5条回答
劫难
2楼-- · 2019-03-24 19:03

Another valid approach I now use just creates the stack with the default certificate as long as the certificate is not issued (Inspired by this post)

It looks like

"Conditions": {
    "HasAcmCertificate": {
        "Fn::Equals": [
            {
                "Ref": "CloudfrontCertificateArn"
            },
            "NOT_ISSUED"
        ]
    }
},

...

"Cloudfront": {
    "Properties": {
        "DistributionConfig": {

            ...

            "ViewerCertificate": {
                "AcmCertificateArn": {
                    "Fn::If": [
                        "HasAcmCertificate",
                        {
                            "Ref": "AWS::NoValue"
                        },
                        {
                            "Ref": "CloudfrontCertificateArn"
                        }
                    ]
                },
                "CloudFrontDefaultCertificate": {
                    "Fn::If": [
                        "HasAcmCertificate",
                        true,
                        {
                            "Ref": "AWS::NoValue"
                        }
                    ]
                },
                "SslSupportMethod": {
                    "Fn::If": [
                        "HasAcmCertificate",
                        {
                            "Ref": "AWS::NoValue"
                        },
                        "sni-only"
                    ]
                }
            }
        }
    },
    "Type": "AWS::CloudFront::Distribution"
},
查看更多
3楼-- · 2019-03-24 19:15

Took a few days but found the answer with some help from AWS support.

The information for:

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

is found using the CLI "aws iam list-server-certificates":

{
    "ServerCertificateId": "ASCAXXXXXXXXXXXXXX", 
    "ServerCertificateName": "devops.XXXXXXX.com", 
    "Expiration": "2017-03-10T15:00:33Z", 
    "Path": "/cloudfront/", 
    "Arn": "arn:aws:iam::XXXXXXXXXXX:server-certificate/cloudfront/devops.XXXXXXXXXXX.com", 
    "UploadDate": "2016-03-14T16:13:59Z"
}, 

Once I found that I added a variable cloudfront.CloudFrontCertificateId with the ServerCertificateId and fed it into the ViewerCertificate:

"ViewerCertificate" : {
  "IamCertificateId" : {{ cloudfront.CloudFrontCertificateId }},
  "SslSupportMethod": "sni-only"
}
查看更多
男人必须洒脱
4楼-- · 2019-03-24 19:17

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

查看更多
Bombasti
5楼-- · 2019-03-24 19:25

I had a properly created certificate (public key 2048 bits), uploaded with the full chain. What was more challenging was the certificate was being used without problem in other AWS services (public ELB).

I was also passing the certificate Id (I had also tried with ARN but that's incorrect) correctly.

In my case, the problem was the certificate had been created with a "path": "/". After I uploaded a new certificate (with different name) with "Path": "/cloudfront/", everything worked without problem.

  aws iam upload-server-certificate \
    --server-certificate-name cert_cf \
    --certificate-body file://cert.crt \
    --private-key file://cert.key \
    --certificate-chain file://chain.pem \
    --path /cloudfront/
查看更多
看我几分像从前
6楼-- · 2019-03-24 19:26

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.

查看更多
登录 后发表回答