diff --git a/command/init.go b/command/init.go index 11ccca5b2d..2f93c1b4ed 100644 --- a/command/init.go +++ b/command/init.go @@ -803,6 +803,18 @@ func (c *InitCommand) backendConfigOverrideBody(flags rawFlags, schema *configsc // The value is interpreted as a filename. newBody, fileDiags := c.loadHCLFile(item.Value) diags = diags.Append(fileDiags) + // Verify that the file contains only key-values pairs, and not a + // full backend config block. JustAttributes() will return an error + // if blocks are found + _, attrDiags := newBody.JustAttributes() + if attrDiags.HasErrors() { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Invalid backend configuration file", + fmt.Sprintf("The backend configuration file %q given on the command line must contain key-value pairs only, and not configuration blocks.", item.Value), + )) + continue + } flushVals() // deal with any accumulated individual values first mergeBody(newBody) } else { diff --git a/command/init_test.go b/command/init_test.go index 0163efb88c..9fdc29ba02 100644 --- a/command/init_test.go +++ b/command/init_test.go @@ -337,24 +337,43 @@ func TestInit_backendConfigFile(t *testing.T) { defer os.RemoveAll(td) defer testChdir(t, td)() - ui := new(cli.MockUi) - c := &InitCommand{ - Meta: Meta{ - testingOverrides: metaOverridesForProvider(testProvider()), - Ui: ui, - }, - } + t.Run("good-config-file", func(t *testing.T) { + ui := new(cli.MockUi) + c := &InitCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(testProvider()), + Ui: ui, + }, + } + args := []string{"-backend-config", "input.config"} + if code := c.Run(args); code != 0 { + t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + } - args := []string{"-backend-config", "input.config"} - if code := c.Run(args); code != 0 { - t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) - } + // Read our saved backend config and verify we have our settings + state := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename)) + if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":"hello","workspace_dir":null}`; got != want { + t.Errorf("wrong config\ngot: %s\nwant: %s", got, want) + } + }) - // Read our saved backend config and verify we have our settings - state := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename)) - if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":"hello","workspace_dir":null}`; got != want { - t.Errorf("wrong config\ngot: %s\nwant: %s", got, want) - } + // the backend config file must be a set of key-value pairs and not a full backend {} block + t.Run("invalid-config-file", func(t *testing.T) { + ui := new(cli.MockUi) + c := &InitCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(testProvider()), + Ui: ui, + }, + } + args := []string{"-backend-config", "backend.config"} + if code := c.Run(args); code != 1 { + t.Fatalf("expected error, got success\n") + } + if !strings.Contains(ui.ErrorWriter.String(), "Invalid backend configuration file") { + t.Fatalf("wrong error: %s", ui.ErrorWriter) + } + }) } func TestInit_backendConfigFilePowershellConfusion(t *testing.T) { diff --git a/command/testdata/init-backend-config-file/backend.config b/command/testdata/init-backend-config-file/backend.config new file mode 100644 index 0000000000..db3d3159b8 --- /dev/null +++ b/command/testdata/init-backend-config-file/backend.config @@ -0,0 +1,7 @@ +// the -backend-config flag on init cannot be used to point to a "full" backend +// block, only key-value pairs (like terraform.tfvars) +terraform { + backend "local" { + path = "hello" + } +} diff --git a/website/docs/backends/config.html.md b/website/docs/backends/config.html.md index f7f7c70be0..4c7f0ea2d3 100644 --- a/website/docs/backends/config.html.md +++ b/website/docs/backends/config.html.md @@ -63,12 +63,12 @@ There are several ways to supply the remaining arguments: values, unless interactive input is disabled. Terraform will not prompt for optional values. - * **File**: A configuration file may be specified via the `init` command line. - To specify a file, use the `-backend-config=PATH` option when running - `terraform init`. If the file contains secrets it may be kept in - a secure data store, such as - [Vault](https://www.vaultproject.io/), in which case it must be downloaded - to the local disk before running Terraform. + * **File**: A [backend configuration file](#backend-configuration-file) may be specified via the + `init` command line. To specify a file, use the `-backend-config=PATH` + option when running `terraform init`. If the file contains secrets it may be + kept in a secure data store, such as [Vault](https://www.vaultproject.io/), + in which case it must be downloaded to the local disk before running + Terraform. * **Command-line key/value pairs**: Key/value pairs can be specified via the `init` command line. Note that many shells retain command-line flags in a @@ -96,6 +96,7 @@ terraform { } ``` +## Backend Configuration File A backend configuration file has the contents of the `backend` block as top-level attributes, without the need to wrap it in another `terraform` or `backend` block: