github.com/pulumi/terraform@v1.4.0/pkg/command/state_rm.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/pulumi/terraform/pkg/addrs"
     8  	"github.com/pulumi/terraform/pkg/command/arguments"
     9  	"github.com/pulumi/terraform/pkg/command/clistate"
    10  	"github.com/pulumi/terraform/pkg/command/views"
    11  	"github.com/pulumi/terraform/pkg/terraform"
    12  	"github.com/pulumi/terraform/pkg/tfdiags"
    13  	"github.com/mitchellh/cli"
    14  )
    15  
    16  // StateRmCommand is a Command implementation that shows a single resource.
    17  type StateRmCommand struct {
    18  	StateMeta
    19  }
    20  
    21  func (c *StateRmCommand) Run(args []string) int {
    22  	args = c.Meta.process(args)
    23  	var dryRun bool
    24  	cmdFlags := c.Meta.ignoreRemoteVersionFlagSet("state rm")
    25  	cmdFlags.BoolVar(&dryRun, "dry-run", false, "dry run")
    26  	cmdFlags.StringVar(&c.backupPath, "backup", "-", "backup")
    27  	cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
    28  	cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
    29  	cmdFlags.StringVar(&c.statePath, "state", "", "path")
    30  	if err := cmdFlags.Parse(args); err != nil {
    31  		c.Ui.Error(fmt.Sprintf("Error parsing command-line flags: %s\n", err.Error()))
    32  		return 1
    33  	}
    34  
    35  	args = cmdFlags.Args()
    36  	if len(args) < 1 {
    37  		c.Ui.Error("At least one address is required.\n")
    38  		return cli.RunResultHelp
    39  	}
    40  
    41  	if diags := c.Meta.checkRequiredVersion(); diags != nil {
    42  		c.showDiagnostics(diags)
    43  		return 1
    44  	}
    45  
    46  	// Get the state
    47  	stateMgr, err := c.State()
    48  	if err != nil {
    49  		c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
    50  		return 1
    51  	}
    52  
    53  	if c.stateLock {
    54  		stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View))
    55  		if diags := stateLocker.Lock(stateMgr, "state-rm"); diags.HasErrors() {
    56  			c.showDiagnostics(diags)
    57  			return 1
    58  		}
    59  		defer func() {
    60  			if diags := stateLocker.Unlock(); diags.HasErrors() {
    61  				c.showDiagnostics(diags)
    62  			}
    63  		}()
    64  	}
    65  
    66  	if err := stateMgr.RefreshState(); err != nil {
    67  		c.Ui.Error(fmt.Sprintf("Failed to refresh state: %s", err))
    68  		return 1
    69  	}
    70  
    71  	state := stateMgr.State()
    72  	if state == nil {
    73  		c.Ui.Error(errStateNotFound)
    74  		return 1
    75  	}
    76  
    77  	// This command primarily works with resource instances, though it will
    78  	// also clean up any modules and resources left empty by actions it takes.
    79  	var addrs []addrs.AbsResourceInstance
    80  	var diags tfdiags.Diagnostics
    81  	for _, addrStr := range args {
    82  		moreAddrs, moreDiags := c.lookupResourceInstanceAddr(state, true, addrStr)
    83  		addrs = append(addrs, moreAddrs...)
    84  		diags = diags.Append(moreDiags)
    85  	}
    86  	if diags.HasErrors() {
    87  		c.showDiagnostics(diags)
    88  		return 1
    89  	}
    90  
    91  	prefix := "Removed "
    92  	if dryRun {
    93  		prefix = "Would remove "
    94  	}
    95  
    96  	var isCount int
    97  	ss := state.SyncWrapper()
    98  	for _, addr := range addrs {
    99  		isCount++
   100  		c.Ui.Output(prefix + addr.String())
   101  		if !dryRun {
   102  			ss.ForgetResourceInstanceAll(addr)
   103  			ss.RemoveResourceIfEmpty(addr.ContainingResource())
   104  		}
   105  	}
   106  
   107  	if dryRun {
   108  		if isCount == 0 {
   109  			c.Ui.Output("Would have removed nothing.")
   110  		}
   111  		return 0 // This is as far as we go in dry-run mode
   112  	}
   113  
   114  	b, backendDiags := c.Backend(nil)
   115  	diags = diags.Append(backendDiags)
   116  	if backendDiags.HasErrors() {
   117  		c.showDiagnostics(diags)
   118  		return 1
   119  	}
   120  
   121  	// Get schemas, if possible, before writing state
   122  	var schemas *terraform.Schemas
   123  	if isCloudMode(b) {
   124  		var schemaDiags tfdiags.Diagnostics
   125  		schemas, schemaDiags = c.MaybeGetSchemas(state, nil)
   126  		diags = diags.Append(schemaDiags)
   127  	}
   128  
   129  	if err := stateMgr.WriteState(state); err != nil {
   130  		c.Ui.Error(fmt.Sprintf(errStateRmPersist, err))
   131  		return 1
   132  	}
   133  	if err := stateMgr.PersistState(schemas); err != nil {
   134  		c.Ui.Error(fmt.Sprintf(errStateRmPersist, err))
   135  		return 1
   136  	}
   137  
   138  	if len(diags) > 0 && isCount != 0 {
   139  		c.showDiagnostics(diags)
   140  	}
   141  
   142  	if isCount == 0 {
   143  		diags = diags.Append(tfdiags.Sourceless(
   144  			tfdiags.Error,
   145  			"Invalid target address",
   146  			"No matching objects found. To view the available instances, use \"terraform state list\". Please modify the address to reference a specific instance.",
   147  		))
   148  		c.showDiagnostics(diags)
   149  		return 1
   150  	}
   151  
   152  	c.Ui.Output(fmt.Sprintf("Successfully removed %d resource instance(s).", isCount))
   153  	return 0
   154  }
   155  
   156  func (c *StateRmCommand) Help() string {
   157  	helpText := `
   158  Usage: terraform [global options] state rm [options] ADDRESS...
   159  
   160    Remove one or more items from the Terraform state, causing Terraform to
   161    "forget" those items without first destroying them in the remote system.
   162  
   163    This command removes one or more resource instances from the Terraform state
   164    based on the addresses given. You can view and list the available instances
   165    with "terraform state list".
   166  
   167    If you give the address of an entire module then all of the instances in
   168    that module and any of its child modules will be removed from the state.
   169  
   170    If you give the address of a resource that has "count" or "for_each" set,
   171    all of the instances of that resource will be removed from the state.
   172  
   173  Options:
   174  
   175    -dry-run                If set, prints out what would've been removed but
   176                            doesn't actually remove anything.
   177  
   178    -backup=PATH            Path where Terraform should write the backup
   179                            state.
   180  
   181    -lock=false             Don't hold a state lock during the operation. This is
   182                            dangerous if others might concurrently run commands
   183                            against the same workspace.
   184  
   185    -lock-timeout=0s        Duration to retry a state lock.
   186  
   187    -state=PATH             Path to the state file to update. Defaults to the
   188                            current workspace state.
   189  
   190    -ignore-remote-version  Continue even if remote and local Terraform versions
   191                            are incompatible. This may result in an unusable
   192                            workspace, and should be used with extreme caution.
   193  
   194  `
   195  	return strings.TrimSpace(helpText)
   196  }
   197  
   198  func (c *StateRmCommand) Synopsis() string {
   199  	return "Remove instances from the state"
   200  }
   201  
   202  const errStateRmPersist = `Error saving the state: %s
   203  
   204  The state was not saved. No items were removed from the persisted
   205  state. No backup was created since no modification occurred. Please
   206  resolve the issue above and try again.`