From fad0c445f3a65c1a0e52d0adb334cd65d69e7a51 Mon Sep 17 00:00:00 2001 From: Luke Chisholm Date: Mon, 15 Sep 2025 15:20:21 -0700 Subject: [PATCH] fix(lib): properly interpolate dependencies in replaceTriggeredBy This change will address the bug identified in https://github.com/hashicorp/terraform-cdk/issues/3196 where using replaceTriggeredBy will improperly generate a tokenized address. --- packages/cdktf/lib/terraform-resource.ts | 12 +++----- packages/cdktf/lib/tfExpression.ts | 28 +++++++++++++++++++ .../test/__snapshots__/resource.test.ts.snap | 4 +-- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/packages/cdktf/lib/terraform-resource.ts b/packages/cdktf/lib/terraform-resource.ts index 675ea7ba29..a0abd8bc5e 100644 --- a/packages/cdktf/lib/terraform-resource.ts +++ b/packages/cdktf/lib/terraform-resource.ts @@ -11,7 +11,7 @@ import { processDynamicAttributesForHcl, } from "./util"; import { ITerraformDependable } from "./terraform-dependable"; -import { ref, dependable } from "./tfExpression"; +import { ref, dependable, replaceTriggeredBy } from "./tfExpression"; import { IResolvable } from "./tokens/resolvable"; import { IInterpolatingParent } from "./terraform-addressable"; import { ITerraformIterator } from "./terraform-iterator"; @@ -78,13 +78,9 @@ export function lifecycleToTerraform( ...lifecycle, ...(lifecycle?.replaceTriggeredBy?.length ? { - replaceTriggeredBy: lifecycle?.replaceTriggeredBy?.map((x) => { - if (typeof x === "string") { - return x; - } else { - return x.fqn; - } - }), + replaceTriggeredBy: lifecycle?.replaceTriggeredBy?.map((x) => + replaceTriggeredBy(x), + ), } : undefined), }; diff --git a/packages/cdktf/lib/tfExpression.ts b/packages/cdktf/lib/tfExpression.ts index f9baf57295..511fae77ec 100644 --- a/packages/cdktf/lib/tfExpression.ts +++ b/packages/cdktf/lib/tfExpression.ts @@ -466,6 +466,33 @@ export function dependable(dependable: ITerraformDependable): string { return Token.asString(new Dependable(dependable)); } +// eslint-disable-next-line jsdoc/require-jsdoc +class ReplaceTriggeredBy extends TFExpression { + constructor(private readonly trigger: ITerraformDependable | string) { + super(trigger); + } + + public resolve(context: IResolveContext) { + context.suppressBraces = true; + + if (typeof this.trigger === "string") { + return context.resolve(this.trigger); + } + return context.resolve(this.trigger.fqn); + } +} + +/** + * Takes either a full resource or a resource property and then + * returns the logical address without braces/interpolation to + * be used in replaceTriggeredBy directives. + */ +export function replaceTriggeredBy( + trigger: ITerraformDependable | string, +): string { + return Token.asString(new ReplaceTriggeredBy(trigger)); +} + export type Expression = | Reference | FunctionCall @@ -473,6 +500,7 @@ export type Expression = | ConditionalExpression | OperatorExpression | Dependable + | ReplaceTriggeredBy | ForExpression | string | string[] diff --git a/packages/cdktf/test/__snapshots__/resource.test.ts.snap b/packages/cdktf/test/__snapshots__/resource.test.ts.snap index 21de4c79f7..dc5d942d90 100644 --- a/packages/cdktf/test/__snapshots__/resource.test.ts.snap +++ b/packages/cdktf/test/__snapshots__/resource.test.ts.snap @@ -456,8 +456,8 @@ exports[`supports resource and attribute references in lifecycle.replaceTriggere "simple": { "lifecycle": { "replace_triggered_by": [ - "\${test_resource.other}", - "\${test_resource.other.string_value}" + "test_resource.other", + "test_resource.other.string_value" ] }, "name": "foo"