diff --git a/pkg/cmd/scan.go b/pkg/cmd/scan.go index 153320f0..1f8c19cd 100644 --- a/pkg/cmd/scan.go +++ b/pkg/cmd/scan.go @@ -168,6 +168,12 @@ func NewScanCmd(opts *pkg.ScanOptions) *cobra.Command { "Terraform Cloud / Enterprise API token.\n"+ "Only used with tfstate+tfcloud backend.\n", ) + fl.StringVar(&opts.BackendOptions.TFCloudEndpoint, + "tfc-endpoint", + "https://app.terraform.io/api/v2", + "Terraform Cloud / Enterprise API endpoint.\n"+ + "Only used with tfstate+tfcloud backend.\n", + ) fl.String( "tf-provider-version", "", diff --git a/pkg/iac/terraform/state/backend/backend.go b/pkg/iac/terraform/state/backend/backend.go index 84b2d5f6..5dec27d0 100644 --- a/pkg/iac/terraform/state/backend/backend.go +++ b/pkg/iac/terraform/state/backend/backend.go @@ -20,8 +20,9 @@ var supportedBackends = []string{ type Backend io.ReadCloser type Options struct { - Headers map[string]string - TFCloudToken string + Headers map[string]string + TFCloudToken string + TFCloudEndpoint string } func IsSupported(backend string) bool { diff --git a/pkg/iac/terraform/state/backend/tfcloud_config_reader.go b/pkg/iac/terraform/state/backend/tfcloud_config_reader.go index 533ef684..a818b818 100644 --- a/pkg/iac/terraform/state/backend/tfcloud_config_reader.go +++ b/pkg/iac/terraform/state/backend/tfcloud_config_reader.go @@ -13,11 +13,11 @@ import ( ) type container struct { - Credentials struct { - TerraformCloud struct { - Token string - } `json:"app.terraform.io"` - } + Credentials map[string]containerToken +} + +type containerToken struct { + Token string } type tfCloudConfigReader struct { @@ -28,7 +28,7 @@ func NewTFCloudConfigReader(reader io.ReadCloser) *tfCloudConfigReader { return &tfCloudConfigReader{reader} } -func (r *tfCloudConfigReader) GetToken() (string, error) { +func (r *tfCloudConfigReader) GetToken(host string) (string, error) { b, err := ioutil.ReadAll(r.reader) if err != nil { return "", errors.New("unable to read file") @@ -38,10 +38,10 @@ func (r *tfCloudConfigReader) GetToken() (string, error) { if err := json.Unmarshal(b, &container); err != nil { return "", err } - if container.Credentials.TerraformCloud.Token == "" { + if container.Credentials[host].Token == "" { return "", errors.New("driftctl could not read your Terraform configuration file, please check that this is a valid Terraform credentials file") } - return container.Credentials.TerraformCloud.Token, nil + return container.Credentials[host].Token, nil } func getTerraformConfigFile() (string, error) { diff --git a/pkg/iac/terraform/state/backend/tfcloud_config_reader_test.go b/pkg/iac/terraform/state/backend/tfcloud_config_reader_test.go index 600fc534..0aff186e 100644 --- a/pkg/iac/terraform/state/backend/tfcloud_config_reader_test.go +++ b/pkg/iac/terraform/state/backend/tfcloud_config_reader_test.go @@ -44,7 +44,7 @@ func TestTFCloudConfigReader_GetToken(t *testing.T) { readerCloser := ioutil.NopCloser(strings.NewReader(tt.src)) defer readerCloser.Close() r := NewTFCloudConfigReader(readerCloser) - got, err := r.GetToken() + got, err := r.GetToken("app.terraform.io") if err != nil && err.Error() != tt.wantErr.Error() { t.Errorf("GetToken() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/pkg/iac/terraform/state/backend/tfcloud_reader.go b/pkg/iac/terraform/state/backend/tfcloud_reader.go index b04e9b20..e220b093 100644 --- a/pkg/iac/terraform/state/backend/tfcloud_reader.go +++ b/pkg/iac/terraform/state/backend/tfcloud_reader.go @@ -14,7 +14,6 @@ import ( ) const BackendKeyTFCloud = "tfcloud" -const TFCloudAPI = "https://app.terraform.io/api/v2" type TFCloudAttributes struct { HostedStateDownloadUrl string `json:"hosted-state-download-url"` @@ -36,7 +35,7 @@ type TFCloudBackend struct { } func NewTFCloudReader(client pkghttp.HTTPClient, workspaceId string, opts *Options) (*TFCloudBackend, error) { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/workspaces/%s/current-state-version", TFCloudAPI, workspaceId), nil) + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/workspaces/%s/current-state-version", opts.TFCloudEndpoint, workspaceId), nil) if err != nil { return nil, err } @@ -57,7 +56,7 @@ func (t *TFCloudBackend) authorize() error { } defer file.Close() reader := NewTFCloudConfigReader(file) - token, err = reader.GetToken() + token, err = reader.GetToken(t.request.URL.Host) if err != nil { return err } diff --git a/pkg/iac/terraform/state/backend/tfcloud_reader_test.go b/pkg/iac/terraform/state/backend/tfcloud_reader_test.go index 66ad3143..1c38cc39 100644 --- a/pkg/iac/terraform/state/backend/tfcloud_reader_test.go +++ b/pkg/iac/terraform/state/backend/tfcloud_reader_test.go @@ -29,7 +29,8 @@ func TestTFCloudBackend_Read(t *testing.T) { args: args{ workspaceId: "workspaceId", options: &Options{ - TFCloudToken: "TOKEN", + TFCloudToken: "TOKEN", + TFCloudEndpoint: "https://app.terraform.io/api/v2", }, }, url: "https://app.terraform.io/api/v2/workspaces/workspaceId/current-state-version", @@ -54,7 +55,8 @@ func TestTFCloudBackend_Read(t *testing.T) { args: args{ workspaceId: "wrong_workspaceId", options: &Options{ - TFCloudToken: "TOKEN", + TFCloudToken: "TOKEN", + TFCloudEndpoint: "https://app.terraform.io/api/v2", }, }, url: "https://app.terraform.io/api/v2/workspaces/wrong_workspaceId/current-state-version", @@ -73,7 +75,8 @@ func TestTFCloudBackend_Read(t *testing.T) { args: args{ workspaceId: "workspaceId", options: &Options{ - TFCloudToken: "TOKEN", + TFCloudToken: "TOKEN", + TFCloudEndpoint: "https://app.terraform.io/api/v2", }, }, url: "https://app.terraform.io/api/v2/workspaces/workspaceId/current-state-version",