Skip to content

Commit 3fdc08a

Browse files
mitchellhjen20
authored andcommitted
core: Add terraform state rm command and docs
This commit adds the `state rm` command for removing an address from state. It is the result of a rebase from pull-request hashicorp#5953 which was lost at some point during the Terraform 0.7 feature branch merges.
1 parent 0a2fd1a commit 3fdc08a

File tree

5 files changed

+283
-1
lines changed

5 files changed

+283
-1
lines changed

command/state_rm.go

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package command
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/mitchellh/cli"
8+
)
9+
10+
// StateRmCommand is a Command implementation that shows a single resource.
11+
type StateRmCommand struct {
12+
Meta
13+
StateMeta
14+
}
15+
16+
func (c *StateRmCommand) Run(args []string) int {
17+
args = c.Meta.process(args, true)
18+
19+
var backupPath string
20+
cmdFlags := c.Meta.flagSet("state show")
21+
cmdFlags.StringVar(&backupPath, "backup", "", "backup")
22+
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
23+
if err := cmdFlags.Parse(args); err != nil {
24+
return cli.RunResultHelp
25+
}
26+
args = cmdFlags.Args()
27+
28+
state, err := c.StateMeta.State(&c.Meta)
29+
if err != nil {
30+
c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
31+
return cli.RunResultHelp
32+
}
33+
34+
stateReal := state.State()
35+
if stateReal == nil {
36+
c.Ui.Error(fmt.Sprintf(errStateNotFound))
37+
return 1
38+
}
39+
40+
if err := stateReal.Remove(args...); err != nil {
41+
c.Ui.Error(fmt.Sprintf(errStateRm, err))
42+
return 1
43+
}
44+
45+
if err := state.WriteState(stateReal); err != nil {
46+
c.Ui.Error(fmt.Sprintf(errStateRmPersist, err))
47+
return 1
48+
}
49+
50+
if err := state.PersistState(); err != nil {
51+
c.Ui.Error(fmt.Sprintf(errStateRmPersist, err))
52+
return 1
53+
}
54+
55+
c.Ui.Output("Item removal successful.")
56+
return 0
57+
}
58+
59+
func (c *StateRmCommand) Help() string {
60+
helpText := `
61+
Usage: terraform state rm [options] ADDRESS...
62+
63+
Remove one or more items from the Terraform state.
64+
65+
This command removes one or more items from the Terraform state based
66+
on the address given. You can view and list the available resources
67+
with "terraform state list".
68+
69+
This command creates a timestamped backup of the state on every invocation.
70+
This can't be disabled. Due to the destructive nature of this command,
71+
the backup is ensured by Terraform for safety reasons.
72+
73+
Options:
74+
75+
-backup=PATH Path where Terraform should write the backup
76+
state. This can't be disabled. If not set, Terraform
77+
will write it to the same path as the statefile with
78+
a backup extension.
79+
80+
-state=statefile Path to a Terraform state file to use to look
81+
up Terraform-managed resources. By default it will
82+
use the state "terraform.tfstate" if it exists.
83+
84+
`
85+
return strings.TrimSpace(helpText)
86+
}
87+
88+
func (c *StateRmCommand) Synopsis() string {
89+
return "Remove an item from the state"
90+
}
91+
92+
const errStateRm = `Error removing items from the state: %s
93+
94+
The state was not saved. No items were removed from the persisted
95+
state. No backup was created since no modification occurred. Please
96+
resolve the issue above and try again.`
97+
98+
const errStateRmPersist = `Error saving the state: %s
99+
100+
The state was not saved. No items were removed from the persisted
101+
state. No backup was created since no modification occurred. Please
102+
resolve the issue above and try again.`

command/state_rm_test.go

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package command
2+
3+
import (
4+
"path/filepath"
5+
"sort"
6+
"testing"
7+
8+
"github.com/hashicorp/terraform/terraform"
9+
"github.com/mitchellh/cli"
10+
)
11+
12+
func TestStateRm(t *testing.T) {
13+
state := &terraform.State{
14+
Modules: []*terraform.ModuleState{
15+
&terraform.ModuleState{
16+
Path: []string{"root"},
17+
Resources: map[string]*terraform.ResourceState{
18+
"test_instance.foo": &terraform.ResourceState{
19+
Type: "test_instance",
20+
Primary: &terraform.InstanceState{
21+
ID: "bar",
22+
Attributes: map[string]string{
23+
"foo": "value",
24+
"bar": "value",
25+
},
26+
},
27+
},
28+
29+
"test_instance.bar": &terraform.ResourceState{
30+
Type: "test_instance",
31+
Primary: &terraform.InstanceState{
32+
ID: "foo",
33+
Attributes: map[string]string{
34+
"foo": "value",
35+
"bar": "value",
36+
},
37+
},
38+
},
39+
},
40+
},
41+
},
42+
}
43+
44+
statePath := testStateFile(t, state)
45+
46+
p := testProvider()
47+
ui := new(cli.MockUi)
48+
c := &StateRmCommand{
49+
Meta: Meta{
50+
ContextOpts: testCtxConfig(p),
51+
Ui: ui,
52+
},
53+
}
54+
55+
args := []string{
56+
"-state", statePath,
57+
"test_instance.foo",
58+
}
59+
if code := c.Run(args); code != 0 {
60+
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
61+
}
62+
63+
// Test it is correct
64+
testStateOutput(t, statePath, testStateRmOutput)
65+
66+
// Test we have backups
67+
backups := testStateBackups(t, filepath.Dir(statePath))
68+
if len(backups) != 1 {
69+
t.Fatalf("bad: %#v", backups)
70+
}
71+
testStateOutput(t, backups[0], testStateRmOutputOriginal)
72+
}
73+
74+
func TestStateRm_noState(t *testing.T) {
75+
tmp, cwd := testCwd(t)
76+
defer testFixCwd(t, tmp, cwd)
77+
78+
p := testProvider()
79+
ui := new(cli.MockUi)
80+
c := &StateRmCommand{
81+
Meta: Meta{
82+
ContextOpts: testCtxConfig(p),
83+
Ui: ui,
84+
},
85+
}
86+
87+
args := []string{}
88+
if code := c.Run(args); code != 1 {
89+
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
90+
}
91+
}
92+
93+
const testStateRmOutputOriginal = `
94+
test_instance.bar:
95+
ID = foo
96+
bar = value
97+
foo = value
98+
test_instance.foo:
99+
ID = bar
100+
bar = value
101+
foo = value
102+
`
103+
104+
const testStateRmOutput = `
105+
test_instance.bar:
106+
ID = foo
107+
bar = value
108+
foo = value
109+
`

commands.go

+6
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,12 @@ func init() {
171171
}, nil
172172
},
173173

174+
"state rm": func() (cli.Command, error) {
175+
return &command.StateRmCommand{
176+
Meta: meta,
177+
}, nil
178+
},
179+
174180
"state mv": func() (cli.Command, error) {
175181
return &command.StateMvCommand{
176182
Meta: meta,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
layout: "commands-state"
3+
page_title: "Command: state rm"
4+
sidebar_current: "docs-state-sub-rm"
5+
description: |-
6+
The `terraform state rm` command removes items from the Terraform state.
7+
---
8+
9+
# Command: state rm
10+
11+
The `terraform state rm` command is used to remove items from the
12+
[Terraform state](/docs/state/index.html). This command can remove
13+
single resources, since instances of a resource, entire modules,
14+
and more.
15+
16+
## Usage
17+
18+
Usage: `terraform state rm [options] ADDRESS...`
19+
20+
The command will remove all the items matched by the addresses given.
21+
22+
Items removed from the Terraform state are _not physically destroyed_.
23+
Items removed from the Terraform state are only no longer managed by
24+
Terraform. For example, if you remove an AWS instance from the state, the AWS
25+
instance will continue running, but `terraform plan` will no longer see that
26+
instance.
27+
28+
There are various use cases for removing items from a Terraform state
29+
file. The most common is refactoring a configuration to no longer manage
30+
that resource (perhaps moving it to another Terraform configuration/state).
31+
32+
The state will only be saved on successful removal of all addresses.
33+
If any specific address errors for any reason (such as a syntax error),
34+
the state will not be modified at all.
35+
36+
This command will output a backup copy of the state prior to saving any
37+
changes. The backup cannot be disabled. Due to the destructive nature
38+
of this command, backups are required.
39+
40+
This command requires one or more addresses that point to a resources in the
41+
state. Addresses are
42+
in [resource addressing format](/docs/commands/state/addressing.html).
43+
44+
The command-line flags are all optional. The list of available flags are:
45+
46+
* `-backup=path` - Path to a backup file Defaults to the state path plus
47+
a timestamp with the ".backup" extension.
48+
49+
* `-state=path` - Path to the state file. Defaults to "terraform.tfstate".
50+
51+
## Example: Remove a Resource
52+
53+
The example below removes a single resource in a module:
54+
55+
```
56+
$ terraform state rm module.foo.packet_device.worker[0]
57+
```
58+
59+
## Example: Remove a Module
60+
61+
The example below removes an entire module:
62+
63+
```
64+
$ terraform state rm module.foo
65+
```

website/source/layouts/commands-state.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
<li<%= sidebar_current("docs-state-sub-mv") %>>
2525
<a href="/docs/commands/state/mv.html">mv</a>
2626
</li>
27-
27+
2828
<li<%= sidebar_current("docs-state-sub-rm") %>>
2929
<a href="/docs/commands/state/rm.html">rm</a>
3030
</li>

0 commit comments

Comments
 (0)