> ## Documentation Index
> Fetch the complete documentation index at: https://cdktn.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Refactoring - CDK Terrain

> Refactor stacks as your CDKTN application grows in size and complexity.

You may need to move resources between stacks as your CDK Terrain (CDKTN) application grows in size and complexity. This guide demonstrates how to refactor your stacks. In general you can change the names of stacks freely without an effect on the synthesized code. If you change the name of a resource this might lead to a re-creation of the resource, especially if you move the resource from one stack to another.

## Moving & Renaming Resources Within a Stack

You may want to rename a deployed resource in a stack, like the `S3Bucket` in the following example.

```typescript theme={null}
import { Construct } from "constructs";
import { App, TerraformStack } from "cdktn";
import { AwsProvider } from "@cdktn/provider-aws/lib/provider";
import { S3Bucket } from "@cdktn/provider-aws/lib/s3-bucket";

class MyStack extends TerraformStack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    new AwsProvider(this, "aws", {
      region: "us-east-1",
    });

    // old S3Bucket definition
    // new S3Bucket(this, "my-bucket", {
    //   bucketprefix: "my-bucket",
    // });

    // new S3Bucket definition
    new S3Bucket(this, "new-bucket", {
      bucketprefix: "my-bucket",
    });
  }
}

const app = new App();
new MyStack(app, "refactoring-example");
app.synth();
```

When you run a `cdktn plan`, the CLI may display the following output:

```shell-session theme={null}
refactoring-example    # aws_s3_bucket.my-bucket will be destroyed
                       # (because aws_s3_bucket.my-bucket is not in configuration)
                       - resource "aws_s3_bucket" "my-bucket" {
...
refactoring-example    # aws_s3_bucket.new-bucket (new-bucket) will be created
                       + resource "aws_s3_bucket" "new-bucket" {
```

Terraform (or OpenTofu) plans to destroy the old bucket and create a new one to replace it. The replacement can be harmful if there is already state in the resource, such as files in the bucket or data in the database.

To avoid this recreation behavior, use one of the move instance functions (`moveTo`, `moveToId`, `moveFromId`) available on the resource.

```typescript theme={null}
import { Construct } from "constructs";
import { App, TerraformStack } from "cdktn";
import { AwsProvider } from "@cdktn/provider-aws/lib/provider";
import { S3Bucket } from "@cdktn/provider-aws/lib/s3-bucket";

class MyStack extends TerraformStack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    new AwsProvider(this, "aws", {
      region: "us-east-1",
    });

    // old S3Bucket definition
    new S3Bucket(this, "my-bucket", {
      bucketprefix: "my-bucket",
    }).moveTo("new-s3-bucket");

    // new S3Bucket definition
    new S3Bucket(this, "new-bucket", {
      bucketprefix: "my-bucket",
    }).addMoveTarget("new-s3-bucket");
  }
}
```

### Performing Resource Moves

The `moveTo` function is available on all resources and is used for relocating a resource to the location specified by the string target `moveTarget`. To set a resource's `moveTarget`, use the `addMoveTarget` function that is present on the resource to move to. You can specify an arbitrary string for the `moveTarget`, but it must be unique within your stack.

```ts theme={null}
new S3Bucket(this, "test-bucket-move-to", {
  bucket: "move-bucket-name",
}).addMoveTarget("move-s3");

new S3Bucket(this, "test-bucket-move-from", {
  bucket: "move-bucket-name",
}).moveTo("move-s3");
```

After deployment, both the resource being moved and the move target on the destination resource can be removed.

#### Move Targets

A `moveTarget` is accessible anywhere within the context of the stack where it is created, including the root of the stack and within a nested construct. This workflow does not support moving resources to a different stack.

```ts theme={null}
new S3Bucket(this, "test-bucket-1", {
  bucket: "test-move-bucket-name-1",
}).moveTo("move");

// Contains resource with moveTarget "move"
new NestedConstructToMoveTo(this, "construct-to-move-to");
```

### Enabling `foreach` on a Resource

To incorporate a deployed resource into a `foreach` composition without destroying the resource, specify an index as a second argument in the `moveTo` function. The index should correspond to the key in the `TerraformIterator` named `iterator`.

```ts theme={null}
const iterator = TerraformIterator.fromMap({
  "website-static-files": {
    name: "website-static-files",
    tags: { app: "website" },
  },
  images: { name: "images", tags: { app: "image-converter" } },
});

new S3Bucket(this, "iterator-bucket", {
  forEach: iterator,
  bucket: iterator.getString("name"),
  tags: iterator.getStringMap("tags"),
}).addMoveTarget("resourceWithIterator");

new S3Bucket(this, "moved-bucket", {
  bucket: "website-static-files",
  tags: { app: "website" },
}).moveTo("resourceWithIterator", "website-static-files");
```

This allows you to efficiently integrate existing resources into a `foreach` composition without destroying the existing deployed resource.

### Move By Resource Address

In instances where the move target workflow does not easily fit your use case, resource moves can be performed by directly specifying the full resource address to be moved to/from.

#### Move To

```typescript theme={null}
new S3Bucket(this, "bucket-moved", {
  bucket: "move-bucket",
}).moveToId("aws_s3_bucket.bucket-new");

new S3Bucket(this, "bucket-new", {
  bucket: "move-bucket",
});
```

#### Move From

```typescript theme={null}
// Old resource
// new S3Bucket(this, "bucket-moved", {
//   bucket: "move-bucket",
// });

new S3Bucket(this, "bucket-new", {
  bucket: "move-bucket",
}).moveFromId("aws_s3_bucket.bucket-moved");
```

Note that in moving from a specified resource address, the original resource being moved from must be removed.

#### Nested Constructs

Resource addresses in the context of nested constructs will not simply be the specified id given to the resource. When dealing with moving resources by their address to/from nested constructs, run `cdktn synth` and refer to `cdktf.out/stacks/'your stack name'` to find the exact address to move to/from.

```typescript theme={null}
new S3Bucket(this, "bucket-moved", {
  bucket: "bucket-moved",
}).moveToId("aws_s3_bucket.nested-construct_bucket-new_5A0C9225"); // moving to S3Bucket "bucket-new" in NestedConstruct "nested-construct"

new NestedConstruct(this, "nested-construct", {});
```

## Moving or renaming modules

To change the id of a module without losing its state use the `terraform state mv` command. This command needs to be run in the output directory of the stack that contains the module. Commonly this is `cdktf.out/stacks/<stack-name>`. The command takes two arguments, the first is the current id of the module, the second is the new id of the module.

```shell-session theme={null}
$ cd cdktf.out/stacks/refactoring-example
$ terraform state mv module.my-module module.my-new-module
```

For further details on the `terraform state mv` command refer to the [Terraform documentation](/terraform/cli/commands/state/mv).

## Moving Resources From One Stack To Another

You may want to move a resource from one stack to another, like the following example aws `S3Bucket` resource.

```typescript theme={null}
import { Construct } from "constructs";
import { App, TerraformStack } from "cdktn";
import { AwsProvider } from "@cdktn/provider-aws/lib/provider";
import { S3Bucket } from "@cdktn/provider-aws/lib/s3-bucket";

class MyFirstStack extends TerraformStack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    new AwsProvider(this, "aws", {
      region: "us-east-1",
    });

    // Old Definition
    // new S3Bucket(this, "my-bucket", {
    //   bucketPrefix: "my-bucket",
    // });
  }
}

class MySecondStack extends TerraformStack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    new AwsProvider(this, "aws", {
      region: "us-east-1",
    });

    // New Definition
    new S3Bucket(this, "my-other-bucket", {
      bucketPrefix: "my-bucket",
    });
  }
}

const app = new App();
new MyFirstStack(app, "refactoring-example-stack-move-1");
new MySecondStack(app, "refactoring-example-stack-move-2");
app.synth();
```

When you run a `cdktn deploy '*'`, the CLI displays the following output.

```shell-session theme={null}
...
refactoring-example-stack-move-1    # aws_s3_bucket.my-bucket will be destroyed
                                    # (because aws_s3_bucket.my-bucket is not in configuration)
                                    - resource "aws_s3_bucket" "my-bucket" {
...
refactoring-example-stack-move-2    # aws_s3_bucket.my-other-bucket (my-other-bucket) will be created
...
```

To preserve the state of the resource, you must move it from one stack to another. You can use [`terraform import`](/terraform/cli/commands/import) and [`terraform state rm`](/terraform/cli/commands/state/rm) to move the resource's state. First we need to find the Terraform name of the Resource in the old stack.

```shell-session theme={null}
$ cat cdktf.out/stacks/refactoring-example-stack-move-1/cdk.tf.json | jq '.resource.aws_s3_bucket'
{
  "my-bucket": {
    "//": {
      "metadata": {
        "path": "refactoring-example-stack-move-1/my-bucket",
        "uniqueId": "my-bucket"
      }
    },
    "bucket_prefix": "my-bucket"
  }
}
```

Now we need to find the name of the resource in the new stack to understand where to move the resource to.

```shell-session theme={null}
$ cdktn synth # To ensure our synthesized stack is up to date
$ cat cdktf.out/stacks/refactoring-example-stack-move-2/cdk.tf.json | jq '.resource.aws_s3_bucket'
{
  "my-other-bucket": {
    "//": {
      "metadata": {
        "path": "refactoring-example-stack-move-2/my-other-bucket",
        "uniqueId": "my-other-bucket"
      }
    },
    "bucket_prefix": "my-other-bucket"
  }
}
```

We need to find the id of the resource, we can use the `terraform state show` command to find it. The id is depenendent on the resource being imported, see [the import docs](/terraform/cli/commands/import#usage) for more details. We need to run these commands in the directory of the stack we are importing the resource from.

```shell-session theme={null}
$ cd cdktf.out/stacks/refactoring-example-stack-move-1/
$ terraform init
$ terraform state show aws_s3_bucket.my-bucket
# aws_s3_bucket.my-bucket:
resource "aws_s3_bucket" "my-bucket" {
    arn                         = "arn:aws:s3:::my-bucket20221208141249058600000001"
    bucket                      = "my-bucket20221208141249058600000001"
    bucket_domain_name          = "my-bucket20221208141249058600000001.s3.amazonaws.com"
    bucket_prefix               = "my-bucket"
    bucket_regional_domain_name = "my-bucket20221208141249058600000001.s3.amazonaws.com"
    force_destroy               = false
    hosted_zone_id              = "Z3AQBSTGFYJSTF"
    id                          = "my-bucket20221208141249058600000001"
    object_lock_enabled         = false
    region                      = "us-east-1"
    request_payer               = "BucketOwner"
    tags_all                    = {}

    grant {
        id          = "554912fda2704333d162d216be50aefb05562e0bf1709997f1d9417cf46087d5"
        permissions = [
            "FULL_CONTROL",
        ]
        type        = "CanonicalUser"
    }

    versioning {
        enabled    = false
        mfa_delete = false
    }
}
```

Now that we have the id (`"my-bucket20221208141249058600000001"`) we can import it. This command needs to be run in the directory of the stack we are importing the resource to.

```shell-session theme={null}
$ cd ../refactoring-example-stack-move-2
$ terraform import aws_s3_bucket.my-other-bucket my-bucket20221208141249058600000001
```

We have imported the resource in the new stack, but we still need to remove it from the old stack. This command needs to be run in the directory of the stack we are removing the resource from.

```shell-session theme={null}
$ cd ../refactoring-example-stack-move-1
$ terraform state rm aws_s3_bucket.my-bucket
```

We can verify everything worked as expected by running a `cdktn deploy '*'` again. We should now see no changes.

```shell-session theme={null}
$ cd ../../.. # Back to the root of the project
# The next deploy should show no changes
$ cdktn deploy '*'
```

### Why Do I Need To Find The Terraform Name Of The Resource?

If you use Constructs to organize your code, you might have a level between the generated provider constructs and the `TerraformStack` construct to organize your code.

This extra level adds a prefix to the name of the resource to ensure uniqueness. Refer to [Constructs](/concepts/constructs#scope) for more details.
