如何使用 Cloud Formation 为 CloudFront 分配配置 ACM 证书

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

我正在尝试使用 Cloud Formation 在我的 CloudFrontDistribution 中设置证书。

我的证书是通过证书管理器颁发的。它已获批准,我已验证该证书可以直接通过 CloudFront 控制台手动配置。

在我的 CloudFormation 模板中,我尝试使用与 IamCertificateId 属性:

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

但是在这两种情况下我都收到以下错误:

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

阅读 DistributionConfig Complex Type 的文档似乎有 'ACMCertificateArn' 属性,但这似乎无法通过 CloudFormation 工作。

如有任何帮助,我们将不胜感激。

(更新:从 Aug 9 2016, AWS CloudFormation now supports ACM using the AcmCertificateArn 属性 开始,因此不再需要下面描述的自定义资源。)


尽管 AWS::CloudFront::Distribution 资源尚未更新以支持 ACMCertificateArn 属性,但目前可以使用 custom CloudFormation resource 来实现使用 AWS API直接更新官方资源

参见 Ryan S. Brown 的 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

要使用它,您需要通过 AWS Lambda 函数使 CloudFormation 资源的实现可用。 Ryan 的实现已发布到 public S3 存储桶,因此您可以在 CloudFormation 模板中直接引用它以进行测试,如下所示:

"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
  }
},

Lambda::Function 资源依赖于 IAM 服务角色和相关策略,以将必要的权限委托给 lambda 函数(上面的 ExecRole 参考),因此您也需要添加它:

"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"]}
        }
      ]
    }
  }
},

有了 lambda 函数,最后添加 Custom::CloudFrontAcmAssociation 资源,提供分发 ID、证书 ARN 和自定义资源 lambda 函数的 ARN:

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

tldr:将上面的所有代码复制到您的 CloudFormation 模板中,设置适当的 SiteCDNAcmCertificate 属性(或使用硬编码值编辑模板),您应该有一个自定义资源解决方法,直到 Amazon 更新官方 CloudFront 资源。

花了几天时间,但在 AWS 支持的帮助下找到了答案。

以下信息:

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

使用 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"
}, 

一旦我发现我添加了一个变量 cloudfront.CloudFrontCertificateId 与 ServerCertificateId 并将其输入到 ViewerCertificate:

"ViewerCertificate" : {
  "IamCertificateId" : {{ cloudfront.CloudFrontCertificateId }},
  "SslSupportMethod": "sni-only"
}

我有一个正确创建的证书(public 密钥 2048 位),与完整链一起上传。更具挑战性的是证书在其他 AWS 服务(public ELB)中的使用没有问题。

我也正确地传递了证书 ID(我也尝试过使用 ARN,但这是不正确的)。

在我的例子中,问题是证书是用 "path":“/”创建的。在我使用 "Path": "/cloudfront/" 上传新证书(名称不同)后,一切正常。

  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/

Cloudformation 添加了这个 属性 但没有记录。您可以像这样轻松使用:

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

注意证书必须在us-east-1地区创建,否则将不被接受。

我现在使用的另一种有效方法是在未颁发证书的情况下使用默认证书创建堆栈(受 this post 启发)

看起来像

"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"
},