Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

(Core): unexpected logical id change for CdkJsonStringify when order of stack changed #31345

Closed
1 task
Wurstnase opened this issue Sep 6, 2024 · 3 comments · Fixed by #31470 or softwaremill/tapir#4137 · May be fixed by NOUIY/aws-solutions-constructs#135 or NOUIY/aws-solutions-constructs#136
Assignees
Labels
@aws-cdk/core Related to core CDK functionality bug This issue is a bug. effort/medium Medium work item – several days of effort p1

Comments

@Wurstnase
Copy link
Contributor

Describe the bug

The logical id of a CdkJsonStringify will change for different stacks, when the order of stacks will change.

Regression Issue

  • Select this option if this issue appears to be a regression.

Last Known Working CDK Version

No response

Expected Behavior

Order of stack shouldn't change the logical id.

Current Behavior

The logical id in CdkJsonStringify<id> is stack order dependend.

Reproduction Steps

Synth the following app twice:

Python app
#!/usr/bin/env python3

import os
import aws_cdk as cdk

from aws_cdk import (
    CfnOutput,
    Fn,
    Stack,
    custom_resources as cr,
    aws_ec2 as ec2,
)
from constructs import Construct


class CdkMinimalStack(Stack):
    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        vpc = ec2.Vpc.from_lookup(self, "Vpc", vpc_name="VPC1-VPC")

        vpc_endpoint = ec2.InterfaceVpcEndpoint(
            self,
            "VpcEndpoint",
            vpc=vpc,
            service=ec2.InterfaceVpcEndpointAwsService.APIGATEWAY,
        )

        eni_ids = vpc_endpoint.vpc_endpoint_network_interface_ids
        output_paths = [f"NetworkInterfaces.{id}.PrivateIpAddress" for id in range(3)]

        describe_network_interfaces_response = cr.AwsCustomResource(
            self,
            "DescribeEnis",
            on_update=cr.AwsSdkCall(
                api_version="v3",
                service="ec2",
                action="describeNetworkInterfaces",
                output_paths=output_paths,
                parameters={"NetworkInterfaceIds": eni_ids},
                physical_resource_id=cr.PhysicalResourceId.of(Fn.join("-", eni_ids)),
            ),
            policy=cr.AwsCustomResourcePolicy.from_sdk_calls(
                resources=cr.AwsCustomResourcePolicy.ANY_RESOURCE
            ),
        )
        ips = [
            describe_network_interfaces_response.get_response_field(op)
            for op in output_paths
        ]

        CfnOutput(
            self,
            "Ips",
            value=Fn.join(
                ",",
                ips,
            ),
        )


app = cdk.App()

if os.getenv("DEFAULT", "true") == "true":
    CdkMinimalStack(
        app,
        "Stack1",
        env=cdk.Environment(account="111122223333", region="eu-central-1"),
    )
CdkMinimalStack(
    app,
    "Stack2",
    env=cdk.Environment(account="222233334444", region="eu-central-1"),
)
if os.getenv("DEFAULT", "false") != "true":
        CdkMinimalStack(
        app,
        "Stack1",
        env=cdk.Environment(account="111122223333", region="eu-central-1"),
    )
app.synth()
DEFAULT=true cdk synth
grep CdkJsonStringify -r cdk.out
DEFAULT=true
cdk.out/Stack2.template.json:         "CdkJsonStringify2",
cdk.out/Stack2.template.json:         "CdkJsonStringify2",
cdk.out/Stack2.template.json:  "CdkJsonStringify2": {
cdk.out/Stack2.template.json:    "aws:cdk:path": "Stack2/CdkJsonStringify2/Default"
cdk.out/Stack1.template.json:         "CdkJsonStringify1",
cdk.out/Stack1.template.json:         "CdkJsonStringify1",
cdk.out/Stack1.template.json:  "CdkJsonStringify1": {
cdk.out/Stack1.template.json:    "aws:cdk:path": "Stack1/CdkJsonStringify1/Default"
cdk.out/manifest.json:        "/Stack1/CdkJsonStringify1/Default": [
cdk.out/manifest.json:            "data": "CdkJsonStringify1"
cdk.out/manifest.json:        "/Stack2/CdkJsonStringify2/Default": [
cdk.out/manifest.json:            "data": "CdkJsonStringify2"
cdk.out/tree.json:          "CdkJsonStringify1": {
cdk.out/tree.json:            "id": "CdkJsonStringify1",
cdk.out/tree.json:            "path": "Stack1/CdkJsonStringify1",
cdk.out/tree.json:                "path": "Stack1/CdkJsonStringify1/Default",
cdk.out/tree.json:          "CdkJsonStringify2": {
cdk.out/tree.json:            "id": "CdkJsonStringify2",
cdk.out/tree.json:            "path": "Stack2/CdkJsonStringify2",
cdk.out/tree.json:                "path": "Stack2/CdkJsonStringify2/Default",
DEFAULT=false cdk synth
grep CdkJsonStringify -r cdk.out
DEFAULT=false
cdk.out/Stack2.template.json:         "CdkJsonStringify1",
cdk.out/Stack2.template.json:         "CdkJsonStringify1",
cdk.out/Stack2.template.json:  "CdkJsonStringify1": {
cdk.out/Stack2.template.json:    "aws:cdk:path": "Stack2/CdkJsonStringify1/Default"
cdk.out/Stack1.template.json:         "CdkJsonStringify2",
cdk.out/Stack1.template.json:         "CdkJsonStringify2",
cdk.out/Stack1.template.json:  "CdkJsonStringify2": {
cdk.out/Stack1.template.json:    "aws:cdk:path": "Stack1/CdkJsonStringify2/Default"
cdk.out/manifest.json:        "/Stack2/CdkJsonStringify1/Default": [
cdk.out/manifest.json:            "data": "CdkJsonStringify1"
cdk.out/manifest.json:        "/Stack1/CdkJsonStringify2/Default": [
cdk.out/manifest.json:            "data": "CdkJsonStringify2"
cdk.out/tree.json:          "CdkJsonStringify1": {
cdk.out/tree.json:            "id": "CdkJsonStringify1",
cdk.out/tree.json:            "path": "Stack2/CdkJsonStringify1",
cdk.out/tree.json:                "path": "Stack2/CdkJsonStringify1/Default",
cdk.out/tree.json:          "CdkJsonStringify2": {
cdk.out/tree.json:            "id": "CdkJsonStringify2",
cdk.out/tree.json:            "path": "Stack1/CdkJsonStringify2",
cdk.out/tree.json:                "path": "Stack1/CdkJsonStringify2/Default",

Possible Solution

No response

Additional Information/Context

No response

CDK CLI Version

2.156.0 (build 2966832)

Framework Version

No response

Node.js Version

v20.17.0

OS

macOS 14.6.1

Language

Python

Language Version

3.12.5

Other information

No response

@Wurstnase Wurstnase added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Sep 6, 2024
@github-actions github-actions bot added the @aws-cdk/core Related to core CDK functionality label Sep 6, 2024
@pahud pahud self-assigned this Sep 9, 2024
@pahud
Copy link
Contributor

pahud commented Sep 10, 2024

Yes I can reproduce this.

Looks like when you deploy 2 stacks, the stack order ID would be appended to the logicalId of Custom::AWSCDKCfnJsonStringify and you get CdkJsonStringify1 and CdkJsonStringify2.

Now if you re-deploy the 2nd stack only the logicalId would change, causing unnecessary custom resource update which could have been avoided.

image image

I believe the root cause is around here:

CfnUtils.stringify(stack, `CdkJsonStringify${stringifyCounter++}`, intrinsic),

@pahud pahud added p1 effort/medium Medium work item – several days of effort and removed needs-triage This issue or PR still needs to be triaged. labels Sep 10, 2024
@pahud pahud removed their assignment Sep 10, 2024
@comcalvi comcalvi self-assigned this Sep 25, 2024
@mergify mergify bot closed this as completed in #31470 Oct 1, 2024
@mergify mergify bot closed this as completed in 4128bf1 Oct 1, 2024
Copy link

github-actions bot commented Oct 1, 2024

Comments on closed issues and PRs are hard for our team to see.
If you need help, please open a new issue that references this one.

1 similar comment
Copy link

github-actions bot commented Oct 1, 2024

Comments on closed issues and PRs are hard for our team to see.
If you need help, please open a new issue that references this one.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 1, 2024
# for free to subscribe to this conversation on GitHub. Already have an account? #.