创建 CloudFront 分发时神秘的 CloudFormation 失败
Cryptic CloudFormation failure when creating CloudFront Distribution
我设置了一个 CloudFormation 模板来跟踪 CloudFront 分布等。进行此设置后,我创建了一个 AWS::CertificateManager::Certificate
和一个 AWS::CloudFront::Distribution
资源,其中 CDN 仅从非网站 S3 来源提供服务。
当我 运行 更改集时,我得到了这个令人难以置信的模糊失败。
“操作 'AWS::CloudFront::Distribution' 的访问被拒绝。”我有点迷失在这里。一方面,我不清楚这应该是什么操作。最重要的是,此后的堆栈回滚是不完整的。 CloudFormation 事件甚至没有显示尝试删除 CDN 或证书,当我尝试从浏览器访问 CloudFront URL 时,它运行完美,所以我什至不确定我的模板在尝试什么在这里做失败。事实上,这对我来说是个问题的唯一原因是不完整的回滚试图将我在堆栈中的 lambda 恢复到 nodejs8.10,这会导致更大的故障。如果这不是问题,我不知道我会感受到这个模糊错误的影响。
模板,基于几年前的静态站点示例:
AWSTemplateFormatVersion: 2010-09-09
Transform:
- AWS::Serverless-2016-10-31
- AWS::CodeStar
Parameters:
ProjectId:
Type: String
Description: AWS CodeStar projectID used to associate new resources to team members
CodeDeployRole:
Type: String
Description: IAM role to allow AWS CodeDeploy to manage deployment of AWS Lambda functions
Stage:
Type: String
Description: The name for a project pipeline stage, such as Staging or Prod, for which resources are provisioned and deployed.
Default: ''
Globals:
Api:
BinaryMediaTypes:
- image~1png
Function:
Runtime: nodejs14.x
AutoPublishAlias: live
DeploymentPreference:
Enabled: true
Type: Canary10Percent5Minutes
Role: !Ref CodeDeployRole
Resources:
MahCert:
Type: AWS::CertificateManager::Certificate
Properties:
DomainName: domain.com
DomainValidationOptions:
- DomainName: domain.com
HostedZoneId: Z2GZX5ZQI1HO5L
SubjectAlternativeNames:
- '*.domain.com'
CertificateTransparencyLoggingPreference: ENABLED
ValidationMethod: DNS
CloudFrontCDN:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Comment: Source for all static resources
PriceClass: PriceClass_100
Aliases:
- domain.com
ViewerCertificate:
AcmCertificateArn: !Ref MahCert
MinimumProtocolVersion: TLSv1.2_2021
SslSupportMethod: sni-only
DefaultRootObject: index.html
DefaultCacheBehavior:
ViewerProtocolPolicy: redirect-to-https
CachePolicyId: b2884449-e4de-46a7-ac36-70bc7f1ddd6d
TargetOriginId: SiteBucket
Enabled: True
Origins:
- DomainName: <my_bucket>.s3.amazonaws.com
Id: SiteBucket
S3OriginConfig:
OriginAccessIdentity: ''
ServerlessRestApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
DefinitionBody:
swagger: 2.0
info:
title: Static resource proxy
paths:
/static/{proxy+}:
get:
x-amazon-apigateway-integration:
httpMethod: ANY
type: http_proxy
uri: <my_bucket>.s3.amazonaws.com/static/{proxy}
responses: {}
GetHelloWorld:
Type: AWS::Serverless::Function
Properties:
Handler: index.get
Role:
Fn::GetAtt:
- LambdaExecutionRole
- Arn
Events:
GetEvent:
Type: Api
Properties:
Path: /
Method: get
ProxyEvent:
Type: Api
Properties:
Path: /{proxy+}
Method: any
GetStaticContent:
Type: AWS::Serverless::Function
Properties:
Handler: index.getResource
Role:
Fn::GetAtt:
- LambdaExecutionRole
- Arn
Events:
GetResourceEvent:
Type: Api
Properties:
Path: /static/{folder}/{file}
Method: get
GetQuote:
Type: AWS::Serverless::Function
Properties:
Handler: index.getQuote
Role:
Fn::GetAtt:
- LambdaDynamoDBReadRole
- Arn
Events:
GetRandomQuoteEvent:
Type: Api
Properties:
Path: /getquote
Method: get
GetQuoteEvent:
Type: Api
Properties:
Path: /getquote/{id}
Method: get
LambdaExecutionRole:
Description: Creating service role in IAM for AWS Lambda
Type: AWS::IAM::Role
Properties:
RoleName: !Sub 'CodeStar-${ProjectId}-Execution${Stage}'
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [lambda.amazonaws.com]
Action: sts:AssumeRole
Path: /
ManagedPolicyArns:
- !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
PermissionsBoundary: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/CodeStar_${ProjectId}_PermissionsBoundary'
LambdaDynamoDBReadRole:
Description: Creating service role in IAM for AWS Lambda
Type: AWS::IAM::Role
Properties:
RoleName: !Sub '${ProjectId}-DynamoDB-Read'
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [lambda.amazonaws.com]
Action: sts:AssumeRole
Path: /
Policies:
-
PolicyName: "dynamodb-read-quotes"
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- "dynamodb:GetItem"
- "dynamodb:DescribeTable"
Resource: "<dynamo_arn>"
ManagedPolicyArns:
- !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
注意 - domain.com
不是我在这里使用的实际域。
更新:
我完全删除了堆栈并从这个模板中重新创建了它,认为堆栈的历史有问题。但是,我得到了同样的错误。
堆栈使用的 IAM 角色具有这些权限
事实上,即使在授予此角色对 CloudFront 资源的完全写入权限后,问题仍然存在。
基于聊天讨论。
发现问题的原因是缺少用于部署堆栈的 IAM 角色的 IAM 权限。具体来说,缺少的权限是:
cloudfront:GetDistribution
- 授予获取有关网络分发的信息的权限
将该权限添加到角色,解决了问题。
为了找到丢失的权限,使用了 CloudTrial 的事件历史记录。
我设置了一个 CloudFormation 模板来跟踪 CloudFront 分布等。进行此设置后,我创建了一个 AWS::CertificateManager::Certificate
和一个 AWS::CloudFront::Distribution
资源,其中 CDN 仅从非网站 S3 来源提供服务。
当我 运行 更改集时,我得到了这个令人难以置信的模糊失败。
模板,基于几年前的静态站点示例:
AWSTemplateFormatVersion: 2010-09-09
Transform:
- AWS::Serverless-2016-10-31
- AWS::CodeStar
Parameters:
ProjectId:
Type: String
Description: AWS CodeStar projectID used to associate new resources to team members
CodeDeployRole:
Type: String
Description: IAM role to allow AWS CodeDeploy to manage deployment of AWS Lambda functions
Stage:
Type: String
Description: The name for a project pipeline stage, such as Staging or Prod, for which resources are provisioned and deployed.
Default: ''
Globals:
Api:
BinaryMediaTypes:
- image~1png
Function:
Runtime: nodejs14.x
AutoPublishAlias: live
DeploymentPreference:
Enabled: true
Type: Canary10Percent5Minutes
Role: !Ref CodeDeployRole
Resources:
MahCert:
Type: AWS::CertificateManager::Certificate
Properties:
DomainName: domain.com
DomainValidationOptions:
- DomainName: domain.com
HostedZoneId: Z2GZX5ZQI1HO5L
SubjectAlternativeNames:
- '*.domain.com'
CertificateTransparencyLoggingPreference: ENABLED
ValidationMethod: DNS
CloudFrontCDN:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Comment: Source for all static resources
PriceClass: PriceClass_100
Aliases:
- domain.com
ViewerCertificate:
AcmCertificateArn: !Ref MahCert
MinimumProtocolVersion: TLSv1.2_2021
SslSupportMethod: sni-only
DefaultRootObject: index.html
DefaultCacheBehavior:
ViewerProtocolPolicy: redirect-to-https
CachePolicyId: b2884449-e4de-46a7-ac36-70bc7f1ddd6d
TargetOriginId: SiteBucket
Enabled: True
Origins:
- DomainName: <my_bucket>.s3.amazonaws.com
Id: SiteBucket
S3OriginConfig:
OriginAccessIdentity: ''
ServerlessRestApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
DefinitionBody:
swagger: 2.0
info:
title: Static resource proxy
paths:
/static/{proxy+}:
get:
x-amazon-apigateway-integration:
httpMethod: ANY
type: http_proxy
uri: <my_bucket>.s3.amazonaws.com/static/{proxy}
responses: {}
GetHelloWorld:
Type: AWS::Serverless::Function
Properties:
Handler: index.get
Role:
Fn::GetAtt:
- LambdaExecutionRole
- Arn
Events:
GetEvent:
Type: Api
Properties:
Path: /
Method: get
ProxyEvent:
Type: Api
Properties:
Path: /{proxy+}
Method: any
GetStaticContent:
Type: AWS::Serverless::Function
Properties:
Handler: index.getResource
Role:
Fn::GetAtt:
- LambdaExecutionRole
- Arn
Events:
GetResourceEvent:
Type: Api
Properties:
Path: /static/{folder}/{file}
Method: get
GetQuote:
Type: AWS::Serverless::Function
Properties:
Handler: index.getQuote
Role:
Fn::GetAtt:
- LambdaDynamoDBReadRole
- Arn
Events:
GetRandomQuoteEvent:
Type: Api
Properties:
Path: /getquote
Method: get
GetQuoteEvent:
Type: Api
Properties:
Path: /getquote/{id}
Method: get
LambdaExecutionRole:
Description: Creating service role in IAM for AWS Lambda
Type: AWS::IAM::Role
Properties:
RoleName: !Sub 'CodeStar-${ProjectId}-Execution${Stage}'
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [lambda.amazonaws.com]
Action: sts:AssumeRole
Path: /
ManagedPolicyArns:
- !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
PermissionsBoundary: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/CodeStar_${ProjectId}_PermissionsBoundary'
LambdaDynamoDBReadRole:
Description: Creating service role in IAM for AWS Lambda
Type: AWS::IAM::Role
Properties:
RoleName: !Sub '${ProjectId}-DynamoDB-Read'
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [lambda.amazonaws.com]
Action: sts:AssumeRole
Path: /
Policies:
-
PolicyName: "dynamodb-read-quotes"
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- "dynamodb:GetItem"
- "dynamodb:DescribeTable"
Resource: "<dynamo_arn>"
ManagedPolicyArns:
- !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
注意 - domain.com
不是我在这里使用的实际域。
更新:
我完全删除了堆栈并从这个模板中重新创建了它,认为堆栈的历史有问题。但是,我得到了同样的错误。
堆栈使用的 IAM 角色具有这些权限
事实上,即使在授予此角色对 CloudFront 资源的完全写入权限后,问题仍然存在。
基于聊天讨论。
发现问题的原因是缺少用于部署堆栈的 IAM 角色的 IAM 权限。具体来说,缺少的权限是:
cloudfront:GetDistribution
- 授予获取有关网络分发的信息的权限
将该权限添加到角色,解决了问题。
为了找到丢失的权限,使用了 CloudTrial 的事件历史记录。