1. Tutorials
  2. Move Resources Between Stacks

Move Resources Between Stacks

There are certain scenarios in which you might need to move resources between different project stacks without recreating them, such as when refactoring a Pulumi project from a monolithic structure to micro-stacks. While it is possible to accomplish this by manually modifying Pulumi state files, doing so requires significant effort, can be error prone, and can be very time consuming.

In this tutorial, you will learn how to move your resources using the pulumi state move command instead.

In this tutorial, you'll learn:

  • How to identify a resource's URN
  • How to move a resource between stacks
  • Why and how to use aliases

Create a new project

To start, login to the Pulumi CLI and ensure it is configured to use your AWS account. Next, create a new project and replace the default program code with the following:

const pulumi = require("@pulumi/pulumi");
const random = require("@pulumi/random");
const aws = require("@pulumi/aws");

const petName = new random.RandomPet("my-pet-name");

const bucket = new aws.s3.Bucket("b");

const index = new aws.s3.BucketObject(
    "index.html",
    {
        bucket: bucket.bucket,
        content: "Thanks for using Pulumi!",
    },
    { parent: bucket },
);

const randomSite = new aws.s3.BucketObject(
    "random.html",
    {
        bucket: bucket.bucket,
        content: petName.id,
    },
    { parent: bucket },
);

exports.PetName = petName.id;
import * as pulumi from "@pulumi/pulumi";
import * as random from "@pulumi/random";
import * as aws from "@pulumi/aws";

const petName = new random.RandomPet("my-pet-name");

const bucket = new aws.s3.Bucket("b");

const index = new aws.s3.BucketObject(
    "index.html",
    {
        bucket: bucket.bucket,
        content: "Thanks for using Pulumi!",
    },
    { parent: bucket },
);

const randomSite = new aws.s3.BucketObject(
    "random.html",
    {
        bucket: bucket.bucket,
        content: petName.id,
    },
    { parent: bucket },
);

export const PetName = petName.id;
import pulumi
import pulumi_random as random
import pulumi_aws as aws

pet_name = random.RandomPet('my-pet-name')

bucket = aws.s3.Bucket("b")

index = aws.s3.BucketObject("index.html",
    bucket=bucket.bucket,
    content="Thanks for using Pulumi!",
    opts=pulumi.ResourceOptions(parent=bucket)
)

random_site = aws.s3.BucketObject("random.html",
    bucket=bucket.bucket,
    content=pet_name.id,
    opts=pulumi.ResourceOptions(parent=bucket)
)

pulumi.export('PetName', pet_name.id)
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/s3"
	"github.com/pulumi/pulumi-random/sdk/v4/go/random"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {

		petName, err := random.NewRandomPet(ctx, "my-pet-name", nil)
		if err != nil {
			return err
		}

		bucket, err := s3.NewBucket(ctx, "b", nil)
		if err != nil {
			return err
		}

		_, err = s3.NewBucketObject(ctx, "index.html", &s3.BucketObjectArgs{
			Bucket:  bucket.ID(),
			Content: pulumi.String("Thanks for using Pulumi!"),
		}, pulumi.Parent(bucket))
		if err != nil {
			return err
		}

		_, err = s3.NewBucketObject(ctx, "random.html", &s3.BucketObjectArgs{
			Bucket:  bucket.ID(),
			Content: petName.ID(),
		}, pulumi.Parent(bucket))
		if err != nil {
			return err
		}

		ctx.Export("PetName", petName.ID())
		return nil
	})
}
using Pulumi;
using Pulumi.Aws.S3;
using Pulumi.Random;
using System.Collections.Generic;

return await Deployment.RunAsync(() =>
{
    var petName = new RandomPet("my-pet-name");

    var bucket = new Bucket("b");

    var index = new BucketObject("index.html", new BucketObjectArgs
    {
        Bucket = bucket.BucketName,
        Content = "Thanks for using Pulumi!"
    }, new CustomResourceOptions { Parent = bucket });

    var randomSite = new BucketObject("random.html", new BucketObjectArgs
    {
        Bucket = bucket.BucketName,
        Content = petName.Id
    }, new CustomResourceOptions { Parent = bucket });

    return new Dictionary<string, object?>
    {
        ["PetName"] = petName.Id
    };
});
name: aws-s3bucket-s3objects-random-yaml
runtime: yaml
description: An example that deploys an S3 bucket, S3 bucket objects, and a Pulumi random resource.

resources:
  pet-name:
    type: random:RandomPet
  my-bucket:
    type: aws:s3:Bucket
  index:
    type: aws:s3:BucketObject
    properties:
      bucket: ${my-bucket.bucket}
      content: "Thanks for using Pulumi!"
    options:
      parent: ${my-bucket}
  random-site:
    type: aws:s3:BucketObject
    properties:
      bucket: ${my-bucket.bucket}
      content: ${pet-name.id}
    options:
      parent: ${my-bucket}

outputs:
  PetName: ${pet-name.id}

This code example creates the following resources:

It also includes one export that will output the name of the Pulumi random object.

Given that this example program makes use of the Pulumi Random provider, you will also need to make sure to install this dependency into your project. Once that is installed, run the pulumi up command to deploy your resources before moving onto the next steps.

Review the pulumi state move command

The pulumi state move command works as follows:

$ pulumi state move --help
Move resources from one stack to another

This command can be used to move resources from one stack to another. This can be useful when
splitting a stack into multiple stacks or when merging multiple stacks into one.

Usage:
  pulumi state move [flags] <urn>...

Flags:
      --dest string       The name of the stack to move resources to
  -h, --help              help for move
      --include-parents   Include all the parents of the moved resources as well
      --source string     The name of the stack to move resources from
  -y, --yes               Automatically approve and perform the move

Both the --dest and --source flags can be either stacks in the current project, or stacks in across different projects. The resources being moved have to be specified by their full Uniform Resource Name (URN), and multiple URNs can be passed at once for scenarios in which you need to move multiple resources at once.

This command will only work for stacks that exist within the same backend. It is currently not possible to move a resource between different backends, but you can move stacks between backends using other existing tools. Please refer to the Migrating Between State Backends documentation for more information.

Move a resource between stacks

Let’s say you have a situation where your program code has grown too large, and you want to group the AWS resources separately from the Random resource. This means you want to move the S3 bucket and its bucket objects to a separate stack. In this section, you will learn how to move these resource into the state file of a different stack.

Create an additional stack

To start, create a second Pulumi project to where you will move your AWS resources.

From within the original project folder, run the pulumi stack ls --all command to verify the existence of your stacks:

$ pulumi stack ls

NAME                                                                LAST UPDATE     RESOURCE COUNT  URL
source*                                                             37 minutes ago  7               https://app.pulumi.com/v-torian-pulumi-corp/pulumi-state-move-tutorial/source
v-torian-pulumi-corp/pulumi-state-move-dest/dest-project            n/a             n/a             https://app.pulumi.com/v-torian-pulumi-corp/pulumi-state-move-dest/dest-project

Retrieve fully qualified stack names

Next, you will need to collect the fully qualified stack names of the source and destination stacks. The format of a stack’s fully qualified name is <organization>/<project>/<stack>. In the URL column of the output in the above section, you can see the value of each stack’s fully qualified name after the https://app.pulumi.com/ part of the URL. An example of a fully qualified stack name is shown below:

v-torian-pulumi-corp/pulumi-state-move-dest/dest-project

Retrieve resource URN

Next, you will need to obtain the full URN of the S3 bucket resource. To do so, run the pulumi stack --show-urns command, and copy the value next to the URN parameter under the aws:s3/bucket:Bucket resource:

$ pulumi stack --show-urns
Current stack is source:
    Owner: v-torian-pulumi-corp
    Last updated: 28 seconds ago (2024-08-30 08:07:56.461286624 +0000 UTC)
    Pulumi version used: v3.130.0
Current stack resources (7):
    TYPE                                    NAME
    pulumi:pulumi:Stack                     pulumi-state-move-tutorial-source
    │  URN: urn:pulumi:source::pulumi-state-move-tutorial::pulumi:pulumi:Stack::pulumi-state-move-tutorial-source
    ├─ random:index/randomPet:RandomPet     my-pet-name
    │     URN: urn:pulumi:source::pulumi-state-move-tutorial::random:index/randomPet:RandomPet::my-pet-name
    ├─ aws:s3/bucket:Bucket                 b
    │  │  URN: urn:pulumi:source::pulumi-state-move-tutorial::aws:s3/bucket:Bucket::b
    │  ├─ aws:s3/bucketObject:BucketObject  index.html
    │  │     URN: urn:pulumi:source::pulumi-state-move-tutorial::aws:s3/bucket:Bucket$aws:s3/bucketObject:BucketObject::index.html
    │  └─ aws:s3/bucketObject:BucketObject  random.html
    │        URN: urn:pulumi:source::pulumi-state-move-tutorial::aws:s3/bucket:Bucket$aws:s3/bucketObject:BucketObject::random.html
    ├─ pulumi:providers:random              default_4_16_3
    │     URN: urn:pulumi:source::pulumi-state-move-tutorial::pulumi:providers:random::default_4_16_3
    └─ pulumi:providers:aws                 default_6_50_1
          URN: urn:pulumi:source::pulumi-state-move-tutorial::pulumi:providers:aws::default_6_50_1

Current stack outputs (1):
    OUTPUT   VALUE
    PetName  main-longhorn

In this scenario, you only need the URN of the S3 bucket resource and not the URNs for the bucket objects. This is because the bucket objects are children of the S3 bucket resource, and when running the pulumi state move command against resources that have children, all of the children of that resource will also be moved.

Move the resource

Now run the following command, making sure to replace <SOURCE_STACK> with the fully qualified stack name of the source stack, <DEST_STACK> with the fully qualified stack name of the destination stack, and <URN> with the value of the S3 bucket URN.

pulumi state move \
--source <SOURCE_STACK> \
--dest <DEST_STACK> \
<URN>

Since URNs can contain characters that get interpreted by the shell, it is always a good idea to wrap them in single quotes (’) when passing them as arguments as shown in the example below:

$ pulumi state move \
--source v-torian-pulumi-corp/pulumi-state-move-tutorial/source \
--dest v-torian-pulumi-corp/pulumi-state-move-dest/dest-project \
'urn:pulumi:source::pulumi-state-move-tutorial::aws:s3/bucket:Bucket::b'

Planning to move the following resources from v-torian-pulumi-corp/pulumi-state-move-tutorial/source to v-torian-pulumi-corp/pulumi-state-move-dest/dest-project:

  - urn:pulumi:source::pulumi-state-move-tutorial::aws:s3/bucket:Bucket::b
  - urn:pulumi:source::pulumi-state-move-tutorial::aws:s3/bucket:Bucket$aws:s3/bucketObject:BucketObject::index.html
  - urn:pulumi:source::pulumi-state-move-tutorial::aws:s3/bucket:Bucket$aws:s3/bucketObject:BucketObject::random.html

The following resources being moved to v-torian-pulumi-corp/pulumi-state-move-dest/dest-project have dependencies on resources in v-torian-pulumi-corp/pulumi-state-move-tutorial/source:

  - urn:pulumi:source::pulumi-state-move-tutorial::aws:s3/bucket:Bucket$aws:s3/bucketObject:BucketObject::random.html has a dependency on urn:pulumi:source::pulumi-state-move-tutorial::random:index/randomPet:RandomPet::my-pet-name
  - urn:pulumi:source::pulumi-state-move-tutorial::aws:s3/bucket:Bucket$aws:s3/bucketObject:BucketObject::random.html (content) has a property dependency on urn:pulumi:source::pulumi-state-move-tutorial::random:index/randomPet:RandomPet::my-pet-name

If you go ahead with moving these dependencies, it will be necessary to create the appropriate inputs and outputs in the program for the stack the resources are moved to.

Do you want to perform this move? yes
Successfully moved resources from v-torian-pulumi-corp/pulumi-state-move-tutorial/source to v-torian-pulumi-corp/pulumi-state-move-dest/dest-project

There are a few things to note about the example above:

  • When moving resources from the currently selected stack, you can omit the --source argument and use only --dest argument.
  • When moving resources between stacks across different projects, you will need to use the fully qualified stack name. When moving resources between stacks within the same project, you can pass just the name of the stack.
  • Before the resources are moved, the command gives a list of resources that will be moved. You will notice that even though you have only provided the URN for the S3 bucket as an argument, the command indicates that both the S3 bucket resource as well as the two child bucket objects will be moved.
  • The output also warns you about any dependencies that will not be transferred to the destination stack. When dealing with cross-project stacks, you will need to create the appropriate inputs and outputs in the program of the destination stack, and you will learn how to do this in the next section.

Update program code

The pulumi state move only modifies the state file of the source and destination stacks. It does not modify the code of your program directly. In the case of moving resources between stacks across different programs, you will need to modify both programs to match the changes you have made. This can typically be accomplished by copy/pasting source code for the resources and/or components between the two program files.

Additionally, inputs and outputs of resources that were moved may need to be adjusted as part of this process. This can be done either by using stack references or by recreating the inputs in the program.

Update source program code

First, start by removing the AWS resources from your source program code. You can copy and paste these resource definitions safely to the side as you will be adding them to the destination program code in a later step. Update your source program code so that it resembles the following:

const pulumi = require("@pulumi/pulumi");
const random = require("@pulumi/random");

const petName = new random.RandomPet("my-pet-name");

exports.PetName = petName.id;
import * as pulumi from "@pulumi/pulumi";
import * as random from "@pulumi/random";

const petName = new random.RandomPet("my-pet-name");

export const PetName = petName.id;
import pulumi
import pulumi_random as random

pet_name = random.RandomPet('my-pet-name')

pulumi.export('PetName', pet_name.id)
package main

import (
	"github.com/pulumi/pulumi-random/sdk/v4/go/random"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {

		petName, err := random.NewRandomPet(ctx, "my-pet-name", nil)
		if err != nil {
			return err
		}

		ctx.Export("PetName", petName.ID())
		return nil
	})
}
using Pulumi;
using Pulumi.Random;
using System.Collections.Generic;

return await Deployment.RunAsync(() =>
{
    var petName = new RandomPet("my-pet-name");

    return new Dictionary<string, object?>
    {
        ["PetName"] = petName.Id
    };
});
name: aws-s3bucket-s3objects-random-yaml
runtime: yaml
description: An example that deploys an S3 bucket, S3 bucket objects, and a Pulumi random resource.

resources:
  pet-name:
    type: random:RandomPet

outputs:
  PetName: ${pet-name.id}

Update destination program code

In your destination program code, you will need to add your AWS resource definitions. Additionally, you will need to add a stack reference to make use of the PetName export from the source program. Update your destination program code so that it resembles the following, making sure to replace the value of the fully qualified stack name with the name of your own source stack:

const pulumi = require("@pulumi/pulumi");
const aws = require("@pulumi/aws");

const stackRef = new pulumi.StackReference(`v-torian-pulumi-corp/pulumi-state-move-tutorial/source`)

const bucket = new aws.s3.Bucket("b");

const index = new aws.s3.BucketObject(
    "index.html",
    {
        bucket: bucket.bucket,
        content: "Thanks for using Pulumi!",
    },
    { parent: bucket },
);

const randomSite = new aws.s3.BucketObject(
    "random.html",
    {
        bucket: bucket.bucket,
        content: stackRef.getOutput("PetName"),
    },
    { parent: bucket },
);
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const stackRef = new pulumi.StackReference(`v-torian-pulumi-corp/pulumi-state-move-tutorial/source`)

const bucket = new aws.s3.Bucket("b");

const index = new aws.s3.BucketObject(
    "index.html",
    {
        bucket: bucket.bucket,
        content: "Thanks for using Pulumi!",
    },
    { parent: bucket },
);

const randomSite = new aws.s3.BucketObject(
    "random.html",
    {
        bucket: bucket.bucket,
        content: stackRef.getOutput("PetName"),
    },
    { parent: bucket },
);
import pulumi
import pulumi_aws as aws

stack_ref = pulumi.StackReference("v-torian-pulumi-corp/pulumi-state-move-tutorial/source")

bucket = aws.s3.Bucket("b")

index = aws.s3.BucketObject("index.html",
    bucket=bucket.bucket,
    content="Thanks for using Pulumi!",
    opts=pulumi.ResourceOptions(parent=bucket)
)

random_site = aws.s3.BucketObject("random.html",
    bucket=bucket.bucket,
    content=stack_ref.get_output("PetName")
    opts=pulumi.ResourceOptions(parent=bucket)
)
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/s3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {

        stackRef, err := pulumi.NewStackReference(ctx, "v-torian-pulumi-corp/pulumi-state-move-tutorial/source", nil)
        if err != nil {
            return err
        }

		bucket, err := s3.NewBucket(ctx, "b", nil)
		if err != nil {
			return err
		}

		_, err = s3.NewBucketObject(ctx, "index.html", &s3.BucketObjectArgs{
			Bucket:  bucket.ID(),
			Content: pulumi.String("Thanks for using Pulumi!"),
		}, pulumi.Parent(bucket))
		if err != nil {
			return err
		}

		_, err = s3.NewBucketObject(ctx, "random.html", &s3.BucketObjectArgs{
			Bucket:  bucket.ID(),
            Content: stackRef.GetOutput("PetName"),
		}, pulumi.Parent(bucket))
		if err != nil {
			return err
		}
	})
}
using Pulumi;
using Pulumi.Aws.S3;
using System.Collections.Generic;

return await Deployment.RunAsync(() =>
{
    var stackRef = new StackReference("v-torian-pulumi-corp/pulumi-state-move-tutorial/source");

    var bucket = new Bucket("b");

    var index = new BucketObject("index.html", new BucketObjectArgs
    {
        Bucket = bucket.BucketName,
        Content = "Thanks for using Pulumi!"
    }, new CustomResourceOptions { Parent = bucket });

    var randomSite = new BucketObject("random.html", new BucketObjectArgs
    {
        Bucket = bucket.BucketName,
        Content = stackRef.GetOutput("PetName")
    }, new CustomResourceOptions { Parent = bucket });
});
name: aws-s3bucket-s3objects-random-yaml
runtime: yaml

resources:
  stack-ref:
    type: pulumi:pulumi:StackReference
    properties:
      name: v-torian-pulumi-corp/pulumi-state-move-tutorial/source
  my-bucket:
    type: aws:s3:Bucket
  index:
    type: aws:s3:BucketObject
    properties:
      bucket: ${my-bucket.bucket}
      content: "Thanks for using Pulumi!"
    options:
      parent: ${my-bucket}
  random-site:
    type: aws:s3:BucketObject
    properties:
      bucket: ${my-bucket.bucket}
      content: ${stack-ref.outputs["PetName"]}
    options:
      parent: ${my-bucket}

Clean-up

Before moving on, tear down the resources that are part of your stack to avoid incurring any charges.

  1. Run pulumi destroy to tear down all resources. You'll be prompted to make sure you really want to delete these resources. A destroy operation may take some time, since Pulumi waits for the resources to finish shutting down before it considers the destroy operation to be complete.
  2. To delete the stack itself, run pulumi stack rm. Note that this command deletes all deployment history from the Pulumi Service.

Next steps

In this tutorial, you created a Pulumi Random resource and AWS S3 resources. You used the pulumi state move command to migrate the AWS resources from the state file of a source project to a destination project. You also updated both the source and destination project code to match the changes made.

To learn more about creating and managing resources in Pulumi, take a look a the following resources: