In Pulumi, what happens when you change the string logical name passed to a resource constructor — for example renaming `new aws.s3.Bucket("assets")` to `"assets-bucket"` — and how do you make that rename without destroying the resource?
answer
- the name string is identity, not a label
- URN is the key into state
- rename reads as delete plus create
- aliases option maps the old identity
- index-derived names shift on insert
basics
~20 sThe logical name is part of the resource's URN, which is its identity in state. Renaming it makes Pulumi see one resource deleted and a different one created. To rename safely, add an aliases resource option naming the old identity so the engine maps the old URN to the new one.
solid answer
~50 sThat string is not cosmetic — together with the stack, project, type and parent chain it forms the URN, and the URN is how Pulumi matches the resource in your program to the resource in state. Change it and the engine finds no match for the old URN and no state entry for the new one, so the preview shows a delete plus a create. For a stateful resource that is data loss. The fix is the `aliases` resource option: `{ aliases: [{ name: "assets" }] }` tells the engine that the resource formerly known by that name is this one, so it updates in place instead. Aliases also cover the other identity changes — moving a resource under a ComponentResource parent, or renaming the component itself, both change child URNs and both need an alias. The trap that bites hardest is a loop deriving names from an array index: inserting an element renames everything after it and replaces the lot.
code
typescript · 8 linesimport * as aws from "@pulumi/aws";
// Renaming the logical name from "assets" to "assets-bucket" would normally
// plan a delete + create; the alias preserves the identity.
const bucket = new aws.s3.Bucket("assets-bucket", {}, {
aliases: [{ name: "assets" }],
protect: true,
});go deeper
Know that the string in new aws.s3.Bucket("assets") is the resource's identity, not a label, and that changing it makes Pulumi plan a delete and a create.
Explain the URN — stack, project, type, parent chain and name — and how the aliases resource option maps an old identity onto the renamed resource so it updates in place.
Demonstrate the operational habits: read the replace and delete lines in every preview, protect stateful resources, and carry aliases through re-parenting refactors until every stack has been rolled forward.
Own the systemic risk — in a general-purpose language, resource identity is derived by code that no type checker validates, so naming conventions, protect on stateful resources and a preview-diffing gate in CI are policy decisions, not personal discipline.
## URN as identity Every Pulumi resource has a URN of roughly the shape `urn:pulumi:stack::project::type::name` — extended with the parent chain when a resource is nested. That URN is the primary key linking three things: the resource declared in your program, the entry recorded in state, and the object in the cloud. The `name` component is the first argument to the constructor. It is *logical*: distinct from the physical name in the cloud, which is either what you set explicitly in the args or one Pulumi auto-generates by appending a random suffix to the logical name. So the string you thought was a variable label is the identity of the resource. ## What a rename does When the engine walks your program it looks up each registered resource by URN in the state snapshot. After a rename: - The new URN has no state entry, so the resource is planned as a **create**. - The old URN is in state but no longer registered by the program, so it is planned as a **delete**. The preview shows both. For a stateless resource — a security group rule, an IAM policy attachment — this is mildly wasteful. For an RDS instance, an S3 bucket with objects, or a persistent disk, it is destruction of production data dressed up as a refactor. This is exactly the class of change that gets waved through in review because the code diff looks like a tidy-up. A useful side note: auto-naming is what makes the create-before-delete even possible without a name collision, because the replacement gets a different physical name. If you pinned the physical name explicitly, you may instead get a hard failure mid-deploy — a resource already exists error — which is at least louder. ## Aliases, the intended fix The `aliases` resource option lists prior identities: ```typescript const bucket = new aws.s3.Bucket("assets-bucket", {}, { aliases: [{ name: "assets" }], }); ``` On the next preview the engine matches the old URN through the alias and reports an update or no change rather than a replace. An alias entry can also specify `type`, `parent` or a full `urn`, which covers the less obvious identity changes: - **Adopting into a component.** Wrapping existing resources in a `ComponentResource` re-parents them, and the parent is part of the URN, so every child needs `aliases: [{ parent: pulumi.rootStackResource }]` or an explicit old URN. This is the most common reason a "pure refactor" into components proposes to delete the estate. - **Renaming the component.** The component's own name is in its children's URNs, so renaming the component renames all of them. A good practice is to keep aliases in place for at least a deployment cycle across all stacks, then remove them once every stack has been updated — an alias only helps a stack whose state still holds the old URN. ## The loop trap The worst version of this problem is not a deliberate rename at all: ```typescript const subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]; subnets.forEach((cidr, i) => new aws.ec2.Subnet(`subnet-${i}`, { vpcId: vpc.id, cidrBlock: cidr, })); ``` Insert a new CIDR at the front and every subnet shifts index: `subnet-0` now means a different CIDR, `subnet-2` is new, and the engine plans updates or replacements across the whole set. The remedy is the same one every IaC tool converges on — derive the logical name from something stable about the item rather than its position: ```typescript for (const cidr of subnets) { new aws.ec2.Subnet(`subnet-${cidr.replace(/[./]/g, "-")}`, { vpcId: vpc.id, cidrBlock: cidr }); } ``` Because a Pulumi program is real code, nothing stops you writing the index version; the language will not warn you. That freedom is the model's strength and this is its sharpest edge. ## Guardrails - **Read the preview, particularly the replace and delete lines.** A rename that is safe and a rename that destroys a database look identical in the source diff and completely different in the preview. - **`protect: true`** on resources that must never be deleted turns an accidental destroy into a refused operation. Removing protection is then a deliberate, visible act. - **`retainOnDelete: true`** is the softer variant: Pulumi forgets the resource without asking the provider to delete it. Useful during a migration, dangerous as a habit because it leaks untracked infrastructure. - If a resource has already been destroyed and recreated by accident, the recovery path is `pulumi import` to bring an existing cloud object back under management — with the *correct* logical name this time.
- Why does wrapping existing resources in a ComponentResource propose to destroy them?Because the parent chain is part of the URN. Re-parenting a resource under a component gives it a new URN, so the engine sees the old one gone and a new one appearing. Each adopted child needs an alias naming its previous parent — the root stack resource, or an explicit old URN — for the refactor to be a no-op.
- How long should an alias stay in the code?Until every stack that held the old URN has been deployed at least once with the alias present. An alias only helps a state file still carrying the old identity, so it is dead code afterwards — but removing it before the last stack is migrated re-arms the same destroy.
- What is the difference between the logical name and the resource's name in the cloud?The logical name is the constructor's first argument and lives only in the URN. The physical name is what the provider sees — either set explicitly in the args or auto-generated by Pulumi as the logical name plus a random suffix. Auto-naming is what lets a replacement be created before the original is deleted without a name collision.
saying these in an interview costs you the question
- Treats the constructor's name argument as a comment
- Believes matching physical names keep identity across a rename
- Renames resources in a refactor without reading the preview
- Names looped resources by array index
- Thinks state surgery is the only way to rename