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