diff --git a/config/config.go b/config/config.go index 5210e49376d4..bdae585242c7 100644 --- a/config/config.go +++ b/config/config.go @@ -9,7 +9,6 @@ import ( "strings" "github.com/hashicorp/go-multierror" - "github.com/hashicorp/go-version" "github.com/hashicorp/hil" "github.com/hashicorp/hil/ast" "github.com/hashicorp/terraform/helper/hilmapstructure" @@ -254,26 +253,7 @@ func (c *Config) Validate() error { // Validate the Terraform config if tf := c.Terraform; tf != nil { - if raw := tf.RequiredVersion; raw != "" { - // Check that the value has no interpolations - rc, err := NewRawConfig(map[string]interface{}{ - "root": raw, - }) - if err != nil { - errs = append(errs, fmt.Errorf( - "terraform.required_version: %s", err)) - } else if len(rc.Interpolations) > 0 { - errs = append(errs, fmt.Errorf( - "terraform.required_version: cannot contain interpolations")) - } else { - // Check it is valid - _, err := version.NewConstraint(raw) - if err != nil { - errs = append(errs, fmt.Errorf( - "terraform.required_version: invalid syntax: %s", err)) - } - } - } + errs = append(errs, c.Terraform.Validate()...) } vars := c.InterpolatedVariables() diff --git a/config/config_terraform.go b/config/config_terraform.go index 272fa62c8e00..952d59cc4e02 100644 --- a/config/config_terraform.go +++ b/config/config_terraform.go @@ -1,6 +1,10 @@ package config import ( + "fmt" + "strings" + + "github.com/hashicorp/go-version" "github.com/mitchellh/hashstructure" ) @@ -11,6 +15,38 @@ type Terraform struct { Backend *Backend // See Backend struct docs } +// Validate performs the validation for just the Terraform configuration. +func (t *Terraform) Validate() []error { + var errs []error + + if raw := t.RequiredVersion; raw != "" { + // Check that the value has no interpolations + rc, err := NewRawConfig(map[string]interface{}{ + "root": raw, + }) + if err != nil { + errs = append(errs, fmt.Errorf( + "terraform.required_version: %s", err)) + } else if len(rc.Interpolations) > 0 { + errs = append(errs, fmt.Errorf( + "terraform.required_version: cannot contain interpolations")) + } else { + // Check it is valid + _, err := version.NewConstraint(raw) + if err != nil { + errs = append(errs, fmt.Errorf( + "terraform.required_version: invalid syntax: %s", err)) + } + } + } + + if t.Backend != nil { + errs = append(errs, t.Backend.Validate()...) + } + + return errs +} + // Backend is the configuration for the "backend" to use with Terraform. // A backend is responsible for all major behavior of Terraform's core. // The abstraction layer above the core (the "backend") allows for behavior @@ -46,3 +82,24 @@ func (b *Backend) Rehash() uint64 { return code } + +func (b *Backend) Validate() []error { + if len(b.RawConfig.Interpolations) > 0 { + return []error{fmt.Errorf(strings.TrimSpace(errBackendInterpolations))} + } + + return nil +} + +const errBackendInterpolations = ` +terraform.backend: configuration cannot contain interpolations + +The backend configuration is loaded by Terraform extremely early, before +the core of Terraform can be initialized. This is necessary because the backend +dictates the behavior of that core. The core is what handles interpolation +processing. Because of this, interpolations cannot be used in backend +configuration. + +If you'd like to parameterize backend configuration, we recommend using +partial configuration with the "-backend-config" flag to "terraform init". +` diff --git a/config/config_test.go b/config/config_test.go index 1c7b36e04e7c..2ef68dae979d 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -194,6 +194,13 @@ func TestConfigValidate_table(t *testing.T) { false, "", }, + + { + "backend config with interpolations", + "validate-backend-interpolate", + true, + "cannot contain interp", + }, } for i, tc := range cases { diff --git a/config/test-fixtures/validate-backend-interpolate/main.tf b/config/test-fixtures/validate-backend-interpolate/main.tf new file mode 100644 index 000000000000..dd4fbdb7b413 --- /dev/null +++ b/config/test-fixtures/validate-backend-interpolate/main.tf @@ -0,0 +1,5 @@ +terraform { + backend "foo" { + key = "${var.var}" + } +}