github.com/hugorut/terraform@v1.1.3/src/cloud/e2e/migrate_state_remote_backend_to_tfc_test.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"io/ioutil"
     6  	"os"
     7  	"testing"
     8  
     9  	expect "github.com/Netflix/go-expect"
    10  	tfe "github.com/hashicorp/go-tfe"
    11  	"github.com/hugorut/terraform/src/e2e"
    12  )
    13  
    14  func Test_migrate_remote_backend_name_to_tfc_name(t *testing.T) {
    15  	skipIfMissingEnvVar(t)
    16  	skipWithoutRemoteTerraformVersion(t)
    17  
    18  	ctx := context.Background()
    19  	operations := []operationSets{
    20  		{
    21  			prep: func(t *testing.T, orgName, dir string) {
    22  				remoteWorkspace := "remote-workspace"
    23  				tfBlock := terraformConfigRemoteBackendName(orgName, remoteWorkspace)
    24  				writeMainTF(t, tfBlock, dir)
    25  			},
    26  			commands: []tfCommand{
    27  				{
    28  					command:           []string{"init"},
    29  					expectedCmdOutput: `Successfully configured the backend "remote"!`,
    30  				},
    31  				{
    32  					command:           []string{"apply", "-auto-approve"},
    33  					expectedCmdOutput: `Apply complete!`,
    34  				},
    35  			},
    36  		},
    37  		{
    38  			prep: func(t *testing.T, orgName, dir string) {
    39  				wsName := "cloud-workspace"
    40  				tfBlock := terraformConfigCloudBackendName(orgName, wsName)
    41  				writeMainTF(t, tfBlock, dir)
    42  			},
    43  			commands: []tfCommand{
    44  				{
    45  					command:           []string{"init", "-ignore-remote-version"},
    46  					expectedCmdOutput: `Migrating from backend "remote" to Terraform Cloud.`,
    47  					userInput:         []string{"yes", "yes"},
    48  					postInputOutput: []string{
    49  						`Should Terraform migrate your existing state?`,
    50  						`Terraform Cloud has been successfully initialized!`},
    51  				},
    52  				{
    53  					command:           []string{"workspace", "show"},
    54  					expectedCmdOutput: `cloud-workspace`,
    55  				},
    56  			},
    57  		},
    58  	}
    59  	validations := func(t *testing.T, orgName string) {
    60  		expectedName := "cloud-workspace"
    61  		ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName)
    62  		if err != nil {
    63  			t.Fatal(err)
    64  		}
    65  		if ws == nil {
    66  			t.Fatalf("Expected workspace %s to be present, but is not.", expectedName)
    67  		}
    68  	}
    69  
    70  	exp, err := expect.NewConsole(defaultOpts()...)
    71  	if err != nil {
    72  		t.Fatal(err)
    73  	}
    74  	defer exp.Close()
    75  
    76  	tmpDir, err := ioutil.TempDir("", "terraform-test")
    77  	if err != nil {
    78  		t.Fatal(err)
    79  	}
    80  	defer os.RemoveAll(tmpDir)
    81  
    82  	tf := e2e.NewBinary(terraformBin, tmpDir)
    83  	tf.AddEnv(cliConfigFileEnv)
    84  	defer tf.Close()
    85  
    86  	organization, cleanup := createOrganization(t)
    87  	defer cleanup()
    88  	for _, op := range operations {
    89  		op.prep(t, organization.Name, tf.WorkDir())
    90  		for _, tfCmd := range op.commands {
    91  			cmd := tf.Cmd(tfCmd.command...)
    92  			cmd.Stdin = exp.Tty()
    93  			cmd.Stdout = exp.Tty()
    94  			cmd.Stderr = exp.Tty()
    95  
    96  			err = cmd.Start()
    97  			if err != nil {
    98  				t.Fatal(err)
    99  			}
   100  
   101  			if tfCmd.expectedCmdOutput != "" {
   102  				got, err := exp.ExpectString(tfCmd.expectedCmdOutput)
   103  				if err != nil {
   104  					t.Fatalf("error while waiting for output\nwant: %s\nerror: %s\noutput\n%s", tfCmd.expectedCmdOutput, err, got)
   105  				}
   106  			}
   107  
   108  			lenInput := len(tfCmd.userInput)
   109  			lenInputOutput := len(tfCmd.postInputOutput)
   110  			if lenInput > 0 {
   111  				for i := 0; i < lenInput; i++ {
   112  					input := tfCmd.userInput[i]
   113  					exp.SendLine(input)
   114  					// use the index to find the corresponding
   115  					// output that matches the input.
   116  					if lenInputOutput-1 >= i {
   117  						output := tfCmd.postInputOutput[i]
   118  						_, err := exp.ExpectString(output)
   119  						if err != nil {
   120  							t.Fatal(err)
   121  						}
   122  					}
   123  				}
   124  			}
   125  
   126  			err = cmd.Wait()
   127  			if err != nil {
   128  				t.Fatal(err)
   129  			}
   130  		}
   131  	}
   132  
   133  	if validations != nil {
   134  		validations(t, organization.Name)
   135  	}
   136  }
   137  
   138  func Test_migrate_remote_backend_name_to_tfc_same_name(t *testing.T) {
   139  	skipIfMissingEnvVar(t)
   140  	skipWithoutRemoteTerraformVersion(t)
   141  	ctx := context.Background()
   142  	operations := []operationSets{
   143  		{
   144  			prep: func(t *testing.T, orgName, dir string) {
   145  				remoteWorkspace := "remote-workspace"
   146  				tfBlock := terraformConfigRemoteBackendName(orgName, remoteWorkspace)
   147  				writeMainTF(t, tfBlock, dir)
   148  			},
   149  			commands: []tfCommand{
   150  				{
   151  					command:           []string{"init"},
   152  					expectedCmdOutput: `Successfully configured the backend "remote"!`,
   153  				},
   154  				{
   155  					command:         []string{"apply", "-auto-approve"},
   156  					postInputOutput: []string{`Apply complete!`},
   157  				},
   158  			},
   159  		},
   160  		{
   161  			prep: func(t *testing.T, orgName, dir string) {
   162  				wsName := "remote-workspace"
   163  				tfBlock := terraformConfigCloudBackendName(orgName, wsName)
   164  				writeMainTF(t, tfBlock, dir)
   165  			},
   166  			commands: []tfCommand{
   167  				{
   168  					command:           []string{"init", "-ignore-remote-version"},
   169  					expectedCmdOutput: `Migrating from backend "remote" to Terraform Cloud.`,
   170  					userInput:         []string{"yes", "yes"},
   171  					postInputOutput: []string{
   172  						`Should Terraform migrate your existing state?`,
   173  						`Terraform Cloud has been successfully initialized!`},
   174  				},
   175  				{
   176  					command:           []string{"workspace", "show"},
   177  					expectedCmdOutput: `remote-workspace`,
   178  				},
   179  			},
   180  		},
   181  	}
   182  	validations := func(t *testing.T, orgName string) {
   183  		expectedName := "remote-workspace"
   184  		ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName)
   185  		if err != nil {
   186  			t.Fatal(err)
   187  		}
   188  		if ws == nil {
   189  			t.Fatalf("Expected workspace %s to be present, but is not.", expectedName)
   190  		}
   191  	}
   192  
   193  	exp, err := expect.NewConsole(defaultOpts()...)
   194  	if err != nil {
   195  		t.Fatal(err)
   196  	}
   197  	defer exp.Close()
   198  
   199  	tmpDir, err := ioutil.TempDir("", "terraform-test")
   200  	if err != nil {
   201  		t.Fatal(err)
   202  	}
   203  	defer os.RemoveAll(tmpDir)
   204  
   205  	tf := e2e.NewBinary(terraformBin, tmpDir)
   206  	tf.AddEnv(cliConfigFileEnv)
   207  	defer tf.Close()
   208  
   209  	organization, cleanup := createOrganization(t)
   210  	defer cleanup()
   211  	for _, op := range operations {
   212  		op.prep(t, organization.Name, tf.WorkDir())
   213  		for _, tfCmd := range op.commands {
   214  			cmd := tf.Cmd(tfCmd.command...)
   215  			cmd.Stdin = exp.Tty()
   216  			cmd.Stdout = exp.Tty()
   217  			cmd.Stderr = exp.Tty()
   218  
   219  			err = cmd.Start()
   220  			if err != nil {
   221  				t.Fatal(err)
   222  			}
   223  
   224  			if tfCmd.expectedCmdOutput != "" {
   225  				got, err := exp.ExpectString(tfCmd.expectedCmdOutput)
   226  				if err != nil {
   227  					t.Fatalf("error while waiting for output\nwant: %s\nerror: %s\noutput\n%s", tfCmd.expectedCmdOutput, err, got)
   228  				}
   229  			}
   230  
   231  			lenInput := len(tfCmd.userInput)
   232  			lenInputOutput := len(tfCmd.postInputOutput)
   233  			if lenInput > 0 {
   234  				for i := 0; i < lenInput; i++ {
   235  					input := tfCmd.userInput[i]
   236  					exp.SendLine(input)
   237  					// use the index to find the corresponding
   238  					// output that matches the input.
   239  					if lenInputOutput-1 >= i {
   240  						output := tfCmd.postInputOutput[i]
   241  						_, err := exp.ExpectString(output)
   242  						if err != nil {
   243  							t.Fatal(err)
   244  						}
   245  					}
   246  				}
   247  			}
   248  
   249  			err = cmd.Wait()
   250  			if err != nil {
   251  				t.Fatal(err)
   252  			}
   253  		}
   254  	}
   255  
   256  	if validations != nil {
   257  		validations(t, organization.Name)
   258  	}
   259  }
   260  
   261  func Test_migrate_remote_backend_name_to_tfc_name_different_org(t *testing.T) {
   262  	skipIfMissingEnvVar(t)
   263  	skipWithoutRemoteTerraformVersion(t)
   264  
   265  	ctx := context.Background()
   266  	operations := []operationSets{
   267  		{
   268  			prep: func(t *testing.T, orgName, dir string) {
   269  				remoteWorkspace := "remote-workspace"
   270  				tfBlock := terraformConfigRemoteBackendName(orgName, remoteWorkspace)
   271  				writeMainTF(t, tfBlock, dir)
   272  			},
   273  			commands: []tfCommand{
   274  				{
   275  					command:           []string{"init"},
   276  					expectedCmdOutput: `Successfully configured the backend "remote"!`,
   277  				},
   278  				{
   279  					command:         []string{"apply", "-auto-approve"},
   280  					postInputOutput: []string{`Apply complete!`},
   281  				},
   282  			},
   283  		},
   284  		{
   285  			prep: func(t *testing.T, orgName, dir string) {
   286  				wsName := "remote-workspace"
   287  				tfBlock := terraformConfigCloudBackendName(orgName, wsName)
   288  				writeMainTF(t, tfBlock, dir)
   289  			},
   290  			commands: []tfCommand{
   291  				{
   292  					command:           []string{"init", "-ignore-remote-version"},
   293  					expectedCmdOutput: `Migrating from backend "remote" to Terraform Cloud.`,
   294  					userInput:         []string{"yes", "yes"},
   295  					postInputOutput: []string{
   296  						`Should Terraform migrate your existing state?`,
   297  						`Terraform Cloud has been successfully initialized!`},
   298  				},
   299  				{
   300  					command:           []string{"workspace", "show"},
   301  					expectedCmdOutput: `remote-workspace`,
   302  				},
   303  			},
   304  		},
   305  	}
   306  	validations := func(t *testing.T, orgName string) {
   307  		expectedName := "remote-workspace"
   308  		ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName)
   309  		if err != nil {
   310  			t.Fatal(err)
   311  		}
   312  		if ws == nil {
   313  			t.Fatalf("Expected workspace %s to be present, but is not.", expectedName)
   314  		}
   315  	}
   316  
   317  	exp, err := expect.NewConsole(defaultOpts()...)
   318  	if err != nil {
   319  		t.Fatal(err)
   320  	}
   321  	defer exp.Close()
   322  
   323  	tmpDir, err := ioutil.TempDir("", "terraform-test")
   324  	if err != nil {
   325  		t.Fatal(err)
   326  	}
   327  	defer os.RemoveAll(tmpDir)
   328  
   329  	tf := e2e.NewBinary(terraformBin, tmpDir)
   330  	tf.AddEnv(cliConfigFileEnv)
   331  	defer tf.Close()
   332  
   333  	orgOne, cleanupOne := createOrganization(t)
   334  	orgTwo, cleanupTwo := createOrganization(t)
   335  	defer cleanupOne()
   336  	defer cleanupTwo()
   337  	orgs := []string{orgOne.Name, orgTwo.Name}
   338  	var orgName string
   339  	for index, op := range operations {
   340  		orgName = orgs[index]
   341  		op.prep(t, orgName, tf.WorkDir())
   342  		for _, tfCmd := range op.commands {
   343  			cmd := tf.Cmd(tfCmd.command...)
   344  			cmd.Stdin = exp.Tty()
   345  			cmd.Stdout = exp.Tty()
   346  			cmd.Stderr = exp.Tty()
   347  
   348  			err = cmd.Start()
   349  			if err != nil {
   350  				t.Fatal(err)
   351  			}
   352  
   353  			if tfCmd.expectedCmdOutput != "" {
   354  				got, err := exp.ExpectString(tfCmd.expectedCmdOutput)
   355  				if err != nil {
   356  					t.Fatalf("error while waiting for output\nwant: %s\nerror: %s\noutput\n%s", tfCmd.expectedCmdOutput, err, got)
   357  				}
   358  			}
   359  
   360  			lenInput := len(tfCmd.userInput)
   361  			lenInputOutput := len(tfCmd.postInputOutput)
   362  			if lenInput > 0 {
   363  				for i := 0; i < lenInput; i++ {
   364  					input := tfCmd.userInput[i]
   365  					exp.SendLine(input)
   366  					// use the index to find the corresponding
   367  					// output that matches the input.
   368  					if lenInputOutput-1 >= i {
   369  						output := tfCmd.postInputOutput[i]
   370  						_, err := exp.ExpectString(output)
   371  						if err != nil {
   372  							t.Fatal(err)
   373  						}
   374  					}
   375  				}
   376  			}
   377  
   378  			err = cmd.Wait()
   379  			if err != nil {
   380  				t.Fatal(err)
   381  			}
   382  		}
   383  	}
   384  
   385  	if validations != nil {
   386  		validations(t, orgName)
   387  	}
   388  }
   389  
   390  func Test_migrate_remote_backend_name_to_tfc_tags(t *testing.T) {
   391  	skipIfMissingEnvVar(t)
   392  	skipWithoutRemoteTerraformVersion(t)
   393  
   394  	ctx := context.Background()
   395  	operations := []operationSets{
   396  		{
   397  			prep: func(t *testing.T, orgName, dir string) {
   398  				remoteWorkspace := "remote-workspace"
   399  				tfBlock := terraformConfigRemoteBackendName(orgName, remoteWorkspace)
   400  				writeMainTF(t, tfBlock, dir)
   401  			},
   402  			commands: []tfCommand{
   403  				{
   404  					command:           []string{"init"},
   405  					expectedCmdOutput: `Successfully configured the backend "remote"!`,
   406  				},
   407  				{
   408  					command:         []string{"apply", "-auto-approve"},
   409  					postInputOutput: []string{`Apply complete!`},
   410  				},
   411  				{
   412  					command:           []string{"workspace", "show"},
   413  					expectedCmdOutput: `default`,
   414  				},
   415  			},
   416  		},
   417  		{
   418  			prep: func(t *testing.T, orgName, dir string) {
   419  				tag := "app"
   420  				tfBlock := terraformConfigCloudBackendTags(orgName, tag)
   421  				writeMainTF(t, tfBlock, dir)
   422  			},
   423  			commands: []tfCommand{
   424  				{
   425  					command:           []string{"init", "-ignore-remote-version"},
   426  					expectedCmdOutput: `Migrating from backend "remote" to Terraform Cloud.`,
   427  					userInput:         []string{"yes", "cloud-workspace", "yes"},
   428  					postInputOutput: []string{
   429  						`Should Terraform migrate your existing state?`,
   430  						`Terraform Cloud requires all workspaces to be given an explicit name.`,
   431  						`Terraform Cloud has been successfully initialized!`},
   432  				},
   433  				{
   434  					command:           []string{"workspace", "show"},
   435  					expectedCmdOutput: `cloud-workspace`,
   436  				},
   437  			},
   438  		},
   439  	}
   440  	validations := func(t *testing.T, orgName string) {
   441  		wsList, err := tfeClient.Workspaces.List(ctx, orgName, tfe.WorkspaceListOptions{
   442  			Tags: tfe.String("app"),
   443  		})
   444  		if err != nil {
   445  			t.Fatal(err)
   446  		}
   447  		if len(wsList.Items) != 1 {
   448  			t.Fatalf("Expected number of workspaces to be 1, but got %d", len(wsList.Items))
   449  		}
   450  		ws := wsList.Items[0]
   451  		if ws.Name != "cloud-workspace" {
   452  			t.Fatalf("Expected workspace to be `cloud-workspace`, but is %s", ws.Name)
   453  		}
   454  	}
   455  
   456  	exp, err := expect.NewConsole(defaultOpts()...)
   457  	if err != nil {
   458  		t.Fatal(err)
   459  	}
   460  	defer exp.Close()
   461  
   462  	tmpDir, err := ioutil.TempDir("", "terraform-test")
   463  	if err != nil {
   464  		t.Fatal(err)
   465  	}
   466  	defer os.RemoveAll(tmpDir)
   467  
   468  	tf := e2e.NewBinary(terraformBin, tmpDir)
   469  	tf.AddEnv(cliConfigFileEnv)
   470  	defer tf.Close()
   471  
   472  	organization, cleanup := createOrganization(t)
   473  	defer cleanup()
   474  	for _, op := range operations {
   475  		op.prep(t, organization.Name, tf.WorkDir())
   476  		for _, tfCmd := range op.commands {
   477  			cmd := tf.Cmd(tfCmd.command...)
   478  			cmd.Stdin = exp.Tty()
   479  			cmd.Stdout = exp.Tty()
   480  			cmd.Stderr = exp.Tty()
   481  
   482  			err = cmd.Start()
   483  			if err != nil {
   484  				t.Fatal(err)
   485  			}
   486  
   487  			if tfCmd.expectedCmdOutput != "" {
   488  				got, err := exp.ExpectString(tfCmd.expectedCmdOutput)
   489  				if err != nil {
   490  					t.Fatalf("error while waiting for output\nwant: %s\nerror: %s\noutput\n%s", tfCmd.expectedCmdOutput, err, got)
   491  				}
   492  			}
   493  
   494  			lenInput := len(tfCmd.userInput)
   495  			lenInputOutput := len(tfCmd.postInputOutput)
   496  			if lenInput > 0 {
   497  				for i := 0; i < lenInput; i++ {
   498  					input := tfCmd.userInput[i]
   499  					exp.SendLine(input)
   500  					// use the index to find the corresponding
   501  					// output that matches the input.
   502  					if lenInputOutput-1 >= i {
   503  						output := tfCmd.postInputOutput[i]
   504  						_, err := exp.ExpectString(output)
   505  						if err != nil {
   506  							t.Fatal(err)
   507  						}
   508  					}
   509  				}
   510  			}
   511  
   512  			err = cmd.Wait()
   513  			if err != nil {
   514  				t.Fatal(err)
   515  			}
   516  		}
   517  	}
   518  
   519  	if validations != nil {
   520  		validations(t, organization.Name)
   521  	}
   522  }
   523  
   524  func Test_migrate_remote_backend_prefix_to_tfc_name_strategy_single_workspace(t *testing.T) {
   525  	skipIfMissingEnvVar(t)
   526  	skipWithoutRemoteTerraformVersion(t)
   527  
   528  	ctx := context.Background()
   529  	operations := []operationSets{
   530  		{
   531  			prep: func(t *testing.T, orgName, dir string) {
   532  				_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-one")})
   533  				prefix := "app-"
   534  				tfBlock := terraformConfigRemoteBackendPrefix(orgName, prefix)
   535  				writeMainTF(t, tfBlock, dir)
   536  			},
   537  			commands: []tfCommand{
   538  				{
   539  					command:           []string{"init"},
   540  					expectedCmdOutput: `Terraform has been successfully initialized!`,
   541  				},
   542  				{
   543  					command:         []string{"apply", "-auto-approve"},
   544  					postInputOutput: []string{`Apply complete!`},
   545  				},
   546  			},
   547  		},
   548  		{
   549  			prep: func(t *testing.T, orgName, dir string) {
   550  				wsName := "cloud-workspace"
   551  				tfBlock := terraformConfigCloudBackendName(orgName, wsName)
   552  				writeMainTF(t, tfBlock, dir)
   553  			},
   554  			commands: []tfCommand{
   555  				{
   556  					command:           []string{"init", "-ignore-remote-version"},
   557  					expectedCmdOutput: `Migrating from backend "remote" to Terraform Cloud.`,
   558  					userInput:         []string{"yes", "yes"},
   559  					postInputOutput: []string{
   560  						`Should Terraform migrate your existing state?`,
   561  						`Terraform Cloud has been successfully initialized!`},
   562  				},
   563  				{
   564  					command:           []string{"workspace", "show"},
   565  					expectedCmdOutput: `cloud-workspace`,
   566  				},
   567  			},
   568  		},
   569  	}
   570  	validations := func(t *testing.T, orgName string) {
   571  		expectedName := "cloud-workspace"
   572  		ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName)
   573  		if err != nil {
   574  			t.Fatal(err)
   575  		}
   576  		if ws == nil {
   577  			t.Fatalf("Expected workspace %s to be present, but is not.", expectedName)
   578  		}
   579  	}
   580  
   581  	exp, err := expect.NewConsole(defaultOpts()...)
   582  	if err != nil {
   583  		t.Fatal(err)
   584  	}
   585  	defer exp.Close()
   586  
   587  	tmpDir, err := ioutil.TempDir("", "terraform-test")
   588  	if err != nil {
   589  		t.Fatal(err)
   590  	}
   591  	defer os.RemoveAll(tmpDir)
   592  
   593  	tf := e2e.NewBinary(terraformBin, tmpDir)
   594  	tf.AddEnv(cliConfigFileEnv)
   595  	defer tf.Close()
   596  
   597  	organization, cleanup := createOrganization(t)
   598  	defer cleanup()
   599  	for _, op := range operations {
   600  		op.prep(t, organization.Name, tf.WorkDir())
   601  		for _, tfCmd := range op.commands {
   602  			cmd := tf.Cmd(tfCmd.command...)
   603  			cmd.Stdin = exp.Tty()
   604  			cmd.Stdout = exp.Tty()
   605  			cmd.Stderr = exp.Tty()
   606  
   607  			err = cmd.Start()
   608  			if err != nil {
   609  				t.Fatal(err)
   610  			}
   611  
   612  			if tfCmd.expectedCmdOutput != "" {
   613  				got, err := exp.ExpectString(tfCmd.expectedCmdOutput)
   614  				if err != nil {
   615  					t.Fatalf("error while waiting for output\nwant: %s\nerror: %s\noutput\n%s", tfCmd.expectedCmdOutput, err, got)
   616  				}
   617  			}
   618  
   619  			lenInput := len(tfCmd.userInput)
   620  			lenInputOutput := len(tfCmd.postInputOutput)
   621  			if lenInput > 0 {
   622  				for i := 0; i < lenInput; i++ {
   623  					input := tfCmd.userInput[i]
   624  					exp.SendLine(input)
   625  					// use the index to find the corresponding
   626  					// output that matches the input.
   627  					if lenInputOutput-1 >= i {
   628  						output := tfCmd.postInputOutput[i]
   629  						got, err := exp.ExpectString(output)
   630  						if err != nil {
   631  							t.Fatalf("error while waiting for output\nwant: %s\nerror: %s\noutput\n%s", output, err, got)
   632  						}
   633  					}
   634  				}
   635  			}
   636  
   637  			err = cmd.Wait()
   638  			if err != nil {
   639  				t.Fatal(err)
   640  			}
   641  		}
   642  	}
   643  
   644  	if validations != nil {
   645  		validations(t, organization.Name)
   646  	}
   647  }
   648  
   649  func Test_migrate_remote_backend_prefix_to_tfc_name_strategy_multi_workspace(t *testing.T) {
   650  	skipIfMissingEnvVar(t)
   651  	skipWithoutRemoteTerraformVersion(t)
   652  
   653  	ctx := context.Background()
   654  	operations := []operationSets{
   655  		{
   656  			prep: func(t *testing.T, orgName, dir string) {
   657  				_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-one")})
   658  				_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-two")})
   659  				prefix := "app-"
   660  				tfBlock := terraformConfigRemoteBackendPrefix(orgName, prefix)
   661  				writeMainTF(t, tfBlock, dir)
   662  			},
   663  			commands: []tfCommand{
   664  				{
   665  					command:           []string{"init"},
   666  					expectedCmdOutput: `The currently selected workspace (default) does not exist.`,
   667  					userInput:         []string{"1"},
   668  					postInputOutput:   []string{`Terraform has been successfully initialized!`},
   669  				},
   670  				{
   671  					command:         []string{"apply", "-auto-approve"},
   672  					postInputOutput: []string{`Apply complete!`},
   673  				},
   674  				{
   675  					command:           []string{"workspace", "list"},
   676  					expectedCmdOutput: "* one", // app name retrieved via prefix
   677  				},
   678  				{
   679  					command:           []string{"workspace", "select", "two"},
   680  					expectedCmdOutput: `Switched to workspace "two".`, // app name retrieved via prefix
   681  				},
   682  			},
   683  		},
   684  		{
   685  			prep: func(t *testing.T, orgName, dir string) {
   686  				wsName := "cloud-workspace"
   687  				tfBlock := terraformConfigCloudBackendName(orgName, wsName)
   688  				writeMainTF(t, tfBlock, dir)
   689  			},
   690  			commands: []tfCommand{
   691  				{
   692  					command:           []string{"init", "-ignore-remote-version"},
   693  					expectedCmdOutput: `Do you want to copy only your current workspace?`,
   694  					userInput:         []string{"yes"},
   695  					postInputOutput: []string{
   696  						`Terraform Cloud has been successfully initialized!`},
   697  				},
   698  				{
   699  					command:           []string{"workspace", "show"},
   700  					expectedCmdOutput: `cloud-workspace`,
   701  				},
   702  			},
   703  		},
   704  	}
   705  	validations := func(t *testing.T, orgName string) {
   706  		expectedName := "cloud-workspace"
   707  		ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName)
   708  		if err != nil {
   709  			t.Fatal(err)
   710  		}
   711  		if ws == nil {
   712  			t.Fatalf("Expected workspace %s to be present, but is not.", expectedName)
   713  		}
   714  		wsList, err := tfeClient.Workspaces.List(ctx, orgName, tfe.WorkspaceListOptions{})
   715  		if err != nil {
   716  			t.Fatal(err)
   717  		}
   718  		if len(wsList.Items) != 3 {
   719  			t.Fatalf("expected number of workspaces in this org to be 3, but got %d", len(wsList.Items))
   720  		}
   721  		_, empty := getWorkspace(wsList.Items, "cloud-workspace")
   722  		if empty {
   723  			t.Fatalf("expected workspaces to include 'cloud-workspace' but didn't.")
   724  		}
   725  		_, empty = getWorkspace(wsList.Items, "app-one")
   726  		if empty {
   727  			t.Fatalf("expected workspaces to include 'app-one' but didn't.")
   728  		}
   729  		_, empty = getWorkspace(wsList.Items, "app-two")
   730  		if empty {
   731  			t.Fatalf("expected workspaces to include 'app-two' but didn't.")
   732  		}
   733  	}
   734  
   735  	exp, err := expect.NewConsole(defaultOpts()...)
   736  	if err != nil {
   737  		t.Fatal(err)
   738  	}
   739  	defer exp.Close()
   740  
   741  	tmpDir, err := ioutil.TempDir("", "terraform-test")
   742  	if err != nil {
   743  		t.Fatal(err)
   744  	}
   745  	defer os.RemoveAll(tmpDir)
   746  
   747  	tf := e2e.NewBinary(terraformBin, tmpDir)
   748  	tf.AddEnv(cliConfigFileEnv)
   749  	defer tf.Close()
   750  
   751  	organization, cleanup := createOrganization(t)
   752  	defer cleanup()
   753  	for _, op := range operations {
   754  		op.prep(t, organization.Name, tf.WorkDir())
   755  		for _, tfCmd := range op.commands {
   756  			cmd := tf.Cmd(tfCmd.command...)
   757  			cmd.Stdin = exp.Tty()
   758  			cmd.Stdout = exp.Tty()
   759  			cmd.Stderr = exp.Tty()
   760  
   761  			err = cmd.Start()
   762  			if err != nil {
   763  				t.Fatal(err)
   764  			}
   765  
   766  			if tfCmd.expectedCmdOutput != "" {
   767  				got, err := exp.ExpectString(tfCmd.expectedCmdOutput)
   768  				if err != nil {
   769  					t.Fatalf("error while waiting for output\nwant: %s\nerror: %s\noutput\n%s", tfCmd.expectedCmdOutput, err, got)
   770  				}
   771  			}
   772  
   773  			lenInput := len(tfCmd.userInput)
   774  			lenInputOutput := len(tfCmd.postInputOutput)
   775  			if lenInput > 0 {
   776  				for i := 0; i < lenInput; i++ {
   777  					input := tfCmd.userInput[i]
   778  					exp.SendLine(input)
   779  					// use the index to find the corresponding
   780  					// output that matches the input.
   781  					if lenInputOutput-1 >= i {
   782  						output := tfCmd.postInputOutput[i]
   783  						_, err := exp.ExpectString(output)
   784  						if err != nil {
   785  							t.Fatal(err)
   786  						}
   787  					}
   788  				}
   789  			}
   790  
   791  			err = cmd.Wait()
   792  			if err != nil {
   793  				t.Fatal(err)
   794  			}
   795  		}
   796  	}
   797  
   798  	if validations != nil {
   799  		validations(t, organization.Name)
   800  	}
   801  }
   802  
   803  func Test_migrate_remote_backend_prefix_to_tfc_tags_strategy_single_workspace(t *testing.T) {
   804  	skipIfMissingEnvVar(t)
   805  	skipWithoutRemoteTerraformVersion(t)
   806  
   807  	ctx := context.Background()
   808  	operations := []operationSets{
   809  		{
   810  			prep: func(t *testing.T, orgName, dir string) {
   811  				_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-one")})
   812  				prefix := "app-"
   813  				tfBlock := terraformConfigRemoteBackendPrefix(orgName, prefix)
   814  				writeMainTF(t, tfBlock, dir)
   815  			},
   816  			commands: []tfCommand{
   817  				{
   818  					command:           []string{"init"},
   819  					expectedCmdOutput: `Terraform has been successfully initialized!`,
   820  				},
   821  				{
   822  					command:         []string{"apply", "-auto-approve"},
   823  					postInputOutput: []string{`Apply complete!`},
   824  				},
   825  			},
   826  		},
   827  		{
   828  			prep: func(t *testing.T, orgName, dir string) {
   829  				tag := "app"
   830  				tfBlock := terraformConfigCloudBackendTags(orgName, tag)
   831  				writeMainTF(t, tfBlock, dir)
   832  			},
   833  			commands: []tfCommand{
   834  				{
   835  					command:           []string{"init", "-ignore-remote-version"},
   836  					expectedCmdOutput: `Migrating from backend "remote" to Terraform Cloud.`,
   837  					userInput:         []string{"yes", "cloud-workspace", "yes"},
   838  					postInputOutput: []string{
   839  						`Should Terraform migrate your existing state?`,
   840  						`Terraform Cloud requires all workspaces to be given an explicit name.`,
   841  						`Terraform Cloud has been successfully initialized!`},
   842  				},
   843  				{
   844  					command:           []string{"workspace", "list"},
   845  					expectedCmdOutput: `cloud-workspace`,
   846  				},
   847  			},
   848  		},
   849  	}
   850  	validations := func(t *testing.T, orgName string) {
   851  		expectedName := "cloud-workspace"
   852  		ws, err := tfeClient.Workspaces.Read(ctx, orgName, expectedName)
   853  		if err != nil {
   854  			t.Fatal(err)
   855  		}
   856  		if ws == nil {
   857  			t.Fatalf("Expected workspace %s to be present, but is not.", expectedName)
   858  		}
   859  	}
   860  
   861  	exp, err := expect.NewConsole(defaultOpts()...)
   862  	if err != nil {
   863  		t.Fatal(err)
   864  	}
   865  	defer exp.Close()
   866  
   867  	tmpDir, err := ioutil.TempDir("", "terraform-test")
   868  	if err != nil {
   869  		t.Fatal(err)
   870  	}
   871  	defer os.RemoveAll(tmpDir)
   872  
   873  	tf := e2e.NewBinary(terraformBin, tmpDir)
   874  	tf.AddEnv(cliConfigFileEnv)
   875  	defer tf.Close()
   876  
   877  	organization, cleanup := createOrganization(t)
   878  	defer cleanup()
   879  	for _, op := range operations {
   880  		op.prep(t, organization.Name, tf.WorkDir())
   881  		for _, tfCmd := range op.commands {
   882  			cmd := tf.Cmd(tfCmd.command...)
   883  			cmd.Stdin = exp.Tty()
   884  			cmd.Stdout = exp.Tty()
   885  			cmd.Stderr = exp.Tty()
   886  
   887  			err = cmd.Start()
   888  			if err != nil {
   889  				t.Fatal(err)
   890  			}
   891  
   892  			if tfCmd.expectedCmdOutput != "" {
   893  				got, err := exp.ExpectString(tfCmd.expectedCmdOutput)
   894  				if err != nil {
   895  					t.Fatalf("error while waiting for output\nwant: %s\nerror: %s\noutput\n%s", tfCmd.expectedCmdOutput, err, got)
   896  				}
   897  			}
   898  
   899  			lenInput := len(tfCmd.userInput)
   900  			lenInputOutput := len(tfCmd.postInputOutput)
   901  			if lenInput > 0 {
   902  				for i := 0; i < lenInput; i++ {
   903  					input := tfCmd.userInput[i]
   904  					exp.SendLine(input)
   905  					// use the index to find the corresponding
   906  					// output that matches the input.
   907  					if lenInputOutput-1 >= i {
   908  						output := tfCmd.postInputOutput[i]
   909  						_, err := exp.ExpectString(output)
   910  						if err != nil {
   911  							t.Fatal(err)
   912  						}
   913  					}
   914  				}
   915  			}
   916  
   917  			err = cmd.Wait()
   918  			if err != nil {
   919  				t.Fatal(err)
   920  			}
   921  		}
   922  	}
   923  
   924  	if validations != nil {
   925  		validations(t, organization.Name)
   926  	}
   927  }
   928  
   929  func Test_migrate_remote_backend_prefix_to_tfc_tags_strategy_multi_workspace(t *testing.T) {
   930  	skipIfMissingEnvVar(t)
   931  	skipWithoutRemoteTerraformVersion(t)
   932  
   933  	ctx := context.Background()
   934  	operations := []operationSets{
   935  		{
   936  			prep: func(t *testing.T, orgName, dir string) {
   937  				_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-one")})
   938  				_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{Name: tfe.String("app-two")})
   939  				prefix := "app-"
   940  				tfBlock := terraformConfigRemoteBackendPrefix(orgName, prefix)
   941  				writeMainTF(t, tfBlock, dir)
   942  			},
   943  			commands: []tfCommand{
   944  				{
   945  					command:           []string{"init"},
   946  					expectedCmdOutput: `The currently selected workspace (default) does not exist.`,
   947  					userInput:         []string{"1"},
   948  					postInputOutput:   []string{`Terraform has been successfully initialized!`},
   949  				},
   950  				{
   951  					command:           []string{"apply"},
   952  					expectedCmdOutput: `Do you want to perform these actions in workspace "app-one"?`,
   953  					userInput:         []string{"yes"},
   954  					postInputOutput:   []string{`Apply complete!`},
   955  				},
   956  				{
   957  					command: []string{"workspace", "select", "two"},
   958  				},
   959  				{
   960  					command:           []string{"apply"},
   961  					expectedCmdOutput: `Do you want to perform these actions in workspace "app-two"?`,
   962  					userInput:         []string{"yes"},
   963  					postInputOutput:   []string{`Apply complete!`},
   964  				},
   965  			},
   966  		},
   967  		{
   968  			prep: func(t *testing.T, orgName, dir string) {
   969  				tag := "app"
   970  				tfBlock := terraformConfigCloudBackendTags(orgName, tag)
   971  				writeMainTF(t, tfBlock, dir)
   972  			},
   973  			commands: []tfCommand{
   974  				{
   975  					command:           []string{"init", "-ignore-remote-version"},
   976  					expectedCmdOutput: `Do you wish to proceed?`,
   977  					userInput:         []string{"yes"},
   978  					postInputOutput:   []string{`Terraform Cloud has been successfully initialized!`},
   979  				},
   980  				{
   981  					command:           []string{"workspace", "show"},
   982  					expectedCmdOutput: "app-two",
   983  				},
   984  				{
   985  					command:           []string{"workspace", "select", "app-one"},
   986  					expectedCmdOutput: `Switched to workspace "app-one".`,
   987  				},
   988  			},
   989  		},
   990  	}
   991  	validations := func(t *testing.T, orgName string) {
   992  		wsList, err := tfeClient.Workspaces.List(ctx, orgName, tfe.WorkspaceListOptions{
   993  			Tags: tfe.String("app"),
   994  		})
   995  		if err != nil {
   996  			t.Fatal(err)
   997  		}
   998  		if len(wsList.Items) != 2 {
   999  			t.Logf("Expected the number of workspaces to be 2, but got %d", len(wsList.Items))
  1000  		}
  1001  		ws, empty := getWorkspace(wsList.Items, "app-one")
  1002  		if empty {
  1003  			t.Fatalf("expected workspaces to include 'app-one' but didn't.")
  1004  		}
  1005  		if len(ws.TagNames) == 0 {
  1006  			t.Fatalf("expected workspaces 'one' to have tags.")
  1007  		}
  1008  		ws, empty = getWorkspace(wsList.Items, "app-two")
  1009  		if empty {
  1010  			t.Fatalf("expected workspaces to include 'app-two' but didn't.")
  1011  		}
  1012  		if len(ws.TagNames) == 0 {
  1013  			t.Fatalf("expected workspaces 'app-two' to have tags.")
  1014  		}
  1015  	}
  1016  
  1017  	exp, err := expect.NewConsole(defaultOpts()...)
  1018  	if err != nil {
  1019  		t.Fatal(err)
  1020  	}
  1021  	defer exp.Close()
  1022  
  1023  	tmpDir, err := ioutil.TempDir("", "terraform-test")
  1024  	if err != nil {
  1025  		t.Fatal(err)
  1026  	}
  1027  	defer os.RemoveAll(tmpDir)
  1028  
  1029  	tf := e2e.NewBinary(terraformBin, tmpDir)
  1030  	tf.AddEnv(cliConfigFileEnv)
  1031  	defer tf.Close()
  1032  
  1033  	organization, cleanup := createOrganization(t)
  1034  	defer cleanup()
  1035  	for _, op := range operations {
  1036  		op.prep(t, organization.Name, tf.WorkDir())
  1037  		for _, tfCmd := range op.commands {
  1038  			cmd := tf.Cmd(tfCmd.command...)
  1039  			cmd.Stdin = exp.Tty()
  1040  			cmd.Stdout = exp.Tty()
  1041  			cmd.Stderr = exp.Tty()
  1042  
  1043  			err = cmd.Start()
  1044  			if err != nil {
  1045  				t.Fatal(err)
  1046  			}
  1047  
  1048  			if tfCmd.expectedCmdOutput != "" {
  1049  				got, err := exp.ExpectString(tfCmd.expectedCmdOutput)
  1050  				if err != nil {
  1051  					t.Fatalf("error while waiting for output\nwant: %s\nerror: %s\noutput\n%s", tfCmd.expectedCmdOutput, err, got)
  1052  				}
  1053  			}
  1054  
  1055  			lenInput := len(tfCmd.userInput)
  1056  			lenInputOutput := len(tfCmd.postInputOutput)
  1057  			if lenInput > 0 {
  1058  				for i := 0; i < lenInput; i++ {
  1059  					input := tfCmd.userInput[i]
  1060  					exp.SendLine(input)
  1061  					// use the index to find the corresponding
  1062  					// output that matches the input.
  1063  					if lenInputOutput-1 >= i {
  1064  						output := tfCmd.postInputOutput[i]
  1065  						_, err := exp.ExpectString(output)
  1066  						if err != nil {
  1067  							t.Fatal(err)
  1068  						}
  1069  					}
  1070  				}
  1071  			}
  1072  
  1073  			err = cmd.Wait()
  1074  			if err != nil {
  1075  				t.Fatal(err)
  1076  			}
  1077  		}
  1078  	}
  1079  
  1080  	if validations != nil {
  1081  		validations(t, organization.Name)
  1082  	}
  1083  }