Merge pull request #37940 from vietcgi/fix/s3-backend-fips-env-var-parsing
Some checks failed
build / Determine intended Terraform version (push) Has been cancelled
build / Determine Go toolchain version (push) Has been cancelled
Quick Checks / Unit Tests (push) Has been cancelled
Quick Checks / Race Tests (push) Has been cancelled
Quick Checks / End-to-end Tests (push) Has been cancelled
Quick Checks / Code Consistency Checks (push) Has been cancelled
build / Generate release metadata (push) Has been cancelled
build / Build for freebsd_386 (push) Has been cancelled
build / Build for linux_386 (push) Has been cancelled
build / Build for openbsd_386 (push) Has been cancelled
build / Build for windows_386 (push) Has been cancelled
build / Build for darwin_amd64 (push) Has been cancelled
build / Build for freebsd_amd64 (push) Has been cancelled
build / Build for linux_amd64 (push) Has been cancelled
build / Build for openbsd_amd64 (push) Has been cancelled
build / Build for solaris_amd64 (push) Has been cancelled
build / Build for windows_amd64 (push) Has been cancelled
build / Build for freebsd_arm (push) Has been cancelled
build / Build for linux_arm (push) Has been cancelled
build / Build for darwin_arm64 (push) Has been cancelled
build / Build for linux_arm64 (push) Has been cancelled
build / Build for windows_arm64 (push) Has been cancelled
build / Build Docker image for linux_386 (push) Has been cancelled
build / Build Docker image for linux_amd64 (push) Has been cancelled
build / Build Docker image for linux_arm (push) Has been cancelled
build / Build Docker image for linux_arm64 (push) Has been cancelled
build / Build e2etest for linux_386 (push) Has been cancelled
build / Build e2etest for windows_386 (push) Has been cancelled
build / Build e2etest for darwin_amd64 (push) Has been cancelled
build / Build e2etest for linux_amd64 (push) Has been cancelled
build / Build e2etest for windows_amd64 (push) Has been cancelled
build / Build e2etest for linux_arm (push) Has been cancelled
build / Build e2etest for darwin_arm64 (push) Has been cancelled
build / Build e2etest for linux_arm64 (push) Has been cancelled
build / Run e2e test for linux_386 (push) Has been cancelled
build / Run e2e test for windows_386 (push) Has been cancelled
build / Run e2e test for darwin_amd64 (push) Has been cancelled
build / Run e2e test for linux_amd64 (push) Has been cancelled
build / Run e2e test for windows_amd64 (push) Has been cancelled
build / Run e2e test for linux_arm (push) Has been cancelled
build / Run e2e test for linux_arm64 (push) Has been cancelled
build / Run terraform-exec test for linux amd64 (push) Has been cancelled

fix: parse boolean env vars in S3 backend correctly
This commit is contained in:
Jared Baker 2026-01-08 09:19:59 -05:00 committed by GitHub
commit 0b9a20c7c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 138 additions and 4 deletions

View file

@ -0,0 +1,5 @@
kind: UPGRADE NOTES
body: 'backend/s3: The `AWS_USE_FIPS_ENDPOINT` and `AWS_USE_DUALSTACK_ENDPOINT` environment variables now only respect `true` or `false` values, aligning with the AWS SDK for Go. This replaces the previous behavior which treated any non-empty value as `true`.'
time: 2026-01-07T15:45:15.958679-05:00
custom:
Issue: "37601"

View file

@ -1331,14 +1331,21 @@ func boolAttrOk(obj cty.Value, name string) (bool, bool) {
}
}
// boolAttrDefaultEnvVarOk checks for a configured bool argument or a non-empty
// value in any of the provided environment variables. If any of the environment
// variables are non-empty, to boolean is considered true.
// boolAttrDefaultEnvVarOk checks for a configured bool argument or a boolean
// value ("true" or "false", case-insensitive) in any of the provided environment
// variables. Invalid values are treated as unset.
func boolAttrDefaultEnvVarOk(obj cty.Value, name string, envvars ...string) (bool, bool) {
if val := obj.GetAttr(name); val.IsNull() {
for _, envvar := range envvars {
if v := os.Getenv(envvar); v != "" {
return true, true
if strings.EqualFold(v, "true") {
return true, true
}
if strings.EqualFold(v, "false") {
return false, true
}
// Invalid boolean value, treat as unset
return false, false
}
}
return false, false

View file

@ -3963,3 +3963,125 @@ func objectLockPreCheck(t *testing.T) {
t.Skip("s3 backend tests using object lock enabled buckets require setting TF_S3_OBJECT_LOCK_TEST")
}
}
// TestBoolAttrDefaultEnvVarOk tests the boolAttrDefaultEnvVarOk helper function
// which reads boolean values from environment variables. This is a regression
// test for https://github.com/hashicorp/terraform/issues/37601 where setting
// AWS_USE_FIPS_ENDPOINT=false incorrectly enabled FIPS endpoints.
func TestBoolAttrDefaultEnvVarOk(t *testing.T) {
testCases := map[string]struct {
attrValue cty.Value
envValue string
wantBool bool
wantOk bool
}{
"attr set true": {
attrValue: cty.BoolVal(true),
envValue: "",
wantBool: true,
wantOk: true,
},
"attr set false": {
attrValue: cty.BoolVal(false),
envValue: "",
wantBool: false,
wantOk: true,
},
"attr null, env true": {
attrValue: cty.NullVal(cty.Bool),
envValue: "true",
wantBool: true,
wantOk: true,
},
"attr null, env TRUE": {
attrValue: cty.NullVal(cty.Bool),
envValue: "TRUE",
wantBool: true,
wantOk: true,
},
"attr null, env True": {
attrValue: cty.NullVal(cty.Bool),
envValue: "True",
wantBool: true,
wantOk: true,
},
"attr null, env false": {
attrValue: cty.NullVal(cty.Bool),
envValue: "false",
wantBool: false,
wantOk: true,
},
"attr null, env FALSE": {
attrValue: cty.NullVal(cty.Bool),
envValue: "FALSE",
wantBool: false,
wantOk: true,
},
"attr null, env False": {
attrValue: cty.NullVal(cty.Bool),
envValue: "False",
wantBool: false,
wantOk: true,
},
"attr null, env empty": {
attrValue: cty.NullVal(cty.Bool),
envValue: "",
wantBool: false,
wantOk: false,
},
"attr null, env invalid": {
attrValue: cty.NullVal(cty.Bool),
envValue: "invalid",
wantBool: false,
wantOk: false,
},
"attr null, env yes": {
attrValue: cty.NullVal(cty.Bool),
envValue: "yes",
wantBool: false,
wantOk: false,
},
"attr null, env 1": {
attrValue: cty.NullVal(cty.Bool),
envValue: "1",
wantBool: false,
wantOk: false,
},
"attr null, env 0": {
attrValue: cty.NullVal(cty.Bool),
envValue: "0",
wantBool: false,
wantOk: false,
},
"attr takes precedence over env": {
attrValue: cty.BoolVal(false),
envValue: "true",
wantBool: false,
wantOk: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
servicemocks.StashEnv(t)
const testEnvVar = "TF_TEST_BOOL_ENV_VAR"
if tc.envValue != "" {
os.Setenv(testEnvVar, tc.envValue)
}
obj := cty.ObjectVal(map[string]cty.Value{
"test_attr": tc.attrValue,
})
gotBool, gotOk := boolAttrDefaultEnvVarOk(obj, "test_attr", testEnvVar)
if gotBool != tc.wantBool {
t.Errorf("got bool %v, want %v", gotBool, tc.wantBool)
}
if gotOk != tc.wantOk {
t.Errorf("got ok %v, want %v", gotOk, tc.wantOk)
}
})
}
}