Skip to content

sst@2.49.4: [Regression] Multi-region deployments fail due to stack region not being considered at deploy-time #111

@gcxnpl

Description

@gcxnpl

Hi! 👋

Firstly, thanks for your work on this project! 🙂

Today I used patch-package to patch sst@2.49.4 for the project I'm working on.

In one of our stacks, we have a setup where one stack deploys to ap-southeast-2 and one deploys to us-east-1. When we upgraded to the mentioned version of SST from 2.48.5 the us-east-1 stack was deploying in ap-southeast-2.

I built the projects on both versions of SST and noted the build manifests looked identical, so the issue seemed to be with how SST ingests the manifest during deploy-time, potentially related to the migration of using the native CDK module instead of the fork the SST team was maintaining. It seemed like the region of the stack as a whole was being utilised instead of the per-stack region configuration exposed within the manifest. crossRegionReferences was enabled in all relevant stacks as well.

The patch below solved the problem however it is not a holistic solution - I patched until it resolved our issues. I'm raising this patch to get eyes on the problem from SMEs so that we can get a conclusive fix merged in. Thanks.

diff --git a/node_modules/sst/stacks/deploy.js b/node_modules/sst/stacks/deploy.js
index 7e764c6..c801c05 100644
--- a/node_modules/sst/stacks/deploy.js
+++ b/node_modules/sst/stacks/deploy.js
@@ -5,6 +5,8 @@ import { useAWSClient, useAWSProvider } from "../credentials.js";
 import { Logger } from "../logger.js";
 import { filterOutputs, isFailed, monitor, } from "./monitor.js";
 import { VisibleError } from "../error.js";
+
+
 export async function publishAssets(stacks) {
     Logger.debug("Publishing assets");
     const { cdk } = useProject().config;
@@ -74,7 +76,7 @@ export async function deployMany(stacks) {
 export async function deploy(stack) {
     const bus = useBus();
     const { cdk } = useProject().config;
-    Logger.debug("Deploying stack", stack.id);
+    Logger.debug("Deploying stack", stack.stackName, stack.id, stack.environment);
     try {
         const cfnStack = await getCloudFormationStack(stack);
         await addInUseExports(stack, cfnStack);
@@ -86,7 +88,7 @@ export async function deploy(stack) {
         await buildAndPublishAssets(deployment, stack);
         if (cfnStack?.StackStatus === "ROLLBACK_COMPLETE" ||
             cfnStack?.StackStatus === "ROLLBACK_FAILED") {
-            await deleteCloudFormationStack(stack.stackName);
+            await deleteCloudFormationStack(stack.stackName, stack.environment.region);
         }
         const stackParams = await buildCloudFormationStackParams(deployment, stack, cdk);
         try {
@@ -238,8 +240,10 @@ async function buildCloudFormationStackParams(deployment, stack, cdkOptions) {
     const templateUrl = s3Url
         ? `https://s3.${env.resolvedEnvironment.region}.amazonaws.com/${s3Url[1]}/${s3Url[2]}`
         : stack.stackTemplateAssetObjectUrl;
+    Logger.debug("Building CloudFormation with environment details", env.resolvedEnvironment);
     return {
         StackName: stack.stackName,
+        StackRegion: env.resolvedEnvironment.region,
         TemplateURL: templateUrl,
         RoleARN: executionRoleArn,
         //TemplateBody: bodyParameter.TemplateBody,
@@ -258,7 +262,10 @@ async function buildCloudFormationStackParams(deployment, stack, cdkOptions) {
 }
 async function getCloudFormationStack(stack) {
     const { CloudFormationClient, DescribeStacksCommand } = await import("@aws-sdk/client-cloudformation");
-    const client = useAWSClient(CloudFormationClient);
+    Logger.debug("Getting CloudFormation stack details", stack.stackName, stack.environment.region);
+    const client = new CloudFormationClient({
+        region: stack.environment.region,
+    });
     try {
         const { Stacks: stacks } = await client.send(new DescribeStacksCommand({
             StackName: stack.stackName,
@@ -280,17 +287,30 @@ async function getCloudFormationStack(stack) {
 }
 async function createCloudFormationStack(input) {
     const { CloudFormationClient, CreateStackCommand } = await import("@aws-sdk/client-cloudformation");
-    const client = useAWSClient(CloudFormationClient);
-    await client.send(new CreateStackCommand(input));
+    Logger.debug("Creating CloudFormation stack", input);
+    const client = new CloudFormationClient({
+        region: input.StackRegion,
+    });
+    // Remove StackRegion from input as it's not a valid CloudFormation parameter
+    const { StackRegion, ...cfnInput } = input;
+    await client.send(new CreateStackCommand(cfnInput));
 }
 async function updateCloudFormationStack(input) {
     const { CloudFormationClient, UpdateStackCommand } = await import("@aws-sdk/client-cloudformation");
-    const client = useAWSClient(CloudFormationClient);
-    await client.send(new UpdateStackCommand(input));
+    Logger.debug("Updating CloudFormation stack", input);
+    const client = new CloudFormationClient({
+        region: input.StackRegion,
+    });
+    // Remove StackRegion from input as it's not a valid CloudFormation parameter
+    const { StackRegion, ...cfnInput } = input;
+    await client.send(new UpdateStackCommand(cfnInput));
 }
-async function deleteCloudFormationStack(stackName) {
+async function deleteCloudFormationStack(stackName, region) {
     const { CloudFormationClient, DescribeStacksCommand, DeleteStackCommand } = await import("@aws-sdk/client-cloudformation");
-    const client = useAWSClient(CloudFormationClient);
+    Logger.debug("Deleting CloudFormation stack", stackName, region);
+    const client = new CloudFormationClient({
+        region: region,
+    });
     try {
         await client.send(new DeleteStackCommand({ StackName: stackName }));
         while (true) {

This issue body was partially generated by patch-package.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions