解决 AWS CloudFormation 中的循环依赖
Work around circular dependency in AWS CloudFormation
下面的AWS CloudFormation给出了一个循环依赖错误。我的理解是依赖关系是这样流动的:rawUploads -> generatePreview -> previewPipeline -> rawUploads
。虽然看起来 rawUploads
并不依赖于 generatePreview
,但我猜想 CF 在创建存储桶时需要知道要触发什么 lambda,即使触发器是在 CloudFormation 模板的 lambda 部分定义的。
我在网上找到了一些讨论类似问题的资源,但它似乎不适用于此处。 https://aws.amazon.com/premiumsupport/knowledge-center/unable-validate-circular-dependency-cloudformation/
要打破这个循环依赖链,我有哪些选择?可编写脚本的解决方案是可行的,但手动更改的多个部署不适合我的用例。
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Resources:
rawUploads:
Type: 'AWS::S3::Bucket'
previewAudioFiles:
Type: 'AWS::S3::Bucket'
generatePreview:
Type: AWS::Serverless::Function
Properties:
Handler: generatePreview.handler
Runtime: nodejs6.10
CodeUri: .
Environment:
Variables:
PipelineId: !Ref previewPipeline
Events:
BucketrawUploads:
Type: S3
Properties:
Bucket: !Ref rawUploads
Events: 's3:ObjectCreated:*'
previewPipeline:
Type: Custom::ElasticTranscoderPipeline
Version: '1.0'
Properties:
ServiceToken:
Fn::Join:
- ":"
- - arn:aws:lambda
- Ref: AWS::Region
- Ref: AWS::AccountId
- function
- aws-cloudformation-elastic-transcoder-pipeline-1-0-0
Name: transcoderPipeline
InputBucket:
Ref: rawUploads
OutputBucket:
Ref: previewAudioFiles
一种方法是为 S3 存储桶指定明确的名称,这样以后您就可以简单地使用存储桶名称,而不是依赖 Ref: bucketname
。如果您想要自动生成存储桶名称,这显然是有问题的,在这种情况下,谨慎的做法是从某个前缀加上(唯一的)堆栈名称生成存储桶名称,例如:
InputBucket: !Join ["-", ['rawuploads', Ref: 'AWS::StackName']]
另一种选择是使用单个 CloudFormation 模板,但分 2 个阶段 - 第一个阶段创建基础资源(以及任何非循环引用),然后将剩余的引用添加到模板并进行堆栈更新。显然不理想,所以我更喜欢第一种方法。
您也可以在需要引用 ARN 的情况下使用第一种技术,例如:
!Join ['/', ['arn:aws:s3:::logsbucket', 'AWSLogs', Ref: 'AWS:AccountId', '*']]
使用此技术时,您可能还想考虑使用 DependsOn,因为您已经删除了有时会导致问题的隐式依赖项。
这个post最终帮助了我:https://aws.amazon.com/premiumsupport/knowledge-center/unable-validate-destination-s3/
我最终在 CloudFormation 中配置了一个 SNS 主题。存储桶会推送关于该主题的事件,而 Lambda 函数会监听该主题。这样依赖关系图如下:
S3 bucket -> SNS topic -> SNS topic policy
Lambda function -> SNS topic
Lambda function -> transcoder pipeline
类似的东西(省略了一些政策)
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Resources:
SNSTopic:
Type: AWS::SNS::Topic
SNSTopicPolicy:
Type: AWS::SNS::TopicPolicy
Properties:
PolicyDocument:
Id: MyTopicPolicy
Version: '2012-10-17'
Statement:
- Sid: Statement-id
Effect: Allow
Principal:
AWS: "*"
Action: sns:Publish
Resource:
Ref: SNSTopic
Condition:
ArnLike:
aws:SourceArn:
!Join ["-", ['arn:aws:s3:::rawuploads', Ref: 'AWS::StackName']]
Topics:
- Ref: SNSTopic
rawUploads:
Type: 'AWS::S3::Bucket'
DependsOn: SNSTopicPolicy
Properties:
BucketName: !Join ["-", ['rawuploads', Ref: 'AWS::StackName']]
NotificationConfiguration:
TopicConfigurations:
- Topic:
Ref: "SNSTopic"
Event: 's3:ObjectCreated:*'
previewAudioFiles:
Type: 'AWS::S3::Bucket'
generatePreview:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Join ["-", ['generatepreview', Ref: 'AWS::StackName']]
Handler: generatePreview.handler
Runtime: nodejs6.10
CodeUri: .
Environment:
Variables:
PipelineId: !Ref previewPipeline
Events:
BucketrawUploads:
Type: SNS
Properties:
Topic: !Ref "SNSTopic"
previewPipeline:
Type: Custom::ElasticTranscoderPipeline
DependsOn: 'rawUploads'
Version: '1.0'
Properties:
ServiceToken:
Fn::Join:
- ":"
- - arn:aws:lambda
- Ref: AWS::Region
- Ref: AWS::AccountId
- function
- aws-cloudformation-elastic-transcoder-pipeline-1-0-0
Name: transcoderPipeline
InputBucket:
!Join ["-", ['arn:aws:s3:::rawuploads', Ref: 'AWS::StackName']]
OutputBucket:
Ref: previewAudioFiles
下面的AWS CloudFormation给出了一个循环依赖错误。我的理解是依赖关系是这样流动的:rawUploads -> generatePreview -> previewPipeline -> rawUploads
。虽然看起来 rawUploads
并不依赖于 generatePreview
,但我猜想 CF 在创建存储桶时需要知道要触发什么 lambda,即使触发器是在 CloudFormation 模板的 lambda 部分定义的。
我在网上找到了一些讨论类似问题的资源,但它似乎不适用于此处。 https://aws.amazon.com/premiumsupport/knowledge-center/unable-validate-circular-dependency-cloudformation/
要打破这个循环依赖链,我有哪些选择?可编写脚本的解决方案是可行的,但手动更改的多个部署不适合我的用例。
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Resources:
rawUploads:
Type: 'AWS::S3::Bucket'
previewAudioFiles:
Type: 'AWS::S3::Bucket'
generatePreview:
Type: AWS::Serverless::Function
Properties:
Handler: generatePreview.handler
Runtime: nodejs6.10
CodeUri: .
Environment:
Variables:
PipelineId: !Ref previewPipeline
Events:
BucketrawUploads:
Type: S3
Properties:
Bucket: !Ref rawUploads
Events: 's3:ObjectCreated:*'
previewPipeline:
Type: Custom::ElasticTranscoderPipeline
Version: '1.0'
Properties:
ServiceToken:
Fn::Join:
- ":"
- - arn:aws:lambda
- Ref: AWS::Region
- Ref: AWS::AccountId
- function
- aws-cloudformation-elastic-transcoder-pipeline-1-0-0
Name: transcoderPipeline
InputBucket:
Ref: rawUploads
OutputBucket:
Ref: previewAudioFiles
一种方法是为 S3 存储桶指定明确的名称,这样以后您就可以简单地使用存储桶名称,而不是依赖 Ref: bucketname
。如果您想要自动生成存储桶名称,这显然是有问题的,在这种情况下,谨慎的做法是从某个前缀加上(唯一的)堆栈名称生成存储桶名称,例如:
InputBucket: !Join ["-", ['rawuploads', Ref: 'AWS::StackName']]
另一种选择是使用单个 CloudFormation 模板,但分 2 个阶段 - 第一个阶段创建基础资源(以及任何非循环引用),然后将剩余的引用添加到模板并进行堆栈更新。显然不理想,所以我更喜欢第一种方法。
您也可以在需要引用 ARN 的情况下使用第一种技术,例如:
!Join ['/', ['arn:aws:s3:::logsbucket', 'AWSLogs', Ref: 'AWS:AccountId', '*']]
使用此技术时,您可能还想考虑使用 DependsOn,因为您已经删除了有时会导致问题的隐式依赖项。
这个post最终帮助了我:https://aws.amazon.com/premiumsupport/knowledge-center/unable-validate-destination-s3/
我最终在 CloudFormation 中配置了一个 SNS 主题。存储桶会推送关于该主题的事件,而 Lambda 函数会监听该主题。这样依赖关系图如下:
S3 bucket -> SNS topic -> SNS topic policy
Lambda function -> SNS topic
Lambda function -> transcoder pipeline
类似的东西(省略了一些政策)
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Resources:
SNSTopic:
Type: AWS::SNS::Topic
SNSTopicPolicy:
Type: AWS::SNS::TopicPolicy
Properties:
PolicyDocument:
Id: MyTopicPolicy
Version: '2012-10-17'
Statement:
- Sid: Statement-id
Effect: Allow
Principal:
AWS: "*"
Action: sns:Publish
Resource:
Ref: SNSTopic
Condition:
ArnLike:
aws:SourceArn:
!Join ["-", ['arn:aws:s3:::rawuploads', Ref: 'AWS::StackName']]
Topics:
- Ref: SNSTopic
rawUploads:
Type: 'AWS::S3::Bucket'
DependsOn: SNSTopicPolicy
Properties:
BucketName: !Join ["-", ['rawuploads', Ref: 'AWS::StackName']]
NotificationConfiguration:
TopicConfigurations:
- Topic:
Ref: "SNSTopic"
Event: 's3:ObjectCreated:*'
previewAudioFiles:
Type: 'AWS::S3::Bucket'
generatePreview:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Join ["-", ['generatepreview', Ref: 'AWS::StackName']]
Handler: generatePreview.handler
Runtime: nodejs6.10
CodeUri: .
Environment:
Variables:
PipelineId: !Ref previewPipeline
Events:
BucketrawUploads:
Type: SNS
Properties:
Topic: !Ref "SNSTopic"
previewPipeline:
Type: Custom::ElasticTranscoderPipeline
DependsOn: 'rawUploads'
Version: '1.0'
Properties:
ServiceToken:
Fn::Join:
- ":"
- - arn:aws:lambda
- Ref: AWS::Region
- Ref: AWS::AccountId
- function
- aws-cloudformation-elastic-transcoder-pipeline-1-0-0
Name: transcoderPipeline
InputBucket:
!Join ["-", ['arn:aws:s3:::rawuploads', Ref: 'AWS::StackName']]
OutputBucket:
Ref: previewAudioFiles