2016-03-31 18:29:39 +02:00
package command
import (
2019-01-08 14:57:52 +01:00
"context"
2016-03-31 18:29:39 +02:00
"fmt"
"strings"
2018-10-15 17:38:06 +02:00
"github.com/hashicorp/terraform/addrs"
2019-01-08 14:57:52 +01:00
"github.com/hashicorp/terraform/command/clistate"
2019-03-16 03:51:26 +01:00
"github.com/hashicorp/terraform/tfdiags"
2018-10-25 15:41:00 +02:00
"github.com/mitchellh/cli"
2016-03-31 18:29:39 +02:00
)
// StateRmCommand is a Command implementation that shows a single resource.
type StateRmCommand struct {
StateMeta
}
func ( c * StateRmCommand ) Run ( args [ ] string ) int {
2020-04-01 21:01:08 +02:00
args = c . Meta . process ( args )
2018-10-25 15:41:00 +02:00
var dryRun bool
2018-11-21 15:35:27 +01:00
cmdFlags := c . Meta . defaultFlagSet ( "state rm" )
2018-10-25 15:41:00 +02:00
cmdFlags . BoolVar ( & dryRun , "dry-run" , false , "dry run" )
2017-07-26 19:08:09 +02:00
cmdFlags . StringVar ( & c . backupPath , "backup" , "-" , "backup" )
2018-11-21 15:35:27 +01:00
cmdFlags . BoolVar ( & c . Meta . stateLock , "lock" , true , "lock state" )
cmdFlags . DurationVar ( & c . Meta . stateLockTimeout , "lock-timeout" , 0 , "lock timeout" )
2017-07-26 19:08:09 +02:00
cmdFlags . StringVar ( & c . statePath , "state" , "" , "path" )
2016-03-31 18:29:39 +02:00
if err := cmdFlags . Parse ( args ) ; err != nil {
2019-08-16 14:31:21 +02:00
c . Ui . Error ( fmt . Sprintf ( "Error parsing command-line flags: %s\n" , err . Error ( ) ) )
return 1
2016-03-31 18:29:39 +02:00
}
2018-11-21 15:35:27 +01:00
args = cmdFlags . Args ( )
2017-07-05 23:58:08 +02:00
if len ( args ) < 1 {
2018-10-25 15:41:00 +02:00
c . Ui . Error ( "At least one address is required.\n" )
return cli . RunResultHelp
2017-07-05 23:58:08 +02:00
}
2018-10-22 15:52:53 +02:00
// Get the state
2018-10-15 17:38:06 +02:00
stateMgr , err := c . State ( )
2016-03-31 18:29:39 +02:00
if err != nil {
c . Ui . Error ( fmt . Sprintf ( errStateLoadingState , err ) )
2017-07-27 20:10:52 +02:00
return 1
2016-03-31 18:29:39 +02:00
}
2019-01-08 14:57:52 +01:00
if c . stateLock {
stateLocker := clistate . NewLocker ( context . Background ( ) , c . stateLockTimeout , c . Ui , c . Colorize ( ) )
if err := stateLocker . Lock ( stateMgr , "state-rm" ) ; err != nil {
c . Ui . Error ( fmt . Sprintf ( "Error locking state: %s" , err ) )
return 1
}
defer stateLocker . Unlock ( nil )
}
2018-10-15 17:38:06 +02:00
if err := stateMgr . RefreshState ( ) ; err != nil {
2018-10-22 15:52:53 +02:00
c . Ui . Error ( fmt . Sprintf ( "Failed to refresh state: %s" , err ) )
2017-02-22 05:35:43 +01:00
return 1
}
2016-03-31 18:29:39 +02:00
2018-10-15 17:38:06 +02:00
state := stateMgr . State ( )
if state == nil {
2016-03-31 18:29:39 +02:00
c . Ui . Error ( fmt . Sprintf ( errStateNotFound ) )
return 1
}
2019-03-16 03:51:26 +01:00
// This command primarily works with resource instances, though it will
// also clean up any modules and resources left empty by actions it takes.
var addrs [ ] addrs . AbsResourceInstance
var diags tfdiags . Diagnostics
for _ , addrStr := range args {
moreAddrs , moreDiags := c . lookupResourceInstanceAddr ( state , true , addrStr )
addrs = append ( addrs , moreAddrs ... )
diags = diags . Append ( moreDiags )
2018-10-15 17:38:06 +02:00
}
2019-03-16 03:51:26 +01:00
if diags . HasErrors ( ) {
c . showDiagnostics ( diags )
return 1
2018-10-15 17:38:06 +02:00
}
2019-03-16 03:51:26 +01:00
prefix := "Removed "
2018-10-25 15:41:00 +02:00
if dryRun {
2019-03-16 03:51:26 +01:00
prefix = "Would remove "
2018-10-15 17:38:06 +02:00
}
2016-03-31 18:29:39 +02:00
2018-10-22 15:52:53 +02:00
var isCount int
2018-10-15 17:38:06 +02:00
ss := state . SyncWrapper ( )
2019-03-16 03:51:26 +01:00
for _ , addr := range addrs {
isCount ++
c . Ui . Output ( prefix + addr . String ( ) )
if ! dryRun {
ss . ForgetResourceInstanceAll ( addr )
ss . RemoveResourceIfEmpty ( addr . ContainingResource ( ) )
2018-10-22 15:52:53 +02:00
}
2018-10-15 17:38:06 +02:00
}
2018-10-25 15:41:00 +02:00
if dryRun {
2018-10-22 15:52:53 +02:00
if isCount == 0 {
c . Ui . Output ( "Would have removed nothing." )
}
return 0 // This is as far as we go in dry-run mode
2018-10-15 17:38:06 +02:00
}
2017-09-20 21:48:27 +02:00
2018-10-15 17:38:06 +02:00
if err := stateMgr . WriteState ( state ) ; err != nil {
2016-03-31 18:29:39 +02:00
c . Ui . Error ( fmt . Sprintf ( errStateRmPersist , err ) )
return 1
}
2018-10-15 17:38:06 +02:00
if err := stateMgr . PersistState ( ) ; err != nil {
2016-03-31 18:29:39 +02:00
c . Ui . Error ( fmt . Sprintf ( errStateRmPersist , err ) )
return 1
}
2020-06-24 20:03:23 +02:00
if len ( diags ) > 0 && isCount != 0 {
2019-03-16 03:51:26 +01:00
c . showDiagnostics ( diags )
}
2018-10-22 15:52:53 +02:00
if isCount == 0 {
2020-06-24 20:03:23 +02:00
diags = diags . Append ( tfdiags . Sourceless (
tfdiags . Error ,
"Invalid target address" ,
"No matching objects found. To view the available instances, use \"terraform state list\". Please modify the address to reference a specific instance." ,
) )
c . showDiagnostics ( diags )
return 1
2018-10-22 15:52:53 +02:00
}
2020-06-24 20:03:23 +02:00
c . Ui . Output ( fmt . Sprintf ( "Successfully removed %d resource instance(s)." , isCount ) )
2016-03-31 18:29:39 +02:00
return 0
}
func ( c * StateRmCommand ) Help ( ) string {
helpText := `
Usage : terraform state rm [ options ] ADDRESS ...
2019-03-16 03:51:26 +01:00
Remove one or more items from the Terraform state , causing Terraform to
"forget" those items without first destroying them in the remote system .
2016-03-31 18:29:39 +02:00
2018-10-15 17:38:06 +02:00
This command removes one or more resource instances from the Terraform state
based on the addresses given . You can view and list the available instances
2016-03-31 18:29:39 +02:00
with "terraform state list" .
2019-03-16 03:51:26 +01:00
If you give the address of an entire module then all of the instances in
that module and any of its child modules will be removed from the state .
If you give the address of a resource that has "count" or "for_each" set ,
all of the instances of that resource will be removed from the state .
2016-03-31 18:29:39 +02:00
Options :
2018-10-15 17:38:06 +02:00
- dry - run If set , prints out what would ' ve been removed but
doesn ' t actually remove anything .
2016-03-31 18:29:39 +02:00
- backup = PATH Path where Terraform should write the backup
2019-03-16 03:51:26 +01:00
state .
2016-03-31 18:29:39 +02:00
2018-11-21 15:35:27 +01:00
- lock = true Lock the state file when locking is supported .
- lock - timeout = 0 s Duration to retry a state lock .
2019-03-16 03:51:26 +01:00
- state = PATH Path to the state file to update . Defaults to the current
workspace state .
2016-03-31 18:29:39 +02:00
`
return strings . TrimSpace ( helpText )
}
func ( c * StateRmCommand ) Synopsis ( ) string {
2018-10-15 17:38:06 +02:00
return "Remove instances from the state"
2016-03-31 18:29:39 +02:00
}
const errStateRm = ` Error removing items from the state : % s
The state was not saved . No items were removed from the persisted
state . No backup was created since no modification occurred . Please
resolve the issue above and try again . `
const errStateRmPersist = ` Error saving the state : % s
The state was not saved . No items were removed from the persisted
state . No backup was created since no modification occurred . Please
resolve the issue above and try again . `