Cloud integration requires input for migrations

We cannot programmatically migrate workspaces to Terraform Cloud without
prompts, so `-input=false` should not be allowed in those cases.

There are 4 scenarios where we need input from a user to complete
migrating workspaces to Terraform Cloud.

1.) Migrate from a single local workspace to Terraform Cloud

* Terraform config for a local backend. Implicit local (no backend
  specified) is fine.
* `terraform init` and `terraform apply`
* Change the Terraform config to use the cloud block
* `terraform init -input=false`
* You should now see an error message

2.) Migrate from a remote backend with a prefix to Terraform Cloud with
  tags

* Create a workspace in Terraform Cloud manually. The name should
  include a prefix, like "app-one"
* Have the terraform config use `backend "remote"` with a prefix set to
  "app-"
* `terraform init` and `terraform apply`
* Update the Terraform config to use a cloud block with `tags
  = ["app"]`. There should not be a prefix defined in the config now.
* `terraform init -input=false`
* You should now see an error message

3.) Migrate from multiple local workspaces to a single Terraform Cloud
  workspace
* Create one or many local workspaces
* `terraform init` and `terraform apply` in each
* Change the Terraform config to use the cloud block
* `terraform init -input=false`
* You should now see an error message

4.) Migrate to Terraform Cloud and ask for a workspace name
* Create several local workspaces
* `terraform init` and `terraform apply` in each
* Change the Terraform config to use the cloud block with tags
* `terraform init -input=false`
* You should now see an error message
This commit is contained in:
Barrett Clark 2021-11-22 13:17:04 -06:00
parent a8972d82e9
commit 419676cb69
2 changed files with 26 additions and 2 deletions

View File

@ -1310,7 +1310,7 @@ func TestInit_inputFalse(t *testing.T) {
}
errMsg := ui.ErrorWriter.String()
if !strings.Contains(errMsg, "input disabled") {
if !strings.Contains(errMsg, "interactive input is disabled") {
t.Fatal("expected input disabled error, got", errMsg)
}

View File

@ -415,7 +415,7 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error {
// Abort if we can't ask for input.
if !m.input {
log.Print("[TRACE] backendMigrateState: can't prompt for input, so aborting migration")
return errors.New("error asking for state migration action: input disabled")
return errors.New(strings.TrimSpace(errInteractiveInputDisabled))
}
// Confirm with the user whether we want to copy state over
@ -773,6 +773,10 @@ func (m *Meta) backendMigrateState_S_TFC(opts *backendMigrateOpts, sourceWorkspa
}
func (m *Meta) promptSingleToCloudSingleStateMigration(opts *backendMigrateOpts) (bool, error) {
if !m.input {
log.Print("[TRACE] backendMigrateState: can't prompt for input, so aborting migration")
return false, errors.New(strings.TrimSpace(errInteractiveInputDisabled))
}
migrate := opts.force
if !migrate {
var err error
@ -790,6 +794,10 @@ func (m *Meta) promptSingleToCloudSingleStateMigration(opts *backendMigrateOpts)
}
func (m *Meta) promptRemotePrefixToCloudTagsMigration(opts *backendMigrateOpts) error {
if !m.input {
log.Print("[TRACE] backendMigrateState: can't prompt for input, so aborting migration")
return errors.New(strings.TrimSpace(errInteractiveInputDisabled))
}
migrate := opts.force
if !migrate {
var err error
@ -812,6 +820,10 @@ func (m *Meta) promptRemotePrefixToCloudTagsMigration(opts *backendMigrateOpts)
// Multi-state to single state.
func (m *Meta) promptMultiToSingleCloudMigration(opts *backendMigrateOpts) error {
if !m.input {
log.Print("[TRACE] backendMigrateState: can't prompt for input, so aborting migration")
return errors.New(strings.TrimSpace(errInteractiveInputDisabled))
}
migrate := opts.force
if !migrate {
var err error
@ -839,6 +851,10 @@ func (m *Meta) promptNewWorkspaceName(destinationType string) (string, error) {
message := fmt.Sprintf("[reset][bold][yellow]The %q backend configuration only allows "+
"named workspaces![reset]", destinationType)
if destinationType == "cloud" {
if !m.input {
log.Print("[TRACE] backendMigrateState: can't prompt for input, so aborting migration")
return "", errors.New(strings.TrimSpace(errInteractiveInputDisabled))
}
message = `[reset][bold][yellow]Terraform Cloud requires all workspaces to be given an explicit name.[reset]`
}
name, err := m.UIInput().Input(context.Background(), &terraform.InputOpts{
@ -854,6 +870,8 @@ func (m *Meta) promptNewWorkspaceName(destinationType string) (string, error) {
}
func (m *Meta) promptMultiStateMigrationPattern(sourceType string) (string, error) {
// This is not the first prompt a user would be presented with in the migration to TFC, so no
// guard on m.input is needed here.
renameWorkspaces, err := m.UIInput().Input(context.Background(), &terraform.InputOpts{
Id: "backend-migrate-multistate-to-tfc",
Query: fmt.Sprintf("[reset][bold][yellow]%s[reset]", "Would you like to rename your workspaces?"),
@ -941,6 +959,12 @@ Migrating state from Terraform Cloud to another backend is not yet implemented.
Please use the API to do this: https://www.terraform.io/docs/cloud/api/state-versions.html
`
const errInteractiveInputDisabled = `
Can't ask approval for state migration when interactive input is disabled.
Please remove the "-input=false" option and try again.
`
const tfcInputBackendMigrateMultiToMultiPattern = `
Enter a pattern with an asterisk (*) to rename all workspaces based on their
previous names. The asterisk represents the current workspace name.