Skip to content

bug: Redeploying an ARecord with CDK does not work when the Hosted Zone is created in another stack and retrieved with SSM Parameters #13491

@Garethp

Description

@Garethp

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

One of the patterns we use to manage Certificates for our Cloudfront Distributions is to set up the Hosted Zones and Certificates in one stack, publish the identifying information into SSM on a well-known parameter name and then consume them in another stack.

When testing the new Cloudformation refactoring for updating stacks in-place in localstack, I found that attempting to redeploy the consuming stack without any changes will result in the following error:

❌  api-gateway-stack failed: InternalFailure: The API action 'CreateChangeSet' for service 'cloudformation' is either not available in your current license plan or has not yet been emulated by LocalStack. Please refer to https://docs.localstack.cloud/references/coverage/coverage_cloudformation for more information.

Expected Behavior

Redeploying this stack should not cause any errors

How are you starting LocalStack?

With a docker-compose file

Steps To Reproduce

How are you starting localstack (e.g., bin/localstack command, arguments, or docker-compose.yml)

docker compose up -d
services:
  localstack:
    container_name: "localstack-testing"
    image: localstack/localstack-pro:4.11.1
    network_mode: bridge
    ports:
      - "4566:4566"
      - "4510-4559:4510-4559"
      - "127.0.0.1:53:53"
      - "127.0.0.1:53:53/udp"
      - "4571:4571"
    environment:
      - DOCKER_HOST=unix:///var/run/docker.sock
      - TEST_AWS_ACCOUNT_ID=000000000000
      - LOCALSTACK_AUTH_TOKEN=${LOCALSTACK_AUTH_TOKEN}
      - NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt
      - CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
      - REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
      - "/etc/ssl/certs/:/etc/ssl/certs/"

Stacks

DNS Stack

export class DnsStack extends Stack {
  constructor(scope: Construct, id: string, props: StackProps) {
    super(scope, id, props);

    const hostedZone = new PublicHostedZone(this, "HostedZone", {
      zoneName: "localhost.localstack.cloud",
    });

    const certificate = new Certificate(this, "Example", {
      domainName: "example.localhost.localstack.cloud",
      validation: CertificateValidation.fromDns(hostedZone),
    });

    new StringParameter(this, "ZoneIdParameter", {
      parameterName: "HostedZoneId",
      stringValue: hostedZone.hostedZoneId,
    });

    new StringParameter(this, "CertificateArnParameter", {
      parameterName: "CertificateArn",
      stringValue: certificate.certificateArn,
    });
  }
}

API Gateway Stack

export class ApiGatewayStack extends Stack {
  constructor(scope: Construct, id: string, props: StackProps) {
    super(scope, id, props);

    const hostedZoneId = StringParameter.fromStringParameterName(
      this,
      "HostedZoneId",
      "HostedZoneId",
    );

    const certificateArn = StringParameter.fromStringParameterName(
      this,
      "certificateArn",
      "CertificateArn",
    );

    const certificate = Certificate.fromCertificateArn(
      this,
      "Certificate",
      certificateArn.stringValue,
    );

    const hostedZone = HostedZone.fromHostedZoneAttributes(
      this,
      "RetrievedZone",
      {
        hostedZoneId: hostedZoneId.stringValue,
        zoneName: "localhost.localstack.cloud",
      },
    );

    const api = new RestApi(this, "test-api", {
      restApiName: "test-api",
      domainName: {
        domainName: "example.localhost.localstack.cloud",
        certificate,
      },
    });

    const distribution = new Distribution(this, "Distribution", {
      defaultBehavior: {
        origin: new HttpOrigin("example.localhost.localstack.cloud", {}),
      },
      domainNames: ["example.localhost.localstack.cloud"],
      certificate,
    });

    new ARecord(this, "ARecord", {
      recordName: "example.localhost.localstack.cloud.",
      zone: hostedZone,
      target: RecordTarget.fromAlias(new CloudFrontTarget(distribution)),
    });

    const responseLambda = new Function(this, "hello-world", {
      runtime: Runtime.NODEJS_20_X,
      handler: "index.handler",
      code: Code.fromInline(
        "exports.handler = async (event) => ({statusCode: 201, body: JSON.stringify({ Hello: 'World' }) })",
      ),
    });

    api.root.addMethod("GET", new LambdaIntegration(responseLambda));
  }
}

Commands

yarn cdklocal bootstrap
yarn cdklocal deploy dns-stack --require-approval never
yarn cdklocal deploy api-gateway-stack --require-approval never
yarn cdklocal deploy api-gateway-stack --require-approval never

Environment

- OS: Ubuntu 24.04
- LocalStack: 4.11.1
  LocalStack version: 4.11.1
  LocalStack Docker image sha: 5e04a5c0b207
  LocalStack build date: 2025-11-26
  LocalStack build git hash: 600b61ad7

Anything else?

I've reproduced the issue here: https://github.com/Garethp/localstack-testing/tree/cross-stack-dns-redeploy.
When I clone down the branch cross-stack-dns-redeploy, run yarn install, and then ./start.sh I see the message

❌  api-gateway-stack failed: InternalFailure: The API action 'CreateChangeSet' for service 'cloudformation' is either not available in your current license plan or has not yet been emulated by LocalStack. Please refer to https://docs.localstack.cloud/references/coverage/coverage_cloudformation for more information.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions