Repository: GoogleCloudPlatform/terraform-validator
Pull request #797 (2022-06-28 22:52:59)

Hoisted setting of zone and region into CLI layer
b/234859914


Patch:
diff --git a/cmd/convert.go b/cmd/convert.go
index e5f927dc..7557c98b 100644
--- a/cmd/convert.go
+++ b/cmd/convert.go
@@ -42,6 +42,15 @@ Example:
     --ancestry organization/my-org/folder/my-folder
 `
 
+func multiEnvSearch(ks []string) string {
+	for _, k := range ks {
+		if v := os.Getenv(k); v != "" {
+			return v
+		}
+	}
+	return ""
+}
+
 type convertOptions struct {
 	project           string
 	ancestry          string
@@ -95,11 +104,23 @@ func (o *convertOptions) validateArgs(args []string) error {
 
 func (o *convertOptions) run(plan string) error {
 	ctx := context.Background()
-	ancestryCache := map[string]string{
-		o.project: o.ancestry,
+	ancestryCache := map[string]string{}
+	if o.project != "" {
+		ancestryCache[o.project] = o.ancestry
 	}
+	zone := multiEnvSearch([]string{
+		"GOOGLE_ZONE",
+		"GCLOUD_ZONE",
+		"CLOUDSDK_COMPUTE_ZONE",
+	})
+
+	region := multiEnvSearch([]string{
+		"GOOGLE_REGION",
+		"GCLOUD_REGION",
+		"CLOUDSDK_COMPUTE_REGION",
+	})
 	userAgent := fmt.Sprintf("config-validator-tf/%s", version.BuildVersion())
-	assets, err := o.readPlannedAssets(ctx, plan, o.project, ancestryCache, o.offline, false, o.rootOptions.errorLogger, userAgent)
+	assets, err := o.readPlannedAssets(ctx, plan, o.project, zone, region, ancestryCache, o.offline, false, o.rootOptions.errorLogger, userAgent)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/validate.go b/cmd/validate.go
index aad56257..64732517 100644
--- a/cmd/validate.go
+++ b/cmd/validate.go
@@ -113,11 +113,23 @@ func (o *validateOptions) run(plan string) error {
 	var assets []google.Asset
 	if err := json.Unmarshal(content, &assets); err != nil {
 		var err error
-		ancestryCache := map[string]string{
-			o.project: o.ancestry,
+		ancestryCache := map[string]string{}
+		if o.project != "" {
+			ancestryCache[o.project] = o.ancestry
 		}
 		userAgent := fmt.Sprintf("config-validator-tf/%s", version.BuildVersion())
-		assets, err = o.readPlannedAssets(ctx, plan, o.project, ancestryCache, o.offline, false, o.rootOptions.errorLogger, userAgent)
+		zone := multiEnvSearch([]string{
+			"GOOGLE_ZONE",
+			"GCLOUD_ZONE",
+			"CLOUDSDK_COMPUTE_ZONE",
+		})
+
+		region := multiEnvSearch([]string{
+			"GOOGLE_REGION",
+			"GCLOUD_REGION",
+			"CLOUDSDK_COMPUTE_REGION",
+		})
+		assets, err = o.readPlannedAssets(ctx, plan, o.project, zone, region, ancestryCache, o.offline, false, o.rootOptions.errorLogger, userAgent)
 		if err != nil {
 			return err
 		}
diff --git a/converters/google/resources/getconfig.go b/converters/google/resources/getconfig.go
index 0decea70..60210fca 100644
--- a/converters/google/resources/getconfig.go
+++ b/converters/google/resources/getconfig.go
@@ -7,12 +7,6 @@ import (
 	"github.com/pkg/errors"
 )
 
-// Compatibility shim to let this change happen in two commits.
-// NewConfig better matches golang best practices.
-func GetConfig(ctx context.Context, project string, offline bool, userAgent string) (*Config, error) {
-	return NewConfig(ctx, project, offline, userAgent, nil)
-}
-
 // Return the value of the private userAgent field
 func (c *Config) UserAgent() string {
 	return c.userAgent
@@ -23,9 +17,11 @@ func (c *Config) Client() *http.Client {
 	return c.client
 }
 
-func NewConfig(ctx context.Context, project string, offline bool, userAgent string, client *http.Client) (*Config, error) {
+func NewConfig(ctx context.Context, project, zone, region string, offline bool, userAgent string, client *http.Client) (*Config, error) {
 	cfg := &Config{
 		Project:   project,
+		Zone:      zone,
+		Region:    region,
 		userAgent: userAgent,
 	}
 
@@ -44,18 +40,6 @@ func NewConfig(ctx context.Context, project string, offline bool, userAgent stri
 		"GOOGLE_IMPERSONATE_SERVICE_ACCOUNT",
 	})
 
-	cfg.Zone = multiEnvSearch([]string{
-		"GOOGLE_ZONE",
-		"GCLOUD_ZONE",
-		"CLOUDSDK_COMPUTE_ZONE",
-	})
-
-	cfg.Region = multiEnvSearch([]string{
-		"GOOGLE_REGION",
-		"GCLOUD_REGION",
-		"CLOUDSDK_COMPUTE_REGION",
-	})
-
 	if !offline {
 		ConfigureBasePaths(cfg)
 		if err := cfg.LoadAndValidate(ctx); err != nil {
diff --git a/tfgcv/planned_assets.go b/tfgcv/planned_assets.go
index dc003c05..e141f5ee 100644
--- a/tfgcv/planned_assets.go
+++ b/tfgcv/planned_assets.go
@@ -28,7 +28,7 @@ import (
 	"go.uber.org/zap"
 )
 
-type ReadPlannedAssetsFunc func(ctx context.Context, path, project string, ancestry map[string]string, offline, convertUnchanged bool, errorLogger *zap.Logger, userAgent string) ([]google.Asset, error)
+type ReadPlannedAssetsFunc func(ctx context.Context, path, project, zone, region string, ancestry map[string]string, offline, convertUnchanged bool, errorLogger *zap.Logger, userAgent string) ([]google.Asset, error)
 
 // ReadPlannedAssets extracts CAI assets from a terraform plan file.
 // If ancestry path is provided, it assumes the project is in that path rather
@@ -37,8 +37,8 @@ type ReadPlannedAssetsFunc func(ctx context.Context, path, project string, ances
 // are also reported in the output, otherwise only resources that are going to
 // be changed are reported.
 // It ignores non-supported resources.
-func ReadPlannedAssets(ctx context.Context, path, project string, ancestry map[string]string, offline, convertUnchanged bool, errorLogger *zap.Logger, userAgent string) ([]google.Asset, error) {
-	converter, err := newConverter(ctx, path, project, ancestry, offline, convertUnchanged, errorLogger, userAgent)
+func ReadPlannedAssets(ctx context.Context, path, project, zone, region string, ancestry map[string]string, offline, convertUnchanged bool, errorLogger *zap.Logger, userAgent string) ([]google.Asset, error) {
+	converter, err := newConverter(ctx, path, project, zone, region, ancestry, offline, convertUnchanged, errorLogger, userAgent)
 	if err != nil {
 		return nil, err
 	}
@@ -61,8 +61,8 @@ func ReadPlannedAssets(ctx context.Context, path, project string, ancestry map[s
 	return converter.Assets(), nil
 }
 
-func newConverter(ctx context.Context, path, project string, ancestry map[string]string, offline, convertUnchanged bool, errorLogger *zap.Logger, userAgent string) (*google.Converter, error) {
-	cfg, err := resources.NewConfig(ctx, project, offline, userAgent, nil)
+func newConverter(ctx context.Context, path, project, zone, region string, ancestry map[string]string, offline, convertUnchanged bool, errorLogger *zap.Logger, userAgent string) (*google.Converter, error) {
+	cfg, err := resources.NewConfig(ctx, project, zone, region, offline, userAgent, nil)
 	if err != nil {
 		return nil, errors.Wrap(err, "building google configuration")
 	}


Test patch:
diff --git a/cmd/convert_test.go b/cmd/convert_test.go
index e4b116c3..7748d577 100644
--- a/cmd/convert_test.go
+++ b/cmd/convert_test.go
@@ -3,16 +3,31 @@ package cmd
 import (
 	"context"
 	"encoding/json"
+	"fmt"
 	"io/ioutil"
+	"os"
 	"path"
 	"testing"
 
 	"github.com/GoogleCloudPlatform/terraform-validator/converters/google"
+	"github.com/GoogleCloudPlatform/terraform-validator/version"
+
 	"github.com/stretchr/testify/assert"
 	"go.uber.org/zap"
 )
 
-func testAssets() []google.Asset {
+func resetEnvKeys() []string {
+	return []string{
+		"GOOGLE_ZONE",
+		"GCLOUD_ZONE",
+		"CLOUDSDK_COMPUTE_ZONE",
+		"GOOGLE_REGION",
+		"GCLOUD_REGION",
+		"CLOUDSDK_COMPUTE_REGION",
+	}
+}
+
+func testAssets(path, project, zone, region string, ancestry map[string]string, offline, convertUnchanged bool, errorLogger *zap.Logger, userAgent string) []google.Asset {
 	return []google.Asset{
 		google.Asset{
 			Name: "//compute.googleapis.com/projects/my-project/zones/us-central1-a/disks/test-disk",
@@ -30,17 +45,42 @@ func testAssets() []google.Asset {
 					"sourceImage":            "projects/debian-cloud/global/images/debian-8-jessie-v20170523",
 					"type":                   "projects/my-project/zones/us-central1-a/diskTypes/pd-ssd",
 					"zone":                   "projects/my-project/global/zones/us-central1-a",
+					"arguments": map[string]interface{}{
+						"path":             path,
+						"project":          project,
+						"zone":             zone,
+						"region":           region,
+						"ancestry":         ancestry,
+						"offline":          offline,
+						"convertUnchanged": false,
+						"errorLogger":      errorLogger,
+						"userAgent":        userAgent,
+					},
 				},
 			},
 		},
 	}
 }
 
-func MockReadPlannedAssets(ctx context.Context, path, project string, ancestry map[string]string, offline, convertUnchanged bool, errorLogger *zap.Logger, userAgent string) ([]google.Asset, error) {
-	return testAssets(), nil
+func MockReadPlannedAssets(ctx context.Context, path, project, zone, region string, ancestry map[string]string, offline, convertUnchanged bool, errorLogger *zap.Logger, userAgent string) ([]google.Asset, error) {
+	return testAssets(path, project, zone, region, ancestry, offline, convertUnchanged, errorLogger, userAgent), nil
 }
 
 func TestConvertRun(t *testing.T) {
+	for _, k := range resetEnvKeys() {
+		k := k
+		originalValue, isSet := os.LookupEnv(k)
+		if isSet {
+			defer os.Setenv(k, originalValue)
+		} else {
+			defer os.Unsetenv(k)
+		}
+		err := os.Setenv(k, "")
+		if err != nil {
+			t.Fatalf("error clearing env var %s: %s", k, err)
+		}
+	}
+
 	a := assert.New(t)
 	verbosity := "debug"
 	useStructuredLogging := true
@@ -60,7 +100,8 @@ func TestConvertRun(t *testing.T) {
 		readPlannedAssets: MockReadPlannedAssets,
 	}
 
-	err := o.run("/path/to/plan")
+	path := "/path/to/plan"
+	err := o.run(path)
 	a.Nil(err)
 
 	errorJSON := errorBuf.String()
@@ -76,7 +117,7 @@ func TestConvertRun(t *testing.T) {
 	a.Len(output["resource_body"], 1)
 
 	var expectedAssets []interface{}
-	expectedAssetJSON, _ := json.Marshal(testAssets())
+	expectedAssetJSON, _ := json.Marshal(testAssets(path, "", "", "", map[string]string{}, false, false, errorLogger, fmt.Sprintf("config-validator-tf/%s", version.BuildVersion())))
 	json.Unmarshal(expectedAssetJSON, &expectedAssets)
 	a.Equal(expectedAssets, output["resource_body"])
 }
@@ -113,6 +154,20 @@ func TestConvertRunLegacy(t *testing.T) {
 }
 
 func TestConvertRunOutputFile(t *testing.T) {
+	for _, k := range resetEnvKeys() {
+		k := k
+		originalValue, isSet := os.LookupEnv(k)
+		if isSet {
+			defer os.Setenv(k, originalValue)
+		} else {
+			defer os.Unsetenv(k)
+		}
+		err := os.Setenv(k, "")
+		if err != nil {
+			t.Fatalf("error clearing env var %s: %s", k, err)
+		}
+	}
+
 	a := assert.New(t)
 	verbosity := "debug"
 	useStructuredLogging := false
@@ -134,7 +189,8 @@ func TestConvertRunOutputFile(t *testing.T) {
 		outputPath:        outputPath,
 	}
 
-	err := o.run("/path/to/plan")
+	path := "/path/to/plan"
+	err := o.run(path)
 	a.Nil(err)
 
 	errorJSON := errorBuf.String()
@@ -154,7 +210,143 @@ func TestConvertRunOutputFile(t *testing.T) {
 	}
 
 	var expectedAssets []interface{}
-	expectedAssetJSON, _ := json.Marshal(testAssets())
+	expectedAssetJSON, _ := json.Marshal(testAssets(path, "", "", "", map[string]string{}, false, false, errorLogger, fmt.Sprintf("config-validator-tf/%s", version.BuildVersion())))
 	json.Unmarshal(expectedAssetJSON, &expectedAssets)
 	a.Equal(expectedAssets, gotAssets)
 }
+
+func TestConvertRun_passesCorrectArguments(t *testing.T) {
+	cases := []struct {
+		name         string
+		project      string
+		ancestry     string
+		envKey       string
+		envValue     string
+		wantAncestry string
+		wantProject  string
+		wantZone     string
+		wantRegion   string
+	}{
+		{
+			name:        "project",
+			project:     "my-project",
+			wantProject: "my-project",
+		},
+		{
+			name:         "project with ancestry",
+			project:      "my-project",
+			ancestry:     "organizations/1234/folders/5678",
+			wantProject:  "my-project",
+			wantAncestry: "organizations/1234/folders/5678",
+		},
+		{
+			name:     "GOOGLE_ZONE",
+			envKey:   "GOOGLE_ZONE",
+			envValue: "whatever",
+			wantZone: "whatever",
+		},
+		{
+			name:     "GCLOUD_ZONE",
+			envKey:   "GCLOUD_ZONE",
+			envValue: "whatever",
+			wantZone: "whatever",
+		},
+		{
+			name:     "CLOUDSDK_COMPUTE_ZONE",
+			envKey:   "CLOUDSDK_COMPUTE_ZONE",
+			envValue: "whatever",
+			wantZone: "whatever",
+		},
+		{
+			name:       "GOOGLE_REGION",
+			envKey:     "GOOGLE_REGION",
+			envValue:   "whatever",
+			wantRegion: "whatever",
+		},
+		{
+			name:       "GCLOUD_REGION",
+			envKey:     "GCLOUD_REGION",
+			envValue:   "whatever",
+			wantRegion: "whatever",
+		},
+		{
+			name:       "CLOUDSDK_COMPUTE_REGION",
+			envKey:     "CLOUDSDK_COMPUTE_REGION",
+			envValue:   "whatever",
+			wantRegion: "whatever",
+		},
+	}
+
+	for _, k := range resetEnvKeys() {
+		k := k
+		originalValue, isSet := os.LookupEnv(k)
+		if isSet {
+			defer os.Setenv(k, originalValue)
+		} else {
+			defer os.Unsetenv(k)
+		}
+	}
+
+	for _, c := range cases {
+		t.Run(c.name, func(t *testing.T) {
+			// clear env vars before each test
+			for _, k := range resetEnvKeys() {
+				err := os.Setenv(k, "")
+				if err != nil {
+					t.Fatalf("error clearing env var %s: %s", k, err)
+				}
+			}
+			if c.envKey != "" {
+				err := os.Setenv(c.envKey, c.envValue)
+				if err != nil {
+					t.Fatalf("error setting env var %s=%s: %s", c.envKey, c.envValue, err)
+				}
+
+			}
+
+			a := assert.New(t)
+			verbosity := "debug"
+			useStructuredLogging := true
+			errorLogger, errorBuf := newTestErrorLogger(verbosity, useStructuredLogging)
+			outputLogger, outputBuf := newTestOutputLogger()
+			ro := &rootOptions{
+				verbosity:            verbosity,
+				useStructuredLogging: useStructuredLogging,
+				errorLogger:          errorLogger,
+				outputLogger:         outputLogger,
+			}
+			o := convertOptions{
+				project:           c.project,
+				ancestry:          c.ancestry,
+				offline:           false,
+				rootOptions:       ro,
+				readPlannedAssets: MockReadPlannedAssets,
+			}
+
+			path := "/path/to/plan"
+			err := o.run(path)
+			a.Nil(err)
+
+			errorJSON := errorBuf.String()
+			outputJSON := outputBuf.Bytes()
+
+			a.Equal(errorJSON, "")
+
+			var output map[string]interface{}
+			json.Unmarshal(outputJSON, &output)
+
+			// On a successful run, we should see a list of google assets in the resource_body field
+			a.Contains(output, "resource_body")
+			a.Len(output["resource_body"], 1)
+
+			wantAncestryCache := map[string]string{}
+			if c.wantProject != "" {
+				wantAncestryCache[c.wantProject] = c.wantAncestry
+			}
+			var expectedAssets []interface{}
+			expectedAssetJSON, _ := json.Marshal(testAssets(path, c.wantProject, c.wantZone, c.wantRegion, wantAncestryCache, false, false, errorLogger, fmt.Sprintf("config-validator-tf/%s", version.BuildVersion())))
+			json.Unmarshal(expectedAssetJSON, &expectedAssets)
+			a.Equal(expectedAssets, output["resource_body"])
+		})
+	}
+}
diff --git a/cmd/validate_test.go b/cmd/validate_test.go
index 0d2941aa..eefb5916 100644
--- a/cmd/validate_test.go
+++ b/cmd/validate_test.go
@@ -3,6 +3,7 @@ package cmd
 import (
 	"context"
 	"encoding/json"
+	"fmt"
 	"io/ioutil"
 	"os"
 	"path"
@@ -10,6 +11,7 @@ import (
 
 	"github.com/GoogleCloudPlatform/config-validator/pkg/api/validator"
 	"github.com/GoogleCloudPlatform/terraform-validator/converters/google"
+	"github.com/GoogleCloudPlatform/terraform-validator/version"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -218,11 +220,15 @@ func TestValidateRunWithConvertedAssets(t *testing.T) {
 		validateAssets: MockValidateAssetsNoViolations,
 	}
 
-	data, err := json.Marshal(testAssets())
+	inputPath := path.Join(t.TempDir(), "testfile.json")
+	data, err := json.Marshal(testAssets(inputPath, "", "", "", map[string]string{}, false, false, errorLogger, fmt.Sprintf("config-validator-tf/%s", version.BuildVersion())))
 	if err != nil {
 		t.Fatalf("Failed to marshal assets: %s", err)
 	}
-	inputPath := createEmptyFile(t, data)
+
+	if err := ioutil.WriteFile(inputPath, data, os.ModePerm); err != nil {
+		t.Fatalf("Failed to write file %s: %s", inputPath, err)
+	}
 	err = o.run(inputPath)
 	a.Nil(err)
 
@@ -234,6 +240,133 @@ func TestValidateRunWithConvertedAssets(t *testing.T) {
 	a.Equal("", outputJSON)
 }
 
+func TestValidateRun_passesCorrectArguments(t *testing.T) {
+	cases := []struct {
+		name         string
+		project      string
+		ancestry     string
+		envKey       string
+		envValue     string
+		wantAncestry string
+		wantProject  string
+		wantZone     string
+		wantRegion   string
+	}{
+		{
+			name:        "project",
+			project:     "my-project",
+			wantProject: "my-project",
+		},
+		{
+			name:         "project with ancestry",
+			project:      "my-project",
+			ancestry:     "organizations/1234/folders/5678",
+			wantProject:  "my-project",
+			wantAncestry: "organizations/1234/folders/5678",
+		},
+		{
+			name:     "GOOGLE_ZONE",
+			envKey:   "GOOGLE_ZONE",
+			envValue: "whatever",
+			wantZone: "whatever",
+		},
+		{
+			name:     "GCLOUD_ZONE",
+			envKey:   "GCLOUD_ZONE",
+			envValue: "whatever",
+			wantZone: "whatever",
+		},
+		{
+			name:     "CLOUDSDK_COMPUTE_ZONE",
+			envKey:   "CLOUDSDK_COMPUTE_ZONE",
+			envValue: "whatever",
+			wantZone: "whatever",
+		},
+		{
+			name:       "GOOGLE_REGION",
+			envKey:     "GOOGLE_REGION",
+			envValue:   "whatever",
+			wantRegion: "whatever",
+		},
+		{
+			name:       "GCLOUD_REGION",
+			envKey:     "GCLOUD_REGION",
+			envValue:   "whatever",
+			wantRegion: "whatever",
+		},
+		{
+			name:       "CLOUDSDK_COMPUTE_REGION",
+			envKey:     "CLOUDSDK_COMPUTE_REGION",
+			envValue:   "whatever",
+			wantRegion: "whatever",
+		},
+	}
+
+	for _, k := range resetEnvKeys() {
+		k := k
+		originalValue, isSet := os.LookupEnv(k)
+		if isSet {
+			defer os.Setenv(k, originalValue)
+		} else {
+			defer os.Unsetenv(k)
+		}
+	}
+
+	for _, c := range cases {
+		t.Run(c.name, func(t *testing.T) {
+			// clear env vars before each test
+			for _, k := range resetEnvKeys() {
+				err := os.Setenv(k, "")
+				if err != nil {
+					t.Fatalf("error clearing env var %s: %s", k, err)
+				}
+			}
+			if c.envKey != "" {
+				err := os.Setenv(c.envKey, c.envValue)
+				if err != nil {
+					t.Fatalf("error setting env var %s=%s: %s", c.envKey, c.envValue, err)
+				}
+
+			}
+
+			a := assert.New(t)
+			verbosity := "debug"
+			useStructuredLogging := true
+			errorLogger, _ := newTestErrorLogger(verbosity, useStructuredLogging)
+			outputLogger, _ := newTestOutputLogger()
+			inputPath := createEmptyFile(t, []byte{'0'})
+			wantAncestryCache := map[string]string{}
+			if c.wantProject != "" {
+				wantAncestryCache[c.wantProject] = c.wantAncestry
+			}
+			expectedAssets := testAssets(inputPath, c.wantProject, c.wantZone, c.wantRegion, wantAncestryCache, false, false, errorLogger, fmt.Sprintf("config-validator-tf/%s", version.BuildVersion()))
+
+			ro := &rootOptions{
+				verbosity:            verbosity,
+				useStructuredLogging: useStructuredLogging,
+				errorLogger:          errorLogger,
+				outputLogger:         outputLogger,
+			}
+			o := validateOptions{
+				project:           c.project,
+				ancestry:          c.ancestry,
+				offline:           false,
+				policyPath:        "",
+				outputJSON:        false,
+				dryRun:            false,
+				rootOptions:       ro,
+				readPlannedAssets: MockReadPlannedAssets,
+				validateAssets: func(ctx context.Context, assets []google.Asset, policyRootPath string) ([]*validator.Violation, error) {
+					a.Equal(expectedAssets, assets)
+					return MockValidateAssetsNoViolations(ctx, assets, policyRootPath)
+				},
+			}
+
+			o.run(inputPath)
+		})
+	}
+}
+
 func createEmptyFile(t *testing.T, data []byte) string {
 	assetPath := path.Join(t.TempDir(), "testfile.json")
 	if err := ioutil.WriteFile(assetPath, data, os.ModePerm); err != nil {
diff --git a/converters/google/convert_test.go b/converters/google/convert_test.go
index 5d04f7cf..ea102b61 100644
--- a/converters/google/convert_test.go
+++ b/converters/google/convert_test.go
@@ -62,7 +62,7 @@ func newTestConverter(convertUnchanged bool) (*Converter, *bytes.Buffer, error)
 	ctx := context.Background()
 	project := testProject
 	offline := true
-	cfg, err := resources.NewConfig(ctx, project, offline, "", nil)
+	cfg, err := resources.NewConfig(ctx, project, "", "", offline, "", nil)
 	if err != nil {
 		return nil, nil, errors.Wrap(err, "constructing configuration")
 	}
diff --git a/converters/google/resources/getconfig_test.go b/converters/google/resources/getconfig_test.go
index 61fae106..fb042e40 100644
--- a/converters/google/resources/getconfig_test.go
+++ b/converters/google/resources/getconfig_test.go
@@ -20,14 +20,8 @@ func getAccessToken(cfg *Config) string {
 func getImpersonateServiceAccount(cfg *Config) string {
 	return cfg.ImpersonateServiceAccount
 }
-func getZoneValue(cfg *Config) string {
-	return cfg.Zone
-}
-func getRegionValue(cfg *Config) string {
-	return cfg.Region
-}
 
-func TestGetConfigExtractsEnvVars(t *testing.T) {
+func TestNewConfigExtractsEnvVars(t *testing.T) {
 	ctx := context.Background()
 	offline := true
 	cases := []struct {
@@ -72,48 +66,6 @@ func TestGetConfigExtractsEnvVars(t *testing.T) {
 			expected:       "whatever",
 			getConfigValue: getImpersonateServiceAccount,
 		},
-		{
-			name:            "GOOGLE_ZONE",
-		        envKey:          "GOOGLE_ZONE",
-		        envValue:        "whatever",
-		        expected:        "whatever",
-		        getConfigValue:  getZoneValue,
-	        },
-		{
-			name:           "GCLOUD_ZONE",
-			envKey:         "GCLOUD_ZONE",
-			envValue:       "whatever",
-			expected:       "whatever",
-			getConfigValue: getZoneValue,
-		},
-		{
-			name:           "CLOUDSDK_COMPUTE_ZONE",
-			envKey:         "CLOUDSDK_COMPUTE_ZONE",
-			envValue:       "whatever",
-			expected:       "whatever",
-			getConfigValue: getZoneValue,
-		},
-		{
-			name:           "GOOGLE_REGION",
-			envKey:         "GOOGLE_REGION",
-			envValue:       "whatever",
-			expected:       "whatever",
-			getConfigValue: getRegionValue,
-		},
-		{
-			name:           "GCLOUD_REGION",
-			envKey:         "GCLOUD_REGION",
-			envValue:       "whatever",
-			expected:       "whatever",
-			getConfigValue: getRegionValue,
-		},
-		{
-			name:           "CLOUDSDK_COMPUTE_REGION",
-			envKey:         "CLOUDSDK_COMPUTE_REGION",
-			envValue:       "whatever",
-			expected:       "whatever",
-			getConfigValue: getRegionValue,
-		},
 	}
 
 	for _, c := range cases {
@@ -124,7 +76,7 @@ func TestGetConfigExtractsEnvVars(t *testing.T) {
 				t.Fatalf("error setting env var %s=%s: %s", c.envKey, c.envValue, err)
 			}
 
-			cfg, err := GetConfig(ctx, "project", offline, "")
+			cfg, err := NewConfig(ctx, "project", "", "", offline, "", nil)
 			if err != nil {
 				t.Fatalf("error building converter: %s", err)
 			}
@@ -146,7 +98,7 @@ func TestGetConfigExtractsEnvVars(t *testing.T) {
 	}
 }
 
-func TestGetConfigUserAgent(t *testing.T) {
+func TestNewConfigUserAgent(t *testing.T) {
 	ctx := context.Background()
 	offline := true
 	cases := []struct {
@@ -165,7 +117,7 @@ func TestGetConfigUserAgent(t *testing.T) {
 
 	for _, c := range cases {
 		t.Run(c.userAgent, func(t *testing.T) {
-			cfg, err := GetConfig(ctx, "project", offline, c.userAgent)
+			cfg, err := NewConfig(ctx, "project", "", "", offline, c.userAgent, nil)
 			if err != nil {
 				t.Fatalf("error building config: %s", err)
 			}
@@ -175,10 +127,10 @@ func TestGetConfigUserAgent(t *testing.T) {
 	}
 }
 
-func TestGetConfigUserAgent_nilClientUsesDefault(t *testing.T) {
+func TestNewConfigUserAgent_nilClientUsesDefault(t *testing.T) {
 	ctx := context.Background()
 	offline := false
-	cfg, err := NewConfig(ctx, "project", offline, "", nil)
+	cfg, err := NewConfig(ctx, "project", "", "", offline, "", nil)
 	if err != nil {
 		t.Fatalf("error building config: %s", err)
 	}
@@ -186,11 +138,11 @@ func TestGetConfigUserAgent_nilClientUsesDefault(t *testing.T) {
 	assert.NotEmpty(t, cfg.Client())
 }
 
-func TestGetConfigUserAgent_usesPassedClient(t *testing.T) {
+func TestNewConfigUserAgent_usesPassedClient(t *testing.T) {
 	ctx := context.Background()
 	offline := false
 	client := &http.Client{}
-	cfg, err := NewConfig(ctx, "project", offline, "", client)
+	cfg, err := NewConfig(ctx, "project", "", "", offline, "", client)
 	if err != nil {
 		t.Fatalf("error building config: %s", err)
 	}
diff --git a/test/read_test.go b/test/read_test.go
index 5fe4c848..0a0be135 100644
--- a/test/read_test.go
+++ b/test/read_test.go
@@ -141,9 +141,9 @@ func TestReadPlannedAssetsCoverage(t *testing.T) {
 			ancestryCache := map[string]string{
 				data.Provider["project"]: data.Ancestry,
 			}
-			got, err := tfgcv.ReadPlannedAssets(ctx, planfile, data.Provider["project"], ancestryCache, true, false, zaptest.NewLogger(t), "")
+			got, err := tfgcv.ReadPlannedAssets(ctx, planfile, data.Provider["project"], "", "", ancestryCache, true, false, zaptest.NewLogger(t), "")
 			if err != nil {
-				t.Fatalf("ReadPlannedAssets(%s, %s, %s, %t): %v", planfile, data.Provider["project"], ancestryCache, true, err)
+				t.Fatalf("ReadPlannedAssets(%s, %s, \"\", \"\", %s, %t): %v", planfile, data.Provider["project"], ancestryCache, true, err)
 			}
 
 			expectedAssets := normalizeAssets(t, want, true)
@@ -188,9 +188,9 @@ func TestReadPlannedAssetsCoverage_WithoutDefaultProject(t *testing.T) {
 			ancestryCache := map[string]string{
 				// data.Provider["project"]: data.Ancestry,
 			}
-			got, err := tfgcv.ReadPlannedAssets(ctx, planfile, "", ancestryCache, true, false, zaptest.NewLogger(t), "")
+			got, err := tfgcv.ReadPlannedAssets(ctx, planfile, "", "", "", ancestryCache, true, false, zaptest.NewLogger(t), "")
 			if err != nil {
-				t.Fatalf("ReadPlannedAssets(%s, %s, %s, %t): %v", planfile, data.Provider["project"], ancestryCache, true, err)
+				t.Fatalf("ReadPlannedAssets(%s, %s, \"\", \"\", %s, %t): %v", planfile, data.Provider["project"], ancestryCache, true, err)
 			}
 
 			expectedAssets := normalizeAssets(t, want, true)
diff --git a/tfgcv/planned_assets_test.go b/tfgcv/planned_assets_test.go
index 5c8ba4c1..3ca8cef5 100644
--- a/tfgcv/planned_assets_test.go
+++ b/tfgcv/planned_assets_test.go
@@ -104,7 +104,7 @@ func TestReadPlannedAssets(t *testing.T) {
 			testFile := filepath.Join(testDataDir, tt.args.file)
 			offline := true
 			ctx := context.Background()
-			got, err := ReadPlannedAssets(ctx, testFile, tt.args.project, tt.ancestryCache, offline, tt.args.convertUnchanged, zap.NewExample(), "")
+			got, err := ReadPlannedAssets(ctx, testFile, tt.args.project, "", "", tt.ancestryCache, offline, tt.args.convertUnchanged, zap.NewExample(), "")
 			if (err != nil) != tt.wantErr {
 				t.Errorf("ReadPlannedAssets() error = %v, wantErr %v", err, tt.wantErr)
 				return