| // Copyright IBM Corp. 2014, 2026 | |
| // SPDX-License-Identifier: BUSL-1.1 | |
| package command | |
| import ( | |
| "fmt" | |
| "strings" | |
| "github.com/hashicorp/terraform/internal/backend/backendrun" | |
| "github.com/hashicorp/terraform/internal/command/arguments" | |
| "github.com/hashicorp/terraform/internal/command/views" | |
| "github.com/hashicorp/terraform/internal/tfdiags" | |
| ) | |
| // RefreshCommand is a cli.Command implementation that refreshes the state | |
| // file. | |
| type RefreshCommand struct { | |
| Meta | |
| } | |
| func (c *RefreshCommand) Run(rawArgs []string) int { | |
| var diags tfdiags.Diagnostics | |
| // Parse and apply global view arguments | |
| common, rawArgs := arguments.ParseView(rawArgs) | |
| c.View.Configure(common) | |
| // Propagate -no-color for legacy use of Ui. The remote backend and | |
| // cloud package use this; it should be removed when/if they are | |
| // migrated to views. | |
| c.Meta.color = !common.NoColor | |
| c.Meta.Color = c.Meta.color | |
| // Parse and validate flags | |
| args, diags := arguments.ParseRefresh(rawArgs) | |
| // Instantiate the view, even if there are flag errors, so that we render | |
| // diagnostics according to the desired view | |
| view := views.NewRefresh(args.ViewType, c.View) | |
| if diags.HasErrors() { | |
| view.Diagnostics(diags) | |
| view.HelpPrompt() | |
| return 1 | |
| } | |
| // Check for user-supplied plugin path | |
| var err error | |
| if c.pluginPath, err = c.loadPluginPath(); err != nil { | |
| diags = diags.Append(err) | |
| view.Diagnostics(diags) | |
| return 1 | |
| } | |
| // FIXME: the -input flag value is needed to initialize the backend and the | |
| // operation, but there is no clear path to pass this value down, so we | |
| // continue to mutate the Meta object state for now. | |
| c.Meta.input = args.InputEnabled | |
| // FIXME: the -parallelism flag is used to control the concurrency of | |
| // Terraform operations. At the moment, this value is used both to | |
| // initialize the backend via the ContextOpts field inside CLIOpts, and to | |
| // set a largely unused field on the Operation request. Again, there is no | |
| // clear path to pass this value down, so we continue to mutate the Meta | |
| // object state for now. | |
| c.Meta.parallelism = args.Operation.Parallelism | |
| // Prepare the backend with the backend-specific arguments | |
| be, beDiags := c.PrepareBackend(args.State, args.ViewType) | |
| diags = diags.Append(beDiags) | |
| if diags.HasErrors() { | |
| view.Diagnostics(diags) | |
| return 1 | |
| } | |
| // Build the operation request | |
| opReq, opDiags := c.OperationRequest(be, view, args.ViewType, args.Operation) | |
| diags = diags.Append(opDiags) | |
| if diags.HasErrors() { | |
| view.Diagnostics(diags) | |
| return 1 | |
| } | |
| // Collect variable value and add them to the operation request | |
| var varDiags tfdiags.Diagnostics | |
| opReq.Variables, varDiags = args.Vars.CollectValues(func(filename string, src []byte) { | |
| opReq.ConfigLoader.Parser().ForceFileSource(filename, src) | |
| }) | |
| diags = diags.Append(varDiags) | |
| // Before we delegate to the backend, we'll print any warning diagnostics | |
| // we've accumulated here, since the backend will start fresh with its own | |
| // diagnostics. | |
| view.Diagnostics(diags) | |
| if diags.HasErrors() { | |
| return 1 | |
| } | |
| diags = nil | |
| // Perform the operation | |
| op, err := c.RunOperation(be, opReq) | |
| if err != nil { | |
| diags = diags.Append(err) | |
| view.Diagnostics(diags) | |
| return 1 | |
| } | |
| if op.State != nil { | |
| view.Outputs(op.State.RootOutputValues) | |
| } | |
| return op.Result.ExitStatus() | |
| } | |
| func (c *RefreshCommand) PrepareBackend(args *arguments.State, viewType arguments.ViewType) (backendrun.OperationsBackend, tfdiags.Diagnostics) { | |
| // FIXME: we need to apply the state arguments to the meta object here | |
| // because they are later used when initializing the backend. Carving a | |
| // path to pass these arguments to the functions that need them is | |
| // difficult but would make their use easier to understand. | |
| c.Meta.applyStateArguments(args) | |
| // Load the backend | |
| be, diags := c.backend(".", viewType) | |
| if diags.HasErrors() { | |
| return nil, diags | |
| } | |
| return be, diags | |
| } | |
| func (c *RefreshCommand) OperationRequest(be backendrun.OperationsBackend, view views.Refresh, viewType arguments.ViewType, args *arguments.Operation, | |
| ) (*backendrun.Operation, tfdiags.Diagnostics) { | |
| var diags tfdiags.Diagnostics | |
| // Build the operation | |
| opReq := c.Operation(be, viewType) | |
| opReq.ConfigDir = "." | |
| opReq.Hooks = view.Hooks() | |
| opReq.Targets = args.Targets | |
| opReq.Type = backendrun.OperationTypeRefresh | |
| opReq.View = view.Operation() | |
| // EXPERIMENTAL: maybe enable deferred actions | |
| if c.AllowExperimentalFeatures { | |
| opReq.DeferralAllowed = args.DeferralAllowed | |
| } else if args.DeferralAllowed { | |
| // Belated flag parse error, since we don't know about experiments | |
| // support at actual parse time. | |
| diags = diags.Append(tfdiags.Sourceless( | |
| tfdiags.Error, | |
| "Failed to parse command-line flags", | |
| "The -allow-deferral flag is only valid in experimental builds of Terraform.", | |
| )) | |
| return nil, diags | |
| } | |
| var err error | |
| opReq.ConfigLoader, err = c.initConfigLoader() | |
| if err != nil { | |
| diags = diags.Append(fmt.Errorf("Failed to initialize config loader: %s", err)) | |
| return nil, diags | |
| } | |
| return opReq, diags | |
| } | |
| func (c *RefreshCommand) Help() string { | |
| helpText := ` | |
| Usage: terraform [global options] refresh [options] | |
| Update the state file of your infrastructure with metadata that matches | |
| the physical resources they are tracking. | |
| This will not modify your infrastructure, but it can modify your | |
| state file to update metadata. This metadata might cause new changes | |
| to occur when you generate a plan or call apply next. | |
| Options: | |
| -compact-warnings If Terraform produces any warnings that are not | |
| accompanied by errors, show them in a more compact form | |
| that includes only the summary messages. | |
| -input=true Ask for input for variables if not directly set. | |
| -lock=false Don't hold a state lock during the operation. This is | |
| dangerous if others might concurrently run commands | |
| against the same workspace. | |
| -lock-timeout=0s Duration to retry a state lock. | |
| -no-color If specified, output won't contain any color. | |
| -parallelism=n Limit the number of concurrent operations. Defaults to 10. | |
| -target=resource Resource to target. Operation will be limited to this | |
| resource and its dependencies. This flag can be used | |
| multiple times. | |
| -var 'foo=bar' Set a variable in the Terraform configuration. This | |
| flag can be set multiple times. | |
| -var-file=foo Set variables in the Terraform configuration from | |
| a file. If "terraform.tfvars" or any ".auto.tfvars" | |
| files are present, they will be automatically loaded. | |
| -state, state-out, and -backup are legacy options supported for the local | |
| backend only. For more information, see the local backend's documentation. | |
| ` | |
| return strings.TrimSpace(helpText) | |
| } | |
| func (c *RefreshCommand) Synopsis() string { | |
| return "Update the state to match remote systems" | |
| } |