Open-Source Wikis

/

Terraform

/

Systems

/

Command package

hashicorp/terraform

Command package

Active contributors: terraform-core team.

Purpose

internal/command/ contains the per-subcommand handlers — PlanCommand, ApplyCommand, InitCommand, WorkspaceListCommand, etc. Each handler parses its own flags, builds a backendrun.Operation (for workflow commands) or directly performs its work (for plumbing commands), wires up a backend, and renders results through a view.

It is the largest package by file count: 245 non-test Go files. The size comes from three things — many subcommands, many sub-flags per command, and a deep view layer with both human and JSON renderings.

Directory layout

internal/command/
├── meta.go                # command.Meta — shared dependency bag
├── meta_backend.go        # backend selection / configuration / migration (114 KB)
├── meta_backend_migrate.go# state migration paths between backends
├── meta_config.go         # loading + parsing the user's configuration
├── meta_dependencies.go   # provider dependency resolution helpers
├── meta_providers.go      # bridging command.Meta to internal/getproviders
├── meta_new.go            # constructors for fresh metas (used in tests)
├── command.go             # shared utilities for subcommands
├── apply.go, plan.go      # the workflow commands
├── init.go, init_run.go   # 50 KB total — the most complex command
├── refresh.go, validate.go# more workflow commands
├── show.go, output.go     # post-plan readers
├── state*.go              # state subcommands (list, mv, rm, pull, push, …)
├── workspace_*.go         # workspace subcommands
├── providers*.go          # providers subcommand and its sub-subcommands
├── login.go, logout.go    # HCP Terraform credentials commands
├── stacks.go              # `terraform stacks ...` wrapper
├── test.go, test_cleanup.go  # native test runner
├── query.go               # read-only resource queries (1.16+)
├── cloud.go, cloud_mock.go# cloud {} block plumbing
├── arguments/             # one file per command's flag-parser
├── views/                 # one directory per output area; Human + JSON
├── format/                # diagnostic formatting and color
├── jsonplan/, jsonstate/, jsonconfig/, jsonchecks/, jsonprovider/, jsonfunction/, jsonformat/
├── junit/                 # JUnit XML output for test results
├── webbrowser/            # native browser launcher used by `terraform login`
├── workdir/               # working-directory abstraction
├── cliconfig/             # ~/.terraformrc parsing
├── clistate/              # state lock UI helpers
├── e2etest/               # binary-level CLI tests
└── testdata/              # 192 sub-directories of fixture configurations

Key types

Type File Description
command.Meta meta.go Shared context every command embeds. ~30 KB; covers working dir, streams, view, services, plugin source, allow-experimental, shutdown channel.
command.PlanCommand plan.go terraform plan handler. Parses flags, builds an Operation, dispatches to the backend, renders the plan.
command.ApplyCommand apply.go terraform apply and terraform destroy (with Destroy: true).
command.InitCommand init.go terraform init. Module installation, backend init, provider installation, lock-file maintenance.
command.RefreshCommand refresh.go Standalone refresh (deprecated for plan/apply but still present).
command.ValidateCommand validate.go Configuration validation without a backend.
command.WorkspaceCommand workspace_command.go Workspace top-level help; subcommands handled in their own files.
command.StateCommand state_command.go state top-level help.
command.LoginCommand login.go HCP Terraform login flow. Drives the OAuth dance via webbrowser.
command.LogoutCommand logout.go Companion to login.
command.TestCommand test.go Native test runner; delegates most work to internal/moduletest.
command.StacksCommand stacks.go terraform stacks ... umbrella for stack subcommands.
command.ProvidersCommand providers.go providers summary; sub-subcommands: lock, mirror, schema.

How it works

The shape of a workflow subcommand:

graph TD
    Run[Run args] --> Args[arguments.ParsePlan / ParseApply / …]
    Args --> Meta[command.Meta + view selection]
    Meta --> Config[Meta.loadSingleModule / .loadConfig]
    Config --> Backend[Meta.Backend select + configure]
    Backend --> Op[Construct backendrun.Operation]
    Op --> Run2[backend.Operation.RunOperation]
    Run2 --> Local[local.Local wraps non-ops backends]
    Local --> Ctx[terraform.Context.Plan / .Apply]
    Ctx --> Result[*plans.Plan or *states.State]
    Result --> Render[view.Plan / view.Apply]
    Render --> Exit[diagnostic-aware exit code]

For plumbing subcommands like state mv, the flow short-circuits: parse flags → load state via state manager → manipulate *states.State in memory → write it back. No graph walk, no providers.

Flag parsing

The newer subcommands use internal/command/arguments/ to centralize their flag definitions and produce typed argument structs (e.g. arguments.Plan, arguments.Apply). Older subcommands still use the standard flag.FlagSet pattern in their Run method. The codebase is migrating toward arguments/. A recent commit e24abdf6ff shows this migration in action: refactor: Update workspace select and delete subcommands to use the arguments package.

Backend selection (Meta.Backend)

meta_backend.go is the single largest non-test file in the repo (114 KB). It implements:

  • Reading the terraform { backend "..." {} } or cloud {} block from the configuration.
  • Reconciling that block with what was last initialized (stored in .terraform/).
  • Configuring the chosen backend with merged values (from config + -backend-config flags + previously stored values).
  • Detecting when the backend has changed and offering migration via meta_backend_migrate.go.
  • Wrapping the chosen backend in local.Local if the backend doesn't itself implement backendrun.OperationsBackend.

The migration paths in meta_backend_migrate.go cover a small matrix (local→remote, remote→local, remote→remote, with or without workspaces) and are guarded by interactive prompts unless -input=false.

View layer

Every command writes user-facing output through a view. The pattern:

type Plan interface {
    Operation() Operation
    Plan(plan *plans.Plan, schemas *schemarepo.Schemas)
    Diagnostics(diags tfdiags.Diagnostics)
    HelpPrompt()
}

type PlanHuman struct { /* ... */ }
type PlanJSON  struct { /* ... */ }

The arguments.ViewType flag (set by -json) selects which implementation. Tests (in internal/command/views/...) typically use a fake view that records calls.

init_run.go and the shape of a long command

internal/command/init.go is 50 KB on its own. It coordinates an enormous matrix:

  1. Module installation (internal/configs/configloadinternal/initwd).
  2. Backend selection (Meta.Backend).
  3. Provider installation (Meta.providerInstaller).
  4. Lock-file regeneration (internal/depsfile).
  5. Auto-detection of cloud {} migration.
  6. Optional plugin lock for hash-only verification.

The -upgrade, -reconfigure, -migrate-state, -input=false, -lockfile=…, and -plugin-dir=… flags interact across these steps and init.go's structure is dominated by their precedence rules.

Integration points

  • Entrypoint: invoked from commands.go at the repo root. Each subcommand factory closes over a single command.Meta.
  • Configuration: loads via internal/configs/configload (see configs).
  • Backends: registered via internal/backend/init (see backends).
  • Engine: instantiates terraform.Context and calls Plan / Apply / Refresh / Validate / Eval (see terraform-core).
  • Provider install: uses internal/getproviders (see getproviders).
  • State: reads/writes via internal/states/statemgr and internal/states/statefile.
  • Plans: writes plans via internal/plans/planfile.

Sub-areas worth their own deep dive

  • internal/command/jsonformat/ — the diff renderer that produces the colored, indented diff users see in plan output. Big enough to be its own package; uses a structural representation in jsonformat/structured/ and emits via jsonformat/computed/.
  • internal/command/jsonplan/, jsonstate/, jsonconfig/, jsonchecks/, jsonprovider/, jsonfunction/ — each defines a stable JSON schema for a particular Terraform artifact. Together these are the public API for -json output.
  • internal/command/cliconfig/ — the parser for ~/.terraformrc (and terraform.rc on Windows) including credentials, host overrides, and the provider_installation block.
  • internal/command/clistate/ — UI for state-locking races (the "Acquiring state lock..." messages and timeout warnings).

Entry points for modification

  • Adding a flag to an existing subcommand: edit the file in internal/command/arguments/<command>.go (or the flag.FlagSet setup in older commands) and the corresponding Run method.
  • Adding a new view output: extend the view interface in internal/command/views/<command>.go and implement in both Human and JSON variants. Update fake views used by tests.
  • Adding a new top-level command: implement a new command.<Name>Command type, register it in commands.go (see CLI dispatch).
  • Changing backend migration behavior: internal/command/meta_backend_migrate.go. Be prepared to write extensive tests in meta_backend_test.go.

For the JSON output formats specifically, treat them as a public API — their structure is not safe to change without a deprecation cycle.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Command package – Terraform wiki | Factory