CloudFormation,在DependsOn上应用条件

时间:2016-01-05 08:47:37

标签: amazon-web-services amazon-s3 amazon-cloudformation

我需要做的任务是让CDN依赖于S3存储桶。但是我们想让它使用现有的存储桶,而不是创建一个新存储桶。

以下是我正在尝试的示例代码:

"Parameters" : {
  "UseExistingBucket" : {
    "Description" : "Yes/No",
    "Default" : "yes",
    "Type" : "String",
    "AllowedValues" : [ "yes", "no" ]
  }
},
"Conditions" : {
  "CreateS3Resources" : {"Fn::Equals" : [{"Ref" : "UseExistingBucket"}, "no"]}
},
"Resources" : {
  "StaticBucket" : {
    "Type" : "AWS::S3::Bucket",
    "Condition" : "CreateS3Resources",
    "Properties" : {
      "BucketName" : { "Fn::Join": [ "-", [ "app",  { "Ref": "EnvType" }, "static" ] ] }
    },
    "DeletionPolicy": "Retain"
  },
  "MyStaticDistribution": {
    "Type": "AWS::CloudFront::Distribution",
    "Properties": {
      "DistributionConfig": {
        "Origins": [
          {
            "DomainName": {
              "Fn::If" : [
                "CreateS3Resources",
                { "Fn::Join": [ "-", [ "app",  { "Ref": "EnvType" }, "static" ] ] },
                {"Fn::GetAtt": [ "StaticBucket", "DomainName" ] }
              ]
            },
            "Id": "S3Origin",
          }
        ]
      }
    },
    "DependsOn": [{
      "Fn::If" : [
        "CreateS3Resources",
        { "Fn::Join": [ "-", [ "app",  { "Ref": "EnvType" }, "static" ] ] },
        ""
      ]
    }]
  }
}

如果需要,请向我建议更多细节(至少stackoverflow确实需要更多细节,但我没有指定任何细节:-P)

5 个答案:

答案 0 :(得分:10)

您可以使用条件Fn:GetAtt中包含的Fn:If来执行此操作。 使用Fn:GetAtt意味着依赖,因此CloudFormation一旦到达该函数就会自动等待,就像使用DependsOn一样。

示例

下面的代码片段通过有条件地检索尚未创建的嵌套堆栈的名称来显示这一点,但只有在条件UseNestedStack设置为true时才会这样做。如果UseNestedStack为false,它将不会等待,而是检索本地变量名称。

{
"Fn::If": ["UseNestedStack", {
    "Fn::GetAtt": ["NestedStack", "Outputs.Name"]
}, {
    "Ref": "LocalName"
}]

我怎么知道这个? (另一个例子)

不幸的是没有官方文档正式声明这一点,但是AWS告诉我这样做,并且在他们的代码示例中,您可以看到,当订单有问题时,他们使用Fn:GetAtt。我已经尝试了很多次,每次都有效。在一个简单的堆栈上自己尝试一下。以下是我调整并自己使用的AWS lambda示例中的一些伪证明。如果在资源AMI信息之后创建AMI功能,AMI Info需要输出AMI功能,则下面的堆栈可能无法工作,因此AWS已使用Fn:GetAtt将它们链接在一起。要查看此滚动到底部并查看资源AMIInfo,您将看到它通过fn:Gett引用AMIFunction。 CloudFormation看到了这一点,然后回到AMIFunction首先创建它。

"AMIInfoFunction": {
  "DependsOn":"SourceStack",
  "Type": "AWS::Lambda::Function",
  "Properties": {
    "Code": {
      "S3Bucket": { "Ref": "DeploymentBucket" },
      "S3Key": {"Fn::Join": [
        "",
        [
          {
            "Ref": "ApplicationName"
          },
          "/amilookup.zip"
        ]
      ]}
    },
    "Handler": "amilookup.handler",
    "Runtime": "nodejs",
    "Timeout": "30",
    "Role": { "Fn::GetAtt" : ["LambdaExecutionRole", "Arn"] },
    "VpcConfig": {
      "SecurityGroupIds": [ {"Ref": "InstanceSecurityGroup"}],
      "SubnetIds": [ {"Ref":"PrivateSubnetA"},{"Ref":"PrivateSubnetB"} ]
    }
  }
},
"AMIInfo": {
  "Type": "Custom::AMIInfo",
  "Properties": {
    "ServiceToken": { "Fn::GetAtt" : ["AMIInfoFunction", "Arn"] },
    "StackName": { "Ref":"SourceStack" }
  }
}

答案 1 :(得分:2)

在模板中,您不需要将DependsOn属性添加到MyStaticDistribution资源,因为您已经拥有对StaticBucket资源的引用。

优化AWS CloudFormation模板博客指定依赖关系部分对此进行了说明: https://aws.amazon.com/blogs/devops/optimize-aws-cloudformation-templates/

When you need CloudFormation to wait to provision one resource until another one has been provisioned, you can use the DependsOn attribute.

You can also introduce references between elements by using either the { "Ref": "MyResource" } or the { "Fn::GetAtt" : [ "MyResource" , "MyAttribute" ] } functions. When you use one of these functions, CloudFormation behaves as if you’ve added a DependsOn attribute to the resource.

答案 2 :(得分:0)

对于Yaml用户,您还可以使用:

Conditions:
  CreateConfigRecorder: !Equals [ !Ref ConfigRecorderExists, 'false' ]

Resource:
#my 1st AWS Resource
  ConfigRecorder: 
    Condition: CreateConfigRecorder
    Type: AWS::Config::ConfigurationRecorder
    *more codes below*

#added, since DependsOn: !If is not possible, trigger by WaitCondition if CreateConfigRecorder is true
#Hacks: https://garbe.io/blog/2017/07/17/cloudformation-hacks/
  ConfigRecorderWaitHandle: 
    Condition: CreateConfigRecorder
    DependsOn: ConfigRecorder
    Type: "AWS::CloudFormation::WaitConditionHandle"
#added, since DependsOn: !If is not possible, trigger by WaitCondition if CreateConfigRecorder is false
  WaitHandle: 
    Type: "AWS::CloudFormation::WaitConditionHandle"
#added, since DependsOn: !If is not possible
  WaitCondition: 
    Type: "AWS::CloudFormation::WaitCondition"
    Properties: 
      Handle: !If [CreateConfigRecorder, !Ref ConfigRecorderWaitHandle, !Ref WaitHandle]
      Timeout: "1"
      Count: 0
#my 2nd AWS Resource that requires DependsOn Attribute
  AWSConfigRule:
    Type: AWS::Config::ConfigRule
    DependsOn: WaitCondition #added, since DependsOn: !If is not possible
    *more codes below*

基本上,如果我的第一资源不存在,则在运行CFN之前,我的第二资源仅具有DependsOn属性。我是从https://garbe.io/blog/2017/07/17/cloudformation-hacks/

获得的

答案 3 :(得分:0)

为了扩展上面@Usman Mutawakil 的回答,您可以在标签中使用 GetAtt 来隐式施加 DependsOn 条件。

例如,在我们的例子中,我们使用 lambda 函数在一段时间后自动删除暂存堆栈。这个func只部署在staging上,必须在栈的最后删除。

  ShopCluster:
    Type: 'AWS::ECS::Cluster'
    Properties:
      ...
      Tags:
        - Key: ConditionalDependsOn
          Value: !If [IsStaging, !GetAtt DeleteShopStackLambda.Arn, Ignored]

答案 4 :(得分:-1)

我正在做同样的事情。 错误是:

  

调用时出现客户端错误(ValidationError)   ValidateTemplate操作:模板格式错误:DependsOn必须是a   字符串或字符串列表。

似乎无法在DependsOn中添加条件。

我开始研究其他表达方式。也许将依赖代码放入2个不同的子模板中。 从外部检测条件并将适当的嵌套模板名称传递给父模板参数。 所以DependsOn看到我传入的参数是依赖模板。

似乎过于扭曲的逻辑。

一定是更好的方式。