Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/v1.15/BUG FIXES-20260106-120000.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: BUG FIXES
body: 'backend/s3: `AWS_USE_FIPS_ENDPOINT` and `AWS_USE_DUALSTACK_ENDPOINT` environment variables are now properly parsed as booleans, allowing values like "false" to correctly disable FIPS/DualStack endpoints'
time: 2026-01-06T12:00:00.000000Z
custom:
Issue: "37601"

11 changes: 7 additions & 4 deletions internal/backend/remote-state/s3/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"
"os"
"regexp"
"strconv"
"strings"
"time"
"unicode"
Expand Down Expand Up @@ -1331,14 +1332,16 @@ 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 in any of the provided environment variables. Environment variable
// values are parsed using strconv.ParseBool. Invalid values are ignored.
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 b, err := strconv.ParseBool(v); err == nil {
return b, true
}
}
}
return false, false
Expand Down
82 changes: 82 additions & 0 deletions internal/backend/remote-state/s3/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3963,3 +3963,85 @@ func objectLockPreCheck(t *testing.T) {
t.Skip("s3 backend tests using object lock enabled buckets require setting TF_S3_OBJECT_LOCK_TEST")
}
}

func TestBoolAttrDefaultEnvVarOk(t *testing.T) {
cases := map[string]struct {
envValue string
expectedBool bool
expectedOk bool
}{
"true": {
envValue: "true",
expectedBool: true,
expectedOk: true,
},
"TRUE": {
envValue: "TRUE",
expectedBool: true,
expectedOk: true,
},
"True": {
envValue: "True",
expectedBool: true,
expectedOk: true,
},
"1": {
envValue: "1",
expectedBool: true,
expectedOk: true,
},
"false": {
envValue: "false",
expectedBool: false,
expectedOk: true,
},
"FALSE": {
envValue: "FALSE",
expectedBool: false,
expectedOk: true,
},
"False": {
envValue: "False",
expectedBool: false,
expectedOk: true,
},
"0": {
envValue: "0",
expectedBool: false,
expectedOk: true,
},
"invalid value": {
envValue: "invalid",
expectedBool: false,
expectedOk: false,
},
"empty string": {
envValue: "",
expectedBool: false,
expectedOk: false,
},
}

for name, tc := range cases {
t.Run(name, func(t *testing.T) {
servicemocks.StashEnv(t)

if tc.envValue != "" {
os.Setenv("TEST_BOOL_ENV_VAR", tc.envValue)
}

obj := cty.ObjectVal(map[string]cty.Value{
"test_bool": cty.NullVal(cty.Bool),
})

gotBool, gotOk := boolAttrDefaultEnvVarOk(obj, "test_bool", "TEST_BOOL_ENV_VAR")

if gotBool != tc.expectedBool {
t.Errorf("expected bool %v, got %v", tc.expectedBool, gotBool)
}
if gotOk != tc.expectedOk {
t.Errorf("expected ok %v, got %v", tc.expectedOk, gotOk)
}
})
}
}
Loading