Skip to content

Commit 70a1fd5

Browse files
authored
Fix diagnostic & avoid variable override via environment (#36435)
* add changie entry * add test * Fix diagnostic & avoid variable override via environment
1 parent 5508e9e commit 70a1fd5

File tree

4 files changed

+113
-10
lines changed

4 files changed

+113
-10
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
kind: BUG FIXES
2+
body: Attempting to override a variable during `apply` via `TF_VAR_` environment variable will now yield warning instead of misleading error.
3+
time: 2025-02-05T12:53:26.000+00:00
4+
custom:
5+
Issue: "36435"

internal/backend/local/backend_apply.go

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -340,16 +340,49 @@ func (b *Local) opApply(
340340
})
341341
} else {
342342
// The user can't override the planned variables, so we
343-
// error when possible to avoid confusion. If the parsed
344-
// variables comes from an auto-file however, it's not input
345-
// directly by the user so we have to ignore it.
346-
if parsedVar.Value.Equals(plannedVar).False() && parsedVar.SourceType != terraform.ValueFromAutoFile {
347-
diags = diags.Append(&hcl.Diagnostic{
348-
Severity: hcl.DiagError,
349-
Summary: "Can't change variable when applying a saved plan",
350-
Detail: fmt.Sprintf("The variable %s cannot be set using the -var and -var-file options when applying a saved plan file, because a saved plan includes the variable values that were set when it was created. The saved plan specifies %s as the value whereas during apply the value %s was %s. To declare an ephemeral variable which is not saved in the plan file, use ephemeral = true.", varName, tfdiags.CompactValueStr(parsedVar.Value), tfdiags.CompactValueStr(plannedVar), parsedVar.SourceType.DiagnosticLabel()),
351-
Subject: rng,
352-
})
343+
// error when possible to avoid confusion.
344+
if parsedVar.Value.Equals(plannedVar).False() {
345+
switch parsedVar.SourceType {
346+
case terraform.ValueFromAutoFile:
347+
// If the parsed variables comes from an auto-file,
348+
// it's not input directly by the user so we have to ignore it.
349+
continue
350+
case terraform.ValueFromEnvVar:
351+
diags = diags.Append(&hcl.Diagnostic{
352+
Severity: hcl.DiagWarning,
353+
Summary: "Ignoring variable when applying a saved plan",
354+
Detail: fmt.Sprintf("The variable %s cannot be overriden when applying a saved plan file, "+
355+
"because a saved plan includes the variable values that were set when it was created. "+
356+
"The saved plan specifies %s as the value whereas during apply the value %s was %s. "+
357+
"To declare an ephemeral variable which is not saved in the plan file, use ephemeral = true.",
358+
varName, tfdiags.CompactValueStr(plannedVar), tfdiags.CompactValueStr(parsedVar.Value),
359+
parsedVar.SourceType.DiagnosticLabel()),
360+
Subject: rng,
361+
})
362+
case terraform.ValueFromCLIArg, terraform.ValueFromNamedFile:
363+
diags = diags.Append(&hcl.Diagnostic{
364+
Severity: hcl.DiagError,
365+
Summary: "Can't change variable when applying a saved plan",
366+
Detail: fmt.Sprintf("The variable %s cannot be set using the -var and -var-file options when "+
367+
"applying a saved plan file, because a saved plan includes the variable values that were "+
368+
"set when it was created. The saved plan specifies %s as the value whereas during apply "+
369+
"the value %s was %s. To declare an ephemeral variable which is not saved in the plan "+
370+
"file, use ephemeral = true.",
371+
varName, tfdiags.CompactValueStr(plannedVar), tfdiags.CompactValueStr(parsedVar.Value),
372+
parsedVar.SourceType.DiagnosticLabel()),
373+
Subject: rng,
374+
})
375+
default:
376+
// Other SourceTypes should never reach this point because
377+
// - ValueFromConfig - supplied plan already contains the original configuration
378+
// - ValueFromInput - we disable prompt when plan file is supplied
379+
// - ValueFromCaller - only used in tests
380+
panic(fmt.Sprintf("Attempted to change variable %s when applying a saved plan. "+
381+
"The saved plan specifies %s as the value whereas during apply the value %s was %s. "+
382+
"This is a bug in Terraform, please report it.",
383+
varName, tfdiags.CompactValueStr(plannedVar), tfdiags.CompactValueStr(parsedVar.Value),
384+
parsedVar.SourceType.DiagnosticLabel()))
385+
}
353386
}
354387
}
355388
}

internal/command/apply_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,64 @@ func TestApply_planUndeclaredVars(t *testing.T) {
994994
}
995995
}
996996

997+
func TestApply_planWithEnvVars(t *testing.T) {
998+
_, snap := testModuleWithSnapshot(t, "apply-output-only")
999+
plan := testPlan(t)
1000+
1001+
addr, diags := addrs.ParseAbsOutputValueStr("output.shadow")
1002+
if diags.HasErrors() {
1003+
t.Fatal(diags.Err())
1004+
}
1005+
1006+
shadowVal := mustNewDynamicValue("noot", cty.DynamicPseudoType)
1007+
plan.VariableValues = map[string]plans.DynamicValue{
1008+
"shadow": shadowVal,
1009+
}
1010+
plan.Changes.Outputs = append(plan.Changes.Outputs, &plans.OutputChangeSrc{
1011+
Addr: addr,
1012+
ChangeSrc: plans.ChangeSrc{
1013+
Action: plans.Create,
1014+
After: shadowVal,
1015+
},
1016+
})
1017+
planPath := testPlanFileMatchState(
1018+
t,
1019+
snap,
1020+
states.NewState(),
1021+
plan,
1022+
statemgr.SnapshotMeta{},
1023+
)
1024+
1025+
statePath := testTempFile(t)
1026+
1027+
p := applyFixtureProvider()
1028+
view, done := testView(t)
1029+
c := &ApplyCommand{
1030+
Meta: Meta{
1031+
testingOverrides: metaOverridesForProvider(p),
1032+
View: view,
1033+
},
1034+
}
1035+
1036+
t.Setenv("TF_VAR_shadow", "env")
1037+
1038+
args := []string{
1039+
"-state", statePath,
1040+
"-no-color",
1041+
planPath,
1042+
}
1043+
code := c.Run(args)
1044+
output := done(t)
1045+
if code != 0 {
1046+
t.Fatal("unexpected failure: ", output.All())
1047+
}
1048+
1049+
expectedWarn := "Warning: Ignoring variable when applying a saved plan\n"
1050+
if !strings.Contains(output.Stdout(), expectedWarn) {
1051+
t.Fatalf("expected warning in output, given: %q", output.Stdout())
1052+
}
1053+
}
1054+
9971055
// A saved plan includes a list of "apply-time variables", i.e. ephemeral
9981056
// input variables that were set during the plan, and must therefore be set
9991057
// during apply. No other variables may be set during apply.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
variable "shadow" {
2+
type = string
3+
}
4+
5+
output "foo" {
6+
value = var.shadow
7+
}

0 commit comments

Comments
 (0)