AWS CDK:向所有/多个资源添加Cfn条件

时间:2020-04-02 02:32:21

标签: amazon-web-services kotlin aws-cdk

我需要为CDK中的所有/多个资源添加一个条件(例如isProdisSpecificRegion)。我看到可以使用以下技术将CfnCondition添加到特定的CDK资源:here。例如

// Create the user using the L2 construct
const user = new iam.User(this, 'User');

// Add the condition on the underlying AWS::IAM::User
(user.node.defaultChild as iam.CfnUser).cfnOptions.condition = regionCondition

是否有办法遍历所有资源并应用条件?

3 个答案:

答案 0 :(得分:1)

通常,除非特别需要,否则您不希望将CfnConditional与CDK一起使用。正如阿米特(Amit)在其评论中提到的那样,惯用的CDK方法是使用该语言的if / else逻辑来实例化构造或不实例化。

如果您绝对需要在每个资源上使用CFNConditional,那么我认为方面功能可以使用转义阴影将条件应用于底层云形成资源,尽管我会强烈 >对此提出建议。

答案 1 :(得分:0)

我想出了一种方法,可以使用CfnCondition中的Construct#onPrepare钩子将Stack自动应用于堆栈中的每个资源

在上一页中,Construct#onPrepare()是:

在合成之前执行最终修改

此方法可以由派生构造实现,以便在合成之前执行最终更改。准备好子构造后,将调用prepare()。

这是高级框架功能。仅在了解其含义后使用此功能。

以下Kotlin中包含以下CDK代码(示例代码-我仅在此处放入相关位) 适用于任何 支持的CDK 语言:

class MyStack internal constructor(app: App, name: String) : Stack(app, name) {
      private lateinit var myCondition: CfnCondition

    init {
        myCondition = CfnCondition.Builder.create(this, "myCondition")
                .expression(Fn.condition...) // Fill in your condition
                .build()
    }

    /**
     * Use the `Stack#onPrepare` hook to find all CF resources and apply our stack standard CF condition on them.
     * See:
     * * [Stack.onPrepare]: https://docs.aws.amazon.com/cdk/api/latest/typescript/api/core/construct.html#core_Construct_onPrepare
     */
    override fun onPrepare() {
        super.onPrepare()
        findAllCfnResources(this.node).forEach { it.cfnOptions.condition = this.myCondition }
    }

    /**
     * Recurse through all children nodes and accumulate [CfnResource] nodes.
     */
    private fun findAllCfnResources(node: ConstructNode): List<CfnResource> {
        val nodes = node.children.map { it.node } + node
        val cfnResources: List<CfnResource> = nodes.flatMap { it.findAll() }.mapNotNull { it as? CfnResource }

        if (node.children.isEmpty()) {
            return cfnResources
        }

        return node.children.fold(cfnResources) {
            accumulatedCfnResources, it -> accumulatedCfnResources + findAllCfnResources(it.node)
        }
    }
}

我添加了一个单元测试(Junit和AssertJ,也在Kotlin中),验证它涵盖了所有资源:

private val mapper = ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT)

private lateinit var stack: Stack
private lateinit var synthesizedStackCfTemplate: JsonNode

@BeforeEach
fun setUp() {
    val app = App()
    stack = MyStack(app, "MyUnitTestStack")
    synthesizedStackCfTemplate = mapper.valueToTree(app.synth().getStackArtifact(stack.artifactId).template)
}

@Test
fun `All CfnResources have the expected CF Condition`() {
    val expectedCondition = "myCondition"

    val softly = SoftAssertions()
    synthesizedStackCfTemplate.get("Resources").forEach { resource ->
        softly.assertThat(resource.get("Condition")?.asText())
                .describedAs("Resource is missing expected CF condition [$expectedCondition]: $resource")
                .isEqualTo(expectedCondition)
    }
    softly.assertAll()
}

答案 2 :(得分:0)

使用CDK Aspectsas suggested by Richard),我可以进一步简化我的解决方案(基于之前的解决方案:https://stackoverflow.com/a/61234581/117750):

class ApplyCfnConditionAspect(val cfnCondition: CfnCondition): IAspect {
    override fun visit(construct: IConstruct) {
        (construct as? CfnResource)?.cfnOptions?.condition = this.cfnCondition
    }
}

...,然后在指定所有资源后,在Stack中:

this.node.applyAspect(ApplyCfnConditionAspect(shouldEnableAnalyticsCondition))