From 3f2136d7eeec1540683167a452cdb4f11cfd9dfa Mon Sep 17 00:00:00 2001 From: Jack Bruno Date: Mon, 18 Sep 2017 12:41:30 -0600 Subject: [PATCH] cli: terraform import -ignore-missing-config This new option allows importing without configuration present. Configuration is required by default as a confirmation that the provided resource name is correct, but it can be useful to override this in tools that wrap Terraform to do more involved operations. --- command/import.go | 83 ++++++++++++++++++++++++------------------ command/import_test.go | 41 +++++++++++++++++++++ command/meta.go | 3 ++ 3 files changed, 92 insertions(+), 35 deletions(-) diff --git a/command/import.go b/command/import.go index e455bb53c2..0dc2003a02 100644 --- a/command/import.go +++ b/command/import.go @@ -41,6 +41,7 @@ func (c *ImportCommand) Run(args []string) int { cmdFlags.StringVar(&c.Meta.provider, "provider", "", "provider") cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state") cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout") + cmdFlags.BoolVar(&c.Meta.allowMissingConfig, "allow-missing-config", false, "allow missing config") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { return 1 @@ -103,7 +104,7 @@ func (c *ImportCommand) Run(args []string) int { break } } - if rc == nil { + if !c.Meta.allowMissingConfig && rc == nil { modulePath := addr.WholeModuleAddress().String() if modulePath == "" { modulePath = "the root module" @@ -182,6 +183,10 @@ func (c *ImportCommand) Run(args []string) int { c.Ui.Output(c.Colorize().Color("[reset][green]\n" + importCommandSuccessMsg)) + if c.Meta.allowMissingConfig && rc == nil { + c.Ui.Output(c.Colorize().Color("[reset][yellow]\n" + importCommandAllowMissingResourceMsg)) + } + return 0 } @@ -202,11 +207,13 @@ Usage: terraform import [options] ADDR ID determine the ID syntax to use. It typically matches directly to the ID that the provider uses. - In the current state of Terraform import, the resource is only imported - into your state file. Once it is imported, you must manually write - configuration for the new resource or Terraform will mark it for destruction. - Future versions of Terraform will expand the functionality of Terraform - import. + The current implementation of Terraform import can only import resources + into the state. It does not generate configuration. A future version of + Terraform will also generate configuration. + + Because of this, prior to running terraform import it is necessary to write + a resource configuration block for the resource manually, to which the + imported object will be attached. This command will not modify your infrastructure, but it will make network requests to inspect parts of your infrastructure relevant to @@ -214,41 +221,43 @@ Usage: terraform import [options] ADDR ID Options: - -backup=path Path to backup the existing state file before - modifying. Defaults to the "-state-out" path with - ".backup" extension. Set to "-" to disable backup. + -backup=path Path to backup the existing state file before + modifying. Defaults to the "-state-out" path with + ".backup" extension. Set to "-" to disable backup. - -config=path Path to a directory of Terraform configuration files - to use to configure the provider. Defaults to pwd. - If no config files are present, they must be provided - via the input prompts or env vars. + -config=path Path to a directory of Terraform configuration files + to use to configure the provider. Defaults to pwd. + If no config files are present, they must be provided + via the input prompts or env vars. - -input=true Ask for input for variables if not directly set. + -allow-missing-config Allow import when no resource configuration block exists. - -lock=true Lock the state file when locking is supported. + -input=true Ask for input for variables if not directly set. - -lock-timeout=0s Duration to retry a state lock. + -lock=true Lock the state file when locking is supported. - -no-color If specified, output won't contain any color. + -lock-timeout=0s Duration to retry a state lock. - -provider=provider Specific provider to use for import. This is used for - specifying aliases, such as "aws.eu". Defaults to the - normal provider prefix of the resource being imported. + -no-color If specified, output won't contain any color. - -state=PATH Path to the source state file. Defaults to the configured - backend, or "terraform.tfstate" + -provider=provider Specific provider to use for import. This is used for + specifying aliases, such as "aws.eu". Defaults to the + normal provider prefix of the resource being imported. - -state-out=PATH Path to the destination state file to write to. If this - isn't specified, the source state file will be used. This - can be a new or existing path. + -state=PATH Path to the source state file. Defaults to the configured + backend, or "terraform.tfstate" - -var 'foo=bar' Set a variable in the Terraform configuration. This - flag can be set multiple times. This is only useful - with the "-config" flag. + -state-out=PATH Path to the destination state file to write to. If this + isn't specified, the source state file will be used. This + can be a new or existing path. - -var-file=foo Set variables in the Terraform configuration from - a file. If "terraform.tfvars" or any ".auto.tfvars" - files are present, they will be automatically loaded. + -var 'foo=bar' Set a variable in the Terraform configuration. This + flag can be set multiple times. This is only useful + with the "-config" flag. + + -var-file=foo Set variables in the Terraform configuration from + a file. If "terraform.tfvars" or any ".auto.tfvars" + files are present, they will be automatically loaded. ` @@ -300,9 +309,13 @@ const importCommandSuccessMsg = `Import successful! The resources that were imported are shown above. These resources are now in your Terraform state and will henceforth be managed by Terraform. +` + +const importCommandAllowMissingResourceMsg = `Import does not generate resource configuration, you must create a resource +configuration block that matches the current or desired state manually. -Import does not generate configuration, so the next step is to ensure that -the resource configurations match the current (or desired) state of the -imported resources. You can use the output from "terraform plan" to verify that -the configuration is correct and complete. +If there is no matching resource configuration block for the imported +resource, Terraform will delete the resource on the next "terraform apply". +It is recommended that you run "terraform plan" to verify that the +configuration is correct and complete. ` diff --git a/command/import_test.go b/command/import_test.go index 417e6143aa..9f0f2d9924 100644 --- a/command/import_test.go +++ b/command/import_test.go @@ -403,6 +403,47 @@ func TestImport_customProvider(t *testing.T) { testStateOutput(t, statePath, testImportCustomProviderStr) } +func TestImport_allowMissingResourceConfig(t *testing.T) { + defer testChdir(t, testFixturePath("import-missing-resource-config"))() + + statePath := testTempFile(t) + + p := testProvider() + ui := new(cli.MockUi) + c := &ImportCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + Ui: ui, + }, + } + + p.ImportStateFn = nil + p.ImportStateReturn = []*terraform.InstanceState{ + { + ID: "yay", + Ephemeral: terraform.EphemeralState{ + Type: "test_instance", + }, + }, + } + + args := []string{ + "-state", statePath, + "-allow-missing-config", + "test_instance.foo", + "bar", + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + if !p.ImportStateCalled { + t.Fatal("ImportState should be called") + } + + testStateOutput(t, statePath, testImportStr) +} + func TestImport_missingResourceConfig(t *testing.T) { defer testChdir(t, testFixturePath("import-missing-resource-config"))() diff --git a/command/meta.go b/command/meta.go index a67a20c2ee..8028c3fe6a 100644 --- a/command/meta.go +++ b/command/meta.go @@ -142,6 +142,9 @@ type Meta struct { errWriter *io.PipeWriter // done chan to wait for the scanner goroutine errScannerDone chan struct{} + + // Used with the import command to allow import of state when no matching config exists. + allowMissingConfig bool } type PluginOverrides struct {