Merge pull request #15652 from hashicorp/jbardin/state-command

state commands with remote state backends
This commit is contained in:
James Bardin 2017-08-03 13:29:45 -04:00 committed by GitHub
commit eadda50f02
9 changed files with 406 additions and 104 deletions

View File

@ -11,30 +11,32 @@ import (
) )
// StateMeta is the meta struct that should be embedded in state subcommands. // StateMeta is the meta struct that should be embedded in state subcommands.
type StateMeta struct{} type StateMeta struct {
Meta
}
// State returns the state for this meta. This gets the appropriate state from // State returns the state for this meta. This gets the appropriate state from
// the backend, but changes the way that backups are done. This configures // the backend, but changes the way that backups are done. This configures
// backups to be timestamped rather than just the original state path plus a // backups to be timestamped rather than just the original state path plus a
// backup path. // backup path.
func (c *StateMeta) State(m *Meta) (state.State, error) { func (c *StateMeta) State() (state.State, error) {
var realState state.State var realState state.State
backupPath := m.backupPath backupPath := c.backupPath
stateOutPath := m.statePath stateOutPath := c.statePath
// use the specified state // use the specified state
if m.statePath != "" { if c.statePath != "" {
realState = &state.LocalState{ realState = &state.LocalState{
Path: m.statePath, Path: c.statePath,
} }
} else { } else {
// Load the backend // Load the backend
b, err := m.Backend(nil) b, err := c.Backend(nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
env := m.Workspace() env := c.Workspace()
// Get the state // Get the state
s, err := b.State(env) s, err := b.State(env)
if err != nil { if err != nil {
@ -42,7 +44,7 @@ func (c *StateMeta) State(m *Meta) (state.State, error) {
} }
// Get a local backend // Get a local backend
localRaw, err := m.Backend(&BackendOpts{ForceLocal: true}) localRaw, err := c.Backend(&BackendOpts{ForceLocal: true})
if err != nil { if err != nil {
// This should never fail // This should never fail
panic(err) panic(err)

View File

@ -10,7 +10,6 @@ import (
// StateMvCommand is a Command implementation that shows a single resource. // StateMvCommand is a Command implementation that shows a single resource.
type StateMvCommand struct { type StateMvCommand struct {
Meta
StateMeta StateMeta
} }
@ -21,12 +20,13 @@ func (c *StateMvCommand) Run(args []string) int {
} }
// We create two metas to track the two states // We create two metas to track the two states
var meta1, meta2 Meta var backupPathOut, statePathOut string
cmdFlags := c.Meta.flagSet("state mv") cmdFlags := c.Meta.flagSet("state mv")
cmdFlags.StringVar(&meta1.backupPath, "backup", "-", "backup") cmdFlags.StringVar(&c.backupPath, "backup", "-", "backup")
cmdFlags.StringVar(&meta1.statePath, "state", DefaultStateFilename, "path") cmdFlags.StringVar(&c.statePath, "state", "", "path")
cmdFlags.StringVar(&meta2.backupPath, "backup-out", "-", "backup") cmdFlags.StringVar(&backupPathOut, "backup-out", "-", "backup")
cmdFlags.StringVar(&meta2.statePath, "state-out", "", "path") cmdFlags.StringVar(&statePathOut, "state-out", "", "path")
if err := cmdFlags.Parse(args); err != nil { if err := cmdFlags.Parse(args); err != nil {
return cli.RunResultHelp return cli.RunResultHelp
} }
@ -36,16 +36,11 @@ func (c *StateMvCommand) Run(args []string) int {
return cli.RunResultHelp return cli.RunResultHelp
} }
// Copy the `-state` flag for output if we weren't given a custom one
if meta2.statePath == "" {
meta2.statePath = meta1.statePath
}
// Read the from state // Read the from state
stateFrom, err := c.StateMeta.State(&meta1) stateFrom, err := c.State()
if err != nil { if err != nil {
c.Ui.Error(fmt.Sprintf(errStateLoadingState, err)) c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
return cli.RunResultHelp return 1
} }
if err := stateFrom.RefreshState(); err != nil { if err := stateFrom.RefreshState(); err != nil {
@ -62,11 +57,14 @@ func (c *StateMvCommand) Run(args []string) int {
// Read the destination state // Read the destination state
stateTo := stateFrom stateTo := stateFrom
stateToReal := stateFromReal stateToReal := stateFromReal
if meta2.statePath != meta1.statePath {
stateTo, err = c.StateMeta.State(&meta2) if statePathOut != "" {
c.statePath = statePathOut
c.backupPath = backupPathOut
stateTo, err = c.State()
if err != nil { if err != nil {
c.Ui.Error(fmt.Sprintf(errStateLoadingState, err)) c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
return cli.RunResultHelp return 1
} }
if err := stateTo.RefreshState(); err != nil { if err := stateTo.RefreshState(); err != nil {
@ -185,28 +183,30 @@ func (c *StateMvCommand) addableResult(results []*terraform.StateFilterResult) i
func (c *StateMvCommand) Help() string { func (c *StateMvCommand) Help() string {
helpText := ` helpText := `
Usage: terraform state mv [options] ADDRESS ADDRESS Usage: terraform state mv [options] SOURCE DESTINATION
Move an item in the state to another location or to a completely different This command will move an item matched by the address given to the
state file. destination address. This command can also move to a destination address
in a completely different state file.
This command is useful for module refactors (moving items into a module), This can be used for simple resource renaming, moving items to and from
configuration refactors (moving items to a completely different or new a module, moving entire modules, and more. And because this command can also
state file), or generally renaming of resources. move data to a completely new state, it can also be used for refactoring
one configuration into multiple separately managed Terraform configurations.
This command creates a timestamped backup of the state on every invocation. This command will output a backup copy of the state prior to saving any
This can't be disabled. Due to the destructive nature of this command, changes. The backup cannot be disabled. Due to the destructive nature
the backup is ensured by Terraform for safety reasons. of this command, backups are required.
If you're moving from one state file to a different state file, a backup If you're moving an item to a different state file, a backup will be created
will be created for each state file. for each state file.
Options: Options:
-backup=PATH Path where Terraform should write the backup for the original -backup=PATH Path where Terraform should write the backup for the original
state. This can't be disabled. If not set, Terraform state. This can't be disabled. If not set, Terraform
will write it to the same path as the statefile with will write it to the same path as the statefile with
a backup extension. a ".backup" extension.
-backup-out=PATH Path where Terraform should write the backup for the destination -backup-out=PATH Path where Terraform should write the backup for the destination
state. This can't be disabled. If not set, Terraform state. This can't be disabled. If not set, Terraform
@ -215,13 +215,12 @@ Options:
to be specified if -state-out is set to a different path to be specified if -state-out is set to a different path
than -state. than -state.
-state=PATH Path to a Terraform state file to use to look -state=PATH Path to the source state file. Defaults to the configured
up Terraform-managed resources. By default it will backend, or "terraform.tfstate"
use the state "terraform.tfstate" if it exists.
-state-out=PATH Path to the destination state file to move the item -state-out=PATH Path to the destination state file to write to. If this
to. This defaults to the same statefile. This will isn't specified, the source state file will be used. This
overwrite the destination state file. can be a new or existing path.
` `
return strings.TrimSpace(helpText) return strings.TrimSpace(helpText)

View File

@ -47,9 +47,11 @@ func TestStateMv(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &StateMvCommand{ c := &StateMvCommand{
Meta: Meta{ StateMeta{
testingOverrides: metaOverridesForProvider(p), Meta: Meta{
Ui: ui, testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}, },
} }
@ -133,9 +135,11 @@ func TestStateMv_explicitWithBackend(t *testing.T) {
p := testProvider() p := testProvider()
ui = new(cli.MockUi) ui = new(cli.MockUi)
c := &StateMvCommand{ c := &StateMvCommand{
Meta: Meta{ StateMeta{
testingOverrides: metaOverridesForProvider(p), Meta: Meta{
Ui: ui, testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}, },
} }
@ -194,9 +198,11 @@ func TestStateMv_backupExplicit(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &StateMvCommand{ c := &StateMvCommand{
Meta: Meta{ StateMeta{
testingOverrides: metaOverridesForProvider(p), Meta: Meta{
Ui: ui, testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}, },
} }
@ -244,9 +250,11 @@ func TestStateMv_stateOutNew(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &StateMvCommand{ c := &StateMvCommand{
Meta: Meta{ StateMeta{
testingOverrides: metaOverridesForProvider(p), Meta: Meta{
Ui: ui, testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}, },
} }
@ -316,9 +324,11 @@ func TestStateMv_stateOutExisting(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &StateMvCommand{ c := &StateMvCommand{
Meta: Meta{ StateMeta{
testingOverrides: metaOverridesForProvider(p), Meta: Meta{
Ui: ui, testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}, },
} }
@ -357,9 +367,11 @@ func TestStateMv_noState(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &StateMvCommand{ c := &StateMvCommand{
Meta: Meta{ StateMeta{
testingOverrides: metaOverridesForProvider(p), Meta: Meta{
Ui: ui, testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}, },
} }
@ -418,9 +430,11 @@ func TestStateMv_stateOutNew_count(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &StateMvCommand{ c := &StateMvCommand{
Meta: Meta{ StateMeta{
testingOverrides: metaOverridesForProvider(p), Meta: Meta{
Ui: ui, testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}, },
} }
@ -596,9 +610,11 @@ func TestStateMv_stateOutNew_largeCount(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &StateMvCommand{ c := &StateMvCommand{
Meta: Meta{ StateMeta{
testingOverrides: metaOverridesForProvider(p), Meta: Meta{
Ui: ui, testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}, },
} }
@ -677,9 +693,11 @@ func TestStateMv_stateOutNew_nestedModule(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &StateMvCommand{ c := &StateMvCommand{
Meta: Meta{ StateMeta{
testingOverrides: metaOverridesForProvider(p), Meta: Meta{
Ui: ui, testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}, },
} }
@ -705,6 +723,160 @@ func TestStateMv_stateOutNew_nestedModule(t *testing.T) {
testStateOutput(t, backups[0], testStateMvNestedModule_stateOutOriginal) testStateOutput(t, backups[0], testStateMvNestedModule_stateOutOriginal)
} }
func TestStateMv_withinBackend(t *testing.T) {
td := tempDir(t)
copy.CopyDir(testFixturePath("backend-unchanged"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
state := &terraform.State{
Modules: []*terraform.ModuleState{
&terraform.ModuleState{
Path: []string{"root"},
Resources: map[string]*terraform.ResourceState{
"test_instance.foo": &terraform.ResourceState{
Type: "test_instance",
Primary: &terraform.InstanceState{
ID: "bar",
Attributes: map[string]string{
"foo": "value",
"bar": "value",
},
},
},
"test_instance.baz": &terraform.ResourceState{
Type: "test_instance",
Primary: &terraform.InstanceState{
ID: "foo",
Attributes: map[string]string{
"foo": "value",
"bar": "value",
},
},
},
},
},
},
}
// the local backend state file is "foo"
statePath := "local-state.tfstate"
backupPath := "local-state.backup"
f, err := os.Create(statePath)
if err != nil {
t.Fatal(err)
}
defer f.Close()
if err := terraform.WriteState(state, f); err != nil {
t.Fatal(err)
}
p := testProvider()
ui := new(cli.MockUi)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
},
}
args := []string{
"-backup", backupPath,
"test_instance.foo",
"test_instance.bar",
}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
testStateOutput(t, statePath, testStateMvOutput)
testStateOutput(t, backupPath, testStateMvOutputOriginal)
}
func TestStateMv_fromBackendToLocal(t *testing.T) {
td := tempDir(t)
copy.CopyDir(testFixturePath("backend-unchanged"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
state := &terraform.State{
Modules: []*terraform.ModuleState{
&terraform.ModuleState{
Path: []string{"root"},
Resources: map[string]*terraform.ResourceState{
"test_instance.foo": &terraform.ResourceState{
Type: "test_instance",
Primary: &terraform.InstanceState{
ID: "bar",
Attributes: map[string]string{
"foo": "value",
"bar": "value",
},
},
},
"test_instance.baz": &terraform.ResourceState{
Type: "test_instance",
Primary: &terraform.InstanceState{
ID: "foo",
Attributes: map[string]string{
"foo": "value",
"bar": "value",
},
},
},
},
},
},
}
// the local backend state file is "foo"
statePath := "local-state.tfstate"
// real "local" state file
statePathOut := "real-local.tfstate"
f, err := os.Create(statePath)
if err != nil {
t.Fatal(err)
}
defer f.Close()
if err := terraform.WriteState(state, f); err != nil {
t.Fatal(err)
}
p := testProvider()
ui := new(cli.MockUi)
c := &StateMvCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
},
}
args := []string{
"-state-out", statePathOut,
"test_instance.foo",
"test_instance.bar",
}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
testStateOutput(t, statePathOut, testStateMvCount_stateOutSrc)
// the backend state should be left with only baz
testStateOutput(t, statePath, testStateMvOriginal_backend)
}
const testStateMvOutputOriginal = ` const testStateMvOutputOriginal = `
test_instance.baz: test_instance.baz:
ID = foo ID = foo
@ -943,3 +1115,10 @@ const testStateMvExisting_stateDstOriginal = `
test_instance.qux: test_instance.qux:
ID = bar ID = bar
` `
const testStateMvOriginal_backend = `
test_instance.baz:
ID = foo
bar = value
foo = value
`

View File

@ -9,7 +9,6 @@ import (
// StateRmCommand is a Command implementation that shows a single resource. // StateRmCommand is a Command implementation that shows a single resource.
type StateRmCommand struct { type StateRmCommand struct {
Meta
StateMeta StateMeta
} }
@ -20,8 +19,8 @@ func (c *StateRmCommand) Run(args []string) int {
} }
cmdFlags := c.Meta.flagSet("state show") cmdFlags := c.Meta.flagSet("state show")
cmdFlags.StringVar(&c.Meta.backupPath, "backup", "-", "backup") cmdFlags.StringVar(&c.backupPath, "backup", "-", "backup")
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path") cmdFlags.StringVar(&c.statePath, "state", "", "path")
if err := cmdFlags.Parse(args); err != nil { if err := cmdFlags.Parse(args); err != nil {
return cli.RunResultHelp return cli.RunResultHelp
} }
@ -32,10 +31,10 @@ func (c *StateRmCommand) Run(args []string) int {
return 1 return 1
} }
state, err := c.StateMeta.State(&c.Meta) state, err := c.State()
if err != nil { if err != nil {
c.Ui.Error(fmt.Sprintf(errStateLoadingState, err)) c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
return cli.RunResultHelp return 1
} }
if err := state.RefreshState(); err != nil { if err := state.RefreshState(); err != nil {
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err)) c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))

View File

@ -6,6 +6,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/copy"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/cli" "github.com/mitchellh/cli"
) )
@ -47,9 +48,11 @@ func TestStateRm(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &StateRmCommand{ c := &StateRmCommand{
Meta: Meta{ StateMeta{
testingOverrides: metaOverridesForProvider(p), Meta: Meta{
Ui: ui, testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}, },
} }
@ -109,9 +112,11 @@ func TestStateRmNoArgs(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &StateRmCommand{ c := &StateRmCommand{
Meta: Meta{ StateMeta{
testingOverrides: metaOverridesForProvider(p), Meta: Meta{
Ui: ui, testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}, },
} }
@ -169,9 +174,11 @@ func TestStateRm_backupExplicit(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &StateRmCommand{ c := &StateRmCommand{
Meta: Meta{ StateMeta{
testingOverrides: metaOverridesForProvider(p), Meta: Meta{
Ui: ui, testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}, },
} }
@ -198,9 +205,11 @@ func TestStateRm_noState(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &StateRmCommand{ c := &StateRmCommand{
Meta: Meta{ StateMeta{
testingOverrides: metaOverridesForProvider(p), Meta: Meta{
Ui: ui, testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}, },
} }
@ -210,6 +219,110 @@ func TestStateRm_noState(t *testing.T) {
} }
} }
func TestStateRm_needsInit(t *testing.T) {
td := tempDir(t)
copy.CopyDir(testFixturePath("backend-change"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
p := testProvider()
ui := new(cli.MockUi)
c := &StateRmCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
},
}
args := []string{"foo"}
if code := c.Run(args); code == 0 {
t.Fatal("expected error\noutput:", ui.OutputWriter)
}
if !strings.Contains(ui.ErrorWriter.String(), "Initialization") {
t.Fatal("expected initialization error, got:\n", ui.ErrorWriter)
}
}
func TestStateRm_backendState(t *testing.T) {
td := tempDir(t)
copy.CopyDir(testFixturePath("backend-unchanged"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
state := &terraform.State{
Modules: []*terraform.ModuleState{
&terraform.ModuleState{
Path: []string{"root"},
Resources: map[string]*terraform.ResourceState{
"test_instance.foo": &terraform.ResourceState{
Type: "test_instance",
Primary: &terraform.InstanceState{
ID: "bar",
Attributes: map[string]string{
"foo": "value",
"bar": "value",
},
},
},
"test_instance.bar": &terraform.ResourceState{
Type: "test_instance",
Primary: &terraform.InstanceState{
ID: "foo",
Attributes: map[string]string{
"foo": "value",
"bar": "value",
},
},
},
},
},
},
}
// the local backend state file is "foo"
statePath := "local-state.tfstate"
backupPath := "local-state.backup"
f, err := os.Create(statePath)
if err != nil {
t.Fatal(err)
}
defer f.Close()
if err := terraform.WriteState(state, f); err != nil {
t.Fatal(err)
}
p := testProvider()
ui := new(cli.MockUi)
c := &StateRmCommand{
StateMeta{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
},
}
args := []string{
"-backup", backupPath,
"test_instance.foo",
}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
// Test it is correct
testStateOutput(t, statePath, testStateRmOutput)
// Test backup
testStateOutput(t, backupPath, testStateRmOutputOriginal)
}
const testStateRmOutputOriginal = ` const testStateRmOutputOriginal = `
test_instance.bar: test_instance.bar:
ID = foo ID = foo

View File

@ -28,7 +28,7 @@ func TestStateDefaultBackupExtension(t *testing.T) {
tmp, cwd := testCwd(t) tmp, cwd := testCwd(t)
defer testFixCwd(t, tmp, cwd) defer testFixCwd(t, tmp, cwd)
s, err := (&StateMeta{}).State(&Meta{}) s, err := (&StateMeta{}).State()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -276,13 +276,17 @@ func init() {
"state rm": func() (cli.Command, error) { "state rm": func() (cli.Command, error) {
return &command.StateRmCommand{ return &command.StateRmCommand{
Meta: meta, StateMeta: command.StateMeta{
Meta: meta,
},
}, nil }, nil
}, },
"state mv": func() (cli.Command, error) { "state mv": func() (cli.Command, error) {
return &command.StateMvCommand{ return &command.StateMvCommand{
Meta: meta, StateMeta: command.StateMeta{
Meta: meta,
},
}, nil }, nil
}, },

View File

@ -40,19 +40,22 @@ in [resource addressing format](/docs/commands/state/addressing.html).
The command-line flags are all optional. The list of available flags are: The command-line flags are all optional. The list of available flags are:
* `-backup=path` - Path to a backup file Defaults to the state path plus * `-backup=path` - Path where Terraform should write the backup for the
a timestamp with the ".backup" extension. original state. This can't be disabled. If not set, Terraform will write it
to the same path as the statefile with a ".backup" extension.
* `-backup-out=path` - Path to the backup file for the output state. * `-backup-out=path` - Path where Terraform should write the backup for the
This is only necessary if `-state-out` is specified. destination state. This can't be disabled. If not set, Terraform will write
it to the same path as the destination state file with a backup extension.
This only needs to be specified if -state-out is set to a different path than
-state.
* `-state=path` - Path to the state file. Defaults to "terraform.tfstate". * `-state=path` - Path to the source state file to read from. Defaults to the
Ignored when [remote state](/docs/state/remote.html) is used. configured backend, or "terraform.tfstate".
* `-state-out=path` - Path to the state file to write to. If this isn't specified * `-state-out=path` - Path to the destination state file to write to. If this
the state specified by `-state` will be used. This can be isn't specified the source state file will be used. This can be a new or
a new or existing path. Ignored when existing path.
[remote state](/docs/state/remote.html) is used.
## Example: Rename a Resource ## Example: Rename a Resource

View File

@ -17,7 +17,7 @@ and more.
Usage: `terraform state rm [options] ADDRESS...` Usage: `terraform state rm [options] ADDRESS...`
The command will remove all the items matched by the addresses given. Remove one or more items from the Terraform state.
Items removed from the Terraform state are _not physically destroyed_. Items removed from the Terraform state are _not physically destroyed_.
Items removed from the Terraform state are only no longer managed by Items removed from the Terraform state are only no longer managed by
@ -43,10 +43,13 @@ in [resource addressing format](/docs/commands/state/addressing.html).
The command-line flags are all optional. The list of available flags are: The command-line flags are all optional. The list of available flags are:
* `-backup=path` - Path to a backup file Defaults to the state path plus * `-backup=path` - Path where Terraform should write the backup state. This
a timestamp with the ".backup" extension. can't be disabled. If not set, Terraform will write it to the same path as
the statefile with a backup extension.
* `-state=path` - Path to the state file. Defaults to "terraform.tfstate". * `-state=path` - Path to a Terraform state file to use to look up
Terraform-managed resources. By default it will use the configured backend,
or the default "terraform.tfstate" if it exists.
## Example: Remove a Resource ## Example: Remove a Resource