Muszę dodać warunek (np. isProd lub isSpecificRegion) do wszystkich / wielu moich zasobów w CDK. Widziałem, że mogę dodać CfnCondition do określonych zasobów CDK za pomocą tej techniki: Dodaj warunki do zasobów w CDK. na przykład

// 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

Czy istnieje sposób na iterację wszystkich zasobów i zastosowanie warunku?

0
arnab 2 kwiecień 2020, 05:32

3 odpowiedzi

Najlepsza odpowiedź

Korzystanie z aspektów CDK (zgodnie z sugestią Richarda). Udało mi się jeszcze bardziej uprościć moje rozwiązanie (opierając się na poprzednim rozwiązaniu: https://stackoverflow.com/a/61234581/117750):

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

... a następnie w Stack, po określeniu wszystkich zasobów:

this.node.applyAspect(ApplyCfnConditionAspect(shouldEnableAnalyticsCondition))
0
arnab 15 kwiecień 2020, 19:05

Ogólnie rzecz biorąc, nie chcesz używać CfnConditional z CDK, chyba że istnieje bardzo konkretna potrzeba. Jak wspomniał Amit w swoim komentarzu, idiomatyczna ocena CDK polegałaby na użyciu logiki if / else w języku do utworzenia instancji konstrukcji lub nie.

Jeśli absolutnie musisz korzystać z warunku CFNC dla każdego zasobu, myślę, że funkcja aspektów może zastosować warunek do bazowych zasobów chmury przy użyciu luków ucieczki, chociaż zdecydowanie odradzam to.

1
Richard H Boyd 7 kwiecień 2020, 15:24

Znalazłem sposób na automatyczne zastosowanie CfnCondition do każdego zasobu w stosie za pomocą Konstruuj # onPrepare w haku Stos

Z powyższej strony Construct#onPrepare() to:

Dokonaj ostatecznych modyfikacji przed syntezą

Metodę tę można zaimplementować za pomocą konstrukcji pochodnych w celu wykonania ostatecznych zmian przed syntezą. Preparat () zostanie wywołany po przygotowaniu konstrukcji potomnych.

To jest zaawansowana funkcja frameworka. Używaj tego tylko wtedy, gdy rozumiesz konsekwencje.

Poniższy kod CDK (przykładowy kod - wstawiam tutaj tylko odpowiednie bity) znajduje się w Kotlin, ale miałoby zastosowanie do dowolnego obsługiwane CDK język:

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)
        }
    }
}

Test jednostkowy (Junit i AssertJ, również w Kotlinie), który dodałem, sprawdza, czy obejmuje to wszystkie zasoby:

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()
}
0
arnab 15 kwiecień 2020, 17:25