From c7f5450a964e0fa89a02303232972b4777e6ec63 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Tue, 8 Mar 2016 14:37:34 -0600 Subject: [PATCH] command: Add `terraform untaint` - [x] Docs - [x] Command Unit Tests - [x] State Unit Tests Closes #4820 --- command/command_test.go | 2 +- command/untaint.go | 184 +++++++ command/untaint_test.go | 478 ++++++++++++++++++ commands.go | 6 + terraform/state.go | 30 ++ terraform/state_test.go | 97 ++++ .../docs/commands/untaint.html.markdown | 61 +++ website/source/layouts/docs.erb | 3 + 8 files changed, 860 insertions(+), 1 deletion(-) create mode 100644 command/untaint.go create mode 100644 command/untaint_test.go create mode 100644 website/source/docs/commands/untaint.html.markdown diff --git a/command/command_test.go b/command/command_test.go index 954579c3d..5d150bcb1 100644 --- a/command/command_test.go +++ b/command/command_test.go @@ -202,7 +202,7 @@ func testStateOutput(t *testing.T, path string, expected string) { actual := strings.TrimSpace(newState.String()) expected = strings.TrimSpace(expected) if actual != expected { - t.Fatalf("bad:\n\n%s", actual) + t.Fatalf("expected:\n%s\nactual:\n%s", expected, actual) } } diff --git a/command/untaint.go b/command/untaint.go new file mode 100644 index 000000000..b34b9a2fd --- /dev/null +++ b/command/untaint.go @@ -0,0 +1,184 @@ +package command + +import ( + "fmt" + "log" + "strings" +) + +// UntaintCommand is a cli.Command implementation that manually untaints +// a resource, marking it as primary and ready for service. +type UntaintCommand struct { + Meta +} + +func (c *UntaintCommand) Run(args []string) int { + args = c.Meta.process(args, false) + + var allowMissing bool + var module string + var index int + cmdFlags := c.Meta.flagSet("untaint") + cmdFlags.BoolVar(&allowMissing, "allow-missing", false, "module") + cmdFlags.StringVar(&module, "module", "", "module") + cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path") + cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path") + cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path") + cmdFlags.IntVar(&index, "index", -1, "index") + cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } + if err := cmdFlags.Parse(args); err != nil { + return 1 + } + + // Require the one argument for the resource to untaint + args = cmdFlags.Args() + if len(args) != 1 { + c.Ui.Error("The untaint command expects exactly one argument.") + cmdFlags.Usage() + return 1 + } + + name := args[0] + if module == "" { + module = "root" + } else { + module = "root." + module + } + + // Get the state that we'll be modifying + state, err := c.State() + if err != nil { + c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err)) + return 1 + } + + // Get the actual state structure + s := state.State() + if s.Empty() { + if allowMissing { + return c.allowMissingExit(name, module) + } + + c.Ui.Error(fmt.Sprintf( + "The state is empty. The most common reason for this is that\n" + + "an invalid state file path was given or Terraform has never\n " + + "been run for this infrastructure. Infrastructure must exist\n" + + "for it to be untainted.")) + return 1 + } + + // Get the proper module holding the resource we want to untaint + modPath := strings.Split(module, ".") + mod := s.ModuleByPath(modPath) + if mod == nil { + if allowMissing { + return c.allowMissingExit(name, module) + } + + c.Ui.Error(fmt.Sprintf( + "The module %s could not be found. There is nothing to untaint.", + module)) + return 1 + } + + // If there are no resources in this module, it is an error + if len(mod.Resources) == 0 { + if allowMissing { + return c.allowMissingExit(name, module) + } + + c.Ui.Error(fmt.Sprintf( + "The module %s has no resources. There is nothing to untaint.", + module)) + return 1 + } + + // Get the resource we're looking for + rs, ok := mod.Resources[name] + if !ok { + if allowMissing { + return c.allowMissingExit(name, module) + } + + c.Ui.Error(fmt.Sprintf( + "The resource %s couldn't be found in the module %s.", + name, + module)) + return 1 + } + + // Untaint the resource + if err := rs.Untaint(index); err != nil { + c.Ui.Error(fmt.Sprintf("Error untainting %s: %s", name, err)) + c.Ui.Error("You can use `terraform show` to inspect the current state.") + return 1 + } + + log.Printf("[INFO] Writing state output to: %s", c.Meta.StateOutPath()) + if err := c.Meta.PersistState(s); err != nil { + c.Ui.Error(fmt.Sprintf("Error writing state file: %s", err)) + return 1 + } + + c.Ui.Output(fmt.Sprintf( + "The resource %s in the module %s has been successfully untainted!", + name, module)) + return 0 +} + +func (c *UntaintCommand) Help() string { + helpText := ` +Usage: terraform untaint [options] name + + Manually unmark a resource as tainted, restoring it as the primary + instance in the state. This reverses either a manual 'terraform taint' + or the result of provisioners failing on a resource. + + This will not modify your infrastructure. This command changes your + state to unmark a resource as tainted. This command can be undone by + reverting the state backup file that is created, or by running + 'terraform taint' on the resource. + +Options: + + -allow-missing If specified, the command will succeed (exit code 0) + even if the resource is missing. + + -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. + + -index=n Selects a single tainted instance when there are more + than one tainted instances present in the state for a + given resource. This flag is required when multiple + tainted instances are present. The vast majority of the + time, there is a maxiumum of one tainted instance per + resource, so this flag can be safely omitted. + + -module=path The module path where the resource lives. By + default this will be root. Child modules can be specified + by names. Ex. "consul" or "consul.vpc" (nested modules). + + -no-color If specified, output won't contain any color. + + -state=path Path to read and save state (unless state-out + is specified). Defaults to "terraform.tfstate". + + -state-out=path Path to write updated state file. By default, the + "-state" path will be used. + +` + return strings.TrimSpace(helpText) +} + +func (c *UntaintCommand) Synopsis() string { + return "Manually unmark a resource as tainted" +} + +func (c *UntaintCommand) allowMissingExit(name, module string) int { + c.Ui.Output(fmt.Sprintf( + "The resource %s in the module %s was not found, but\n"+ + "-allow-missing is set, so we're exiting successfully.", + name, module)) + return 0 +} diff --git a/command/untaint_test.go b/command/untaint_test.go new file mode 100644 index 000000000..4627f9fa8 --- /dev/null +++ b/command/untaint_test.go @@ -0,0 +1,478 @@ +package command + +import ( + "os" + "strings" + "testing" + + "github.com/hashicorp/terraform/terraform" + "github.com/mitchellh/cli" +) + +func TestUntaint(t *testing.T) { + state := &terraform.State{ + Modules: []*terraform.ModuleState{ + &terraform.ModuleState{ + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test_instance.foo": &terraform.ResourceState{ + Type: "test_instance", + Tainted: []*terraform.InstanceState{ + &terraform.InstanceState{ID: "bar"}, + }, + }, + }, + }, + }, + } + statePath := testStateFile(t, state) + + ui := new(cli.MockUi) + c := &UntaintCommand{ + Meta: Meta{ + Ui: ui, + }, + } + + args := []string{ + "-state", statePath, + "test_instance.foo", + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + expected := strings.TrimSpace(` +test_instance.foo: + ID = bar + `) + testStateOutput(t, statePath, expected) +} + +func TestUntaint_indexRequired(t *testing.T) { + state := &terraform.State{ + Modules: []*terraform.ModuleState{ + &terraform.ModuleState{ + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test_instance.foo": &terraform.ResourceState{ + Type: "test_instance", + Tainted: []*terraform.InstanceState{ + &terraform.InstanceState{ID: "bar"}, + &terraform.InstanceState{ID: "bar2"}, + }, + }, + }, + }, + }, + } + statePath := testStateFile(t, state) + + ui := new(cli.MockUi) + c := &UntaintCommand{ + Meta: Meta{ + Ui: ui, + }, + } + + args := []string{ + "-state", statePath, + "test_instance.foo", + } + if code := c.Run(args); code == 0 { + t.Fatalf("Expected non-zero exit. Output:\n\n%s", ui.OutputWriter.String()) + } + + // Nothing should have gotten untainted + expected := strings.TrimSpace(` +test_instance.foo: (2 tainted) + ID = + Tainted ID 1 = bar + Tainted ID 2 = bar2 + `) + testStateOutput(t, statePath, expected) + + // Should have gotten an error message mentioning index + errOut := ui.ErrorWriter.String() + errContains := "please specify an index" + if !strings.Contains(errOut, errContains) { + t.Fatalf("Expected err output: %s, to contain: %s", errOut, errContains) + } +} + +func TestUntaint_indexSpecified(t *testing.T) { + state := &terraform.State{ + Modules: []*terraform.ModuleState{ + &terraform.ModuleState{ + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test_instance.foo": &terraform.ResourceState{ + Type: "test_instance", + Tainted: []*terraform.InstanceState{ + &terraform.InstanceState{ID: "bar"}, + &terraform.InstanceState{ID: "bar2"}, + }, + }, + }, + }, + }, + } + statePath := testStateFile(t, state) + + ui := new(cli.MockUi) + c := &UntaintCommand{ + Meta: Meta{ + Ui: ui, + }, + } + + args := []string{ + "-state", statePath, + "-index", "1", + "test_instance.foo", + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + // Nothing should have gotten untainted + expected := strings.TrimSpace(` +test_instance.foo: (1 tainted) + ID = bar2 + Tainted ID 1 = bar + `) + testStateOutput(t, statePath, expected) +} + +func TestUntaint_backup(t *testing.T) { + // Get a temp cwd + tmp, cwd := testCwd(t) + defer testFixCwd(t, tmp, cwd) + + // Write the temp state + state := &terraform.State{ + Modules: []*terraform.ModuleState{ + &terraform.ModuleState{ + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test_instance.foo": &terraform.ResourceState{ + Type: "test_instance", + Tainted: []*terraform.InstanceState{ + &terraform.InstanceState{ID: "bar"}, + }, + }, + }, + }, + }, + } + path := testStateFileDefault(t, state) + + ui := new(cli.MockUi) + c := &UntaintCommand{ + Meta: Meta{ + Ui: ui, + }, + } + + args := []string{ + "test_instance.foo", + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + // Backup is still tainted + testStateOutput(t, path+".backup", strings.TrimSpace(` +test_instance.foo: (1 tainted) + ID = + Tainted ID 1 = bar + `)) + + // State is untainted + testStateOutput(t, path, strings.TrimSpace(` +test_instance.foo: + ID = bar + `)) +} + +func TestUntaint_backupDisable(t *testing.T) { + // Get a temp cwd + tmp, cwd := testCwd(t) + defer testFixCwd(t, tmp, cwd) + + // Write the temp state + state := &terraform.State{ + Modules: []*terraform.ModuleState{ + &terraform.ModuleState{ + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test_instance.foo": &terraform.ResourceState{ + Type: "test_instance", + Tainted: []*terraform.InstanceState{ + &terraform.InstanceState{ID: "bar"}, + }, + }, + }, + }, + }, + } + path := testStateFileDefault(t, state) + + ui := new(cli.MockUi) + c := &UntaintCommand{ + Meta: Meta{ + Ui: ui, + }, + } + + args := []string{ + "-backup", "-", + "test_instance.foo", + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + if _, err := os.Stat(path + ".backup"); err == nil { + t.Fatal("backup path should not exist") + } + + testStateOutput(t, path, strings.TrimSpace(` +test_instance.foo: + ID = bar + `)) +} + +func TestUntaint_badState(t *testing.T) { + ui := new(cli.MockUi) + c := &UntaintCommand{ + Meta: Meta{ + Ui: ui, + }, + } + + args := []string{ + "-state", "i-should-not-exist-ever", + "foo", + } + if code := c.Run(args); code != 1 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } +} + +func TestUntaint_defaultState(t *testing.T) { + // Get a temp cwd + tmp, cwd := testCwd(t) + defer testFixCwd(t, tmp, cwd) + + // Write the temp state + state := &terraform.State{ + Modules: []*terraform.ModuleState{ + &terraform.ModuleState{ + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test_instance.foo": &terraform.ResourceState{ + Type: "test_instance", + Tainted: []*terraform.InstanceState{ + &terraform.InstanceState{ID: "bar"}, + }, + }, + }, + }, + }, + } + path := testStateFileDefault(t, state) + + ui := new(cli.MockUi) + c := &UntaintCommand{ + Meta: Meta{ + Ui: ui, + }, + } + + args := []string{ + "test_instance.foo", + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + testStateOutput(t, path, strings.TrimSpace(` +test_instance.foo: + ID = bar + `)) +} + +func TestUntaint_missing(t *testing.T) { + state := &terraform.State{ + Modules: []*terraform.ModuleState{ + &terraform.ModuleState{ + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test_instance.foo": &terraform.ResourceState{ + Type: "test_instance", + Tainted: []*terraform.InstanceState{ + &terraform.InstanceState{ID: "bar"}, + }, + }, + }, + }, + }, + } + statePath := testStateFile(t, state) + + ui := new(cli.MockUi) + c := &UntaintCommand{ + Meta: Meta{ + Ui: ui, + }, + } + + args := []string{ + "-state", statePath, + "test_instance.bar", + } + if code := c.Run(args); code == 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.OutputWriter.String()) + } +} + +func TestUntaint_missingAllow(t *testing.T) { + state := &terraform.State{ + Modules: []*terraform.ModuleState{ + &terraform.ModuleState{ + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test_instance.foo": &terraform.ResourceState{ + Type: "test_instance", + Tainted: []*terraform.InstanceState{ + &terraform.InstanceState{ID: "bar"}, + }, + }, + }, + }, + }, + } + statePath := testStateFile(t, state) + + ui := new(cli.MockUi) + c := &UntaintCommand{ + Meta: Meta{ + Ui: ui, + }, + } + + args := []string{ + "-allow-missing", + "-state", statePath, + "test_instance.bar", + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } +} + +func TestUntaint_stateOut(t *testing.T) { + // Get a temp cwd + tmp, cwd := testCwd(t) + defer testFixCwd(t, tmp, cwd) + + // Write the temp state + state := &terraform.State{ + Modules: []*terraform.ModuleState{ + &terraform.ModuleState{ + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test_instance.foo": &terraform.ResourceState{ + Type: "test_instance", + Tainted: []*terraform.InstanceState{ + &terraform.InstanceState{ID: "bar"}, + }, + }, + }, + }, + }, + } + path := testStateFileDefault(t, state) + + ui := new(cli.MockUi) + c := &UntaintCommand{ + Meta: Meta{ + Ui: ui, + }, + } + + args := []string{ + "-state-out", "foo", + "test_instance.foo", + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + testStateOutput(t, path, strings.TrimSpace(` +test_instance.foo: (1 tainted) + ID = + Tainted ID 1 = bar + `)) + testStateOutput(t, "foo", strings.TrimSpace(` +test_instance.foo: + ID = bar + `)) +} + +func TestUntaint_module(t *testing.T) { + state := &terraform.State{ + Modules: []*terraform.ModuleState{ + &terraform.ModuleState{ + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test_instance.foo": &terraform.ResourceState{ + Type: "test_instance", + Tainted: []*terraform.InstanceState{ + &terraform.InstanceState{ID: "bar"}, + }, + }, + }, + }, + &terraform.ModuleState{ + Path: []string{"root", "child"}, + Resources: map[string]*terraform.ResourceState{ + "test_instance.blah": &terraform.ResourceState{ + Type: "test_instance", + Tainted: []*terraform.InstanceState{ + &terraform.InstanceState{ID: "bar"}, + }, + }, + }, + }, + }, + } + statePath := testStateFile(t, state) + + ui := new(cli.MockUi) + c := &UntaintCommand{ + Meta: Meta{ + Ui: ui, + }, + } + + args := []string{ + "-module=child", + "-state", statePath, + "test_instance.blah", + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + testStateOutput(t, statePath, strings.TrimSpace(` +test_instance.foo: (1 tainted) + ID = + Tainted ID 1 = bar + +module.child: + test_instance.blah: + ID = bar + `)) +} diff --git a/commands.go b/commands.go index 45783d3a5..17f29a43d 100644 --- a/commands.go +++ b/commands.go @@ -125,6 +125,12 @@ func init() { CheckFunc: commandVersionCheck, }, nil }, + + "untaint": func() (cli.Command, error) { + return &command.UntaintCommand{ + Meta: meta, + }, nil + }, } } diff --git a/terraform/state.go b/terraform/state.go index cd9218cb4..d63313278 100644 --- a/terraform/state.go +++ b/terraform/state.go @@ -872,6 +872,36 @@ func (r *ResourceState) Taint() { r.Primary = nil } +// Untaint takes a tainted InstanceState and marks it as primary. +// The index argument is used to select a single InstanceState from the +// array of Tainted when there are more than one. If index is -1, the +// first Tainted InstanceState will be untainted iff there is only one +// Tainted InstanceState. Index must be >= 0 to specify an InstanceState +// when Tainted has more than one member. +func (r *ResourceState) Untaint(index int) error { + if len(r.Tainted) == 0 { + return fmt.Errorf("Nothing to untaint.") + } + if r.Primary != nil { + return fmt.Errorf("Resource has a primary instance in the state that would be overwritten by untainting. If you want to restore a tainted resource to primary, taint the existing primary instance first.") + } + if index == -1 && len(r.Tainted) > 1 { + return fmt.Errorf("There are %d tainted instances for this resource, please specify an index to select which one to untaint.", len(r.Tainted)) + } + if index == -1 { + index = 0 + } + if index >= len(r.Tainted) { + return fmt.Errorf("There are %d tainted instances for this resource, the index specified (%d) is out of range.", len(r.Tainted), index) + } + + // Perform the untaint + r.Primary = r.Tainted[index] + r.Tainted = append(r.Tainted[:index], r.Tainted[index+1:]...) + + return nil +} + func (r *ResourceState) init() { if r.Primary == nil { r.Primary = &InstanceState{} diff --git a/terraform/state_test.go b/terraform/state_test.go index 8351dc6ae..f145652a4 100644 --- a/terraform/state_test.go +++ b/terraform/state_test.go @@ -502,6 +502,103 @@ func TestResourceStateTaint(t *testing.T) { } } +func TestResourceStateUntaint(t *testing.T) { + cases := map[string]struct { + Input *ResourceState + Index func() int + ExpectedOutput *ResourceState + ExpectedErrMsg string + }{ + "no primary, no tainted, err": { + Input: &ResourceState{}, + ExpectedOutput: &ResourceState{}, + ExpectedErrMsg: "Nothing to untaint", + }, + + "one tainted, no primary": { + Input: &ResourceState{ + Tainted: []*InstanceState{ + &InstanceState{ID: "foo"}, + }, + }, + ExpectedOutput: &ResourceState{ + Primary: &InstanceState{ID: "foo"}, + Tainted: []*InstanceState{}, + }, + }, + + "one tainted, existing primary error": { + Input: &ResourceState{ + Primary: &InstanceState{ID: "foo"}, + Tainted: []*InstanceState{ + &InstanceState{ID: "foo"}, + }, + }, + ExpectedErrMsg: "Resource has a primary", + }, + + "multiple tainted, no index": { + Input: &ResourceState{ + Tainted: []*InstanceState{ + &InstanceState{ID: "bar"}, + &InstanceState{ID: "foo"}, + }, + }, + ExpectedErrMsg: "please specify an index", + }, + + "multiple tainted, with index": { + Input: &ResourceState{ + Tainted: []*InstanceState{ + &InstanceState{ID: "bar"}, + &InstanceState{ID: "foo"}, + }, + }, + Index: func() int { return 1 }, + ExpectedOutput: &ResourceState{ + Primary: &InstanceState{ID: "foo"}, + Tainted: []*InstanceState{ + &InstanceState{ID: "bar"}, + }, + }, + }, + + "index out of bounds error": { + Input: &ResourceState{ + Tainted: []*InstanceState{ + &InstanceState{ID: "bar"}, + &InstanceState{ID: "foo"}, + }, + }, + Index: func() int { return 2 }, + ExpectedErrMsg: "out of range", + }, + } + + for k, tc := range cases { + index := -1 + if tc.Index != nil { + index = tc.Index() + } + err := tc.Input.Untaint(index) + if tc.ExpectedErrMsg == "" && err != nil { + t.Fatalf("[%s] unexpected err: %s", k, err) + } + if tc.ExpectedErrMsg != "" { + if strings.Contains(err.Error(), tc.ExpectedErrMsg) { + continue + } + t.Fatalf("[%s] expected err: %s to contain: %s", + k, err, tc.ExpectedErrMsg) + } + if !reflect.DeepEqual(tc.Input, tc.ExpectedOutput) { + t.Fatalf( + "Failure: %s\n\nExpected: %#v\n\nGot: %#v", + k, tc.ExpectedOutput, tc.Input) + } + } +} + func TestInstanceStateEmpty(t *testing.T) { cases := map[string]struct { In *InstanceState diff --git a/website/source/docs/commands/untaint.html.markdown b/website/source/docs/commands/untaint.html.markdown new file mode 100644 index 000000000..557460d32 --- /dev/null +++ b/website/source/docs/commands/untaint.html.markdown @@ -0,0 +1,61 @@ +--- +layout: "docs" +page_title: "Command: untaint" +sidebar_current: "docs-commands-untaint" +description: |- + The `terraform untaint` command manually unmarks a Terraform-managed resource as tainted, restoring it as the primary instance in the state. +--- + +# Command: untaint + +The `terraform untaint` command manually unmarks a Terraform-managed resource +as tainted, restoring it as the primary instance in the state. This reverses +either a manual `terraform taint` or the result of provisioners failing on a +resource. + +This command _will not_ modify infrastructure, but does modify the state file +in order to unmark a resource as tainted. + +~> **NOTE on Tainted Indexes:** In certain edge cases, more than one tainted +instance can be present for a single resource. When this happens, the `-index` +flag is required to select which of the tainted instances to restore as +primary. You can use the `terraform show` command to inspect the state and +determine which index holds the instance you'd like to restore. In the vast +majority of cases, there will only be one tainted instance, and the `-index` +flag can be omitted. + +## Usage + +Usage: `terraform untaint [options] name` + +The `name` argument is the name of the resource to mark as untainted. The +format of this argument is `TYPE.NAME`, such as `aws_instance.foo`. + +The command-line flags are all optional (with the exception of `-index` in +certain cases, see above note). The list of available flags are: + +* `-allow-missing` - If specified, the command will succeed (exit code 0) + even if the resource is missing. The command can still error, but only + in critically erroneous cases. + +* `-backup=path` - Path to the backup file. Defaults to `-state-out` with + the ".backup" extension. Disabled by setting to "-". + +* `-index=n` - Selects a single tainted instance when there are more than one + tainted instances present in the state for a given resource. This flag is + required when multiple tainted instances are present. The vast majority of the + time, there is a maxiumum of one tainted instance per resource, so this flag + can be safely omitted. + +* `-module=path` - The module path where the resource to untaint exists. + By default this is the root path. Other modules can be specified by + a period-separated list. Example: "foo" would reference the module + "foo" but "foo.bar" would reference the "bar" module in the "foo" + module. + +* `-no-color` - Disables output with coloring + +* `-state=path` - Path to read and write the state file to. Defaults to "terraform.tfstate". + +* `-state-out=path` - Path to write updated state file. By default, the + `-state` path will be used. diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index a000ec6c8..495a36828 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -111,6 +111,9 @@ validate + > + untaint +