main: new global option -chdir

This new option is intended to address the previous inconsistencies where
some older subcommands supported partially changing the target directory
(where Terraform would use the new directory inconsistently) where newer
commands did not support that override at all.

Instead, now Terraform will accept a -chdir command at the start of the
command line (before the subcommand) and will interpret it as a request
to direct all actions that would normally be taken in the current working
directory into the target directory instead. This is similar to options
offered by some other similar tools, such as the -C option in "make".

The new option is only accepted at the start of the command line (before
the subcommand) as a way to reflect that it is a global command (not
specific to a particular subcommand) and that it takes effect _before_
executing the subcommand. This also means it'll be forced to appear before
any other command-specific arguments that take file paths, which hopefully
communicates that those other arguments are interpreted relative to the
overridden path.

As a measure of pragmatism for existing uses, the path.cwd object in
the Terraform language will continue to return the _original_ working
directory (ignoring -chdir), in case that is important in some exceptional
workflows. The path.root object gives the root module directory, which
will always match the overriden working directory unless the user
simultaneously uses one of the legacy directory override arguments, which
is not a pattern we intend to support in the long run.

As a first step down the deprecation path, this commit adjusts the
documentation to de-emphasize the inconsistent old command line arguments,
including specific guidance on what to use instead for the main three
workflow commands, but all of those options remain supported in the same
way as they were before. In a later commit we'll make those arguments
produce a visible deprecation warning in Terraform's output, and then
in an even later commit we'll remove them entirely so that -chdir is the
single supported way to run Terraform from a directory other than the
one containing the root module configuration.
This commit is contained in:
Martin Atkins 2020-09-01 15:45:12 -07:00
parent 883e4487a2
commit efe78b2910
22 changed files with 397 additions and 102 deletions

View File

@ -9,6 +9,7 @@ import (
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/hashicorp/terraform/e2e" "github.com/hashicorp/terraform/e2e"
"github.com/zclconf/go-cty/cty"
) )
// The tests in this file are for the "primary workflow", which includes // The tests in this file are for the "primary workflow", which includes
@ -126,3 +127,90 @@ func TestPrimarySeparatePlan(t *testing.T) {
} }
} }
func TestPrimaryChdirOption(t *testing.T) {
t.Parallel()
// This test case does not include any provider dependencies, so it's
// safe to run it even when network access is disallowed.
fixturePath := filepath.Join("testdata", "chdir-option")
tf := e2e.NewBinary(terraformBin, fixturePath)
defer tf.Close()
//// INIT
stdout, stderr, err := tf.Run("-chdir=subdir", "init")
if err != nil {
t.Fatalf("unexpected init error: %s\nstderr:\n%s", err, stderr)
}
//// PLAN
stdout, stderr, err = tf.Run("-chdir=subdir", "plan", "-out=tfplan")
if err != nil {
t.Fatalf("unexpected plan error: %s\nstderr:\n%s", err, stderr)
}
if !strings.Contains(stdout, "0 to add, 0 to change, 0 to destroy") {
t.Errorf("incorrect plan tally; want 0 to add:\n%s", stdout)
}
if !strings.Contains(stdout, "This plan was saved to: tfplan") {
t.Errorf("missing \"This plan was saved to...\" message in plan output\n%s", stdout)
}
if !strings.Contains(stdout, "terraform apply \"tfplan\"") {
t.Errorf("missing next-step instruction in plan output\n%s", stdout)
}
// The saved plan is in the subdirectory because -chdir switched there
plan, err := tf.Plan("subdir/tfplan")
if err != nil {
t.Fatalf("failed to read plan file: %s", err)
}
diffResources := plan.Changes.Resources
if len(diffResources) != 0 {
t.Errorf("incorrect diff in plan; want no resource changes, but have:\n%s", spew.Sdump(diffResources))
}
//// APPLY
stdout, stderr, err = tf.Run("-chdir=subdir", "apply", "tfplan")
if err != nil {
t.Fatalf("unexpected apply error: %s\nstderr:\n%s", err, stderr)
}
if !strings.Contains(stdout, "Resources: 0 added, 0 changed, 0 destroyed") {
t.Errorf("incorrect apply tally; want 0 added:\n%s", stdout)
}
// The state file is in subdir because -chdir changed the current working directory.
state, err := tf.StateFromFile("subdir/terraform.tfstate")
if err != nil {
t.Fatalf("failed to read state file: %s", err)
}
gotOutput := state.RootModule().OutputValues["cwd"]
wantOutputValue := cty.StringVal(tf.Path()) // path.cwd returns the original path, because path.root is how we get the overridden path
if gotOutput == nil || !wantOutputValue.RawEquals(gotOutput.Value) {
t.Errorf("incorrect value for cwd output\ngot: %#v\nwant Value: %#v", gotOutput, wantOutputValue)
}
gotOutput = state.RootModule().OutputValues["root"]
wantOutputValue = cty.StringVal(tf.Path("subdir")) // path.root is a relative path, but the text fixture uses abspath on it.
if gotOutput == nil || !wantOutputValue.RawEquals(gotOutput.Value) {
t.Errorf("incorrect value for root output\ngot: %#v\nwant Value: %#v", gotOutput, wantOutputValue)
}
if len(state.RootModule().Resources) != 0 {
t.Errorf("unexpected resources in state")
}
//// DESTROY
stdout, stderr, err = tf.Run("-chdir=subdir", "destroy", "-auto-approve")
if err != nil {
t.Fatalf("unexpected destroy error: %s\nstderr:\n%s", err, stderr)
}
if !strings.Contains(stdout, "Resources: 0 destroyed") {
t.Errorf("incorrect destroy tally; want 0 destroyed:\n%s", stdout)
}
}

View File

@ -0,0 +1,7 @@
output "cwd" {
value = path.cwd
}
output "root" {
value = abspath(path.root)
}

View File

@ -39,6 +39,17 @@ type Meta struct {
// command with a Meta field. These are expected to be set externally // command with a Meta field. These are expected to be set externally
// (not from within the command itself). // (not from within the command itself).
// OriginalWorkingDir, if set, is the actual working directory where
// Terraform was run from. This might not be the _actual_ current working
// directory, because users can add the -chdir=... option to the beginning
// of their command line to ask Terraform to switch.
//
// Most things should just use the current working directory in order to
// respect the user's override, but we retain this for exceptional
// situations where we need to refer back to the original working directory
// for some reason.
OriginalWorkingDir string
Color bool // True if output should be colored Color bool // True if output should be colored
GlobalPluginDirs []string // Additional paths to search for plugins GlobalPluginDirs []string // Additional paths to search for plugins
PluginOverrides *PluginOverrides // legacy overrides from .terraformrc file PluginOverrides *PluginOverrides // legacy overrides from .terraformrc file
@ -385,6 +396,7 @@ func (m *Meta) contextOpts() (*terraform.ContextOpts, error) {
opts.Meta = &terraform.ContextMeta{ opts.Meta = &terraform.ContextMeta{
Env: workspace, Env: workspace,
OriginalWorkingDir: m.OriginalWorkingDir,
} }
return &opts, nil return &opts, nil

View File

@ -40,7 +40,7 @@ const (
OutputPrefix = "o:" OutputPrefix = "o:"
) )
func initCommands(config *cliconfig.Config, services *disco.Disco, providerSrc getproviders.Source, unmanagedProviders map[addrs.Provider]*plugin.ReattachConfig) { func initCommands(originalWorkingDir string, config *cliconfig.Config, services *disco.Disco, providerSrc getproviders.Source, unmanagedProviders map[addrs.Provider]*plugin.ReattachConfig) {
var inAutomation bool var inAutomation bool
if v := os.Getenv(runningInAutomationEnvName); v != "" { if v := os.Getenv(runningInAutomationEnvName); v != "" {
inAutomation = true inAutomation = true
@ -64,6 +64,8 @@ func initCommands(config *cliconfig.Config, services *disco.Disco, providerSrc g
dataDir := os.Getenv("TF_DATA_DIR") dataDir := os.Getenv("TF_DATA_DIR")
meta := command.Meta{ meta := command.Meta{
OriginalWorkingDir: originalWorkingDir,
Color: true, Color: true,
GlobalPluginDirs: globalPluginDirs(), GlobalPluginDirs: globalPluginDirs(),
PluginOverrides: &PluginOverrides, PluginOverrides: &PluginOverrides,

View File

@ -180,7 +180,13 @@ func (b *binary) FileExists(path ...string) bool {
// LocalState is a helper for easily reading the local backend's state file // LocalState is a helper for easily reading the local backend's state file
// terraform.tfstate from the working directory. // terraform.tfstate from the working directory.
func (b *binary) LocalState() (*states.State, error) { func (b *binary) LocalState() (*states.State, error) {
f, err := b.OpenFile("terraform.tfstate") return b.StateFromFile("terraform.tfstate")
}
// StateFromFile is a helper for easily reading a state snapshot from a file
// on disk relative to the working directory.
func (b *binary) StateFromFile(filename string) (*states.State, error) {
f, err := b.OpenFile(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -32,7 +32,7 @@ func helpFunc(commands map[string]cli.CommandFactory) string {
// website/source/docs/commands/index.html.markdown; if you // website/source/docs/commands/index.html.markdown; if you
// change this then consider updating that to match. // change this then consider updating that to match.
helpText := fmt.Sprintf(` helpText := fmt.Sprintf(`
Usage: terraform [-version] [-help] <command> [args] Usage: terraform [global options] <subcommand> [args]
The available commands for execution are listed below. The available commands for execution are listed below.
The most common, useful commands are shown first, followed by The most common, useful commands are shown first, followed by
@ -44,6 +44,13 @@ Common commands:
%s %s
All other commands: All other commands:
%s %s
Global options (use these before the subcommand, if any):
-chdir=DIR Switch to a different working directory before executing
the given subcommand.
-help Show this help output, or the help for a specified
subcommand.
-version An alias for the "version" subcommand.
`, listCommands(porcelain, maxKeyLen), listCommands(plumbing, maxKeyLen)) `, listCommands(porcelain, maxKeyLen), listCommands(plumbing, maxKeyLen))
return strings.TrimSpace(helpText) return strings.TrimSpace(helpText)

88
main.go
View File

@ -129,6 +129,11 @@ func wrappedMain() int {
log.Printf("[INFO] Go runtime version: %s", runtime.Version()) log.Printf("[INFO] Go runtime version: %s", runtime.Version())
log.Printf("[INFO] CLI args: %#v", os.Args) log.Printf("[INFO] CLI args: %#v", os.Args)
// NOTE: We're intentionally calling LoadConfig _before_ handling a possible
// -chdir=... option on the command line, so that a possible relative
// path in the TERRAFORM_CONFIG_FILE environment variable (though probably
// ill-advised) will be resolved relative to the true working directory,
// not the overridden one.
config, diags := cliconfig.LoadConfig() config, diags := cliconfig.LoadConfig()
if len(diags) > 0 { if len(diags) > 0 {
@ -203,9 +208,40 @@ func wrappedMain() int {
// Initialize the backends. // Initialize the backends.
backendInit.Init(services) backendInit.Init(services)
// Get the command line args.
binName := filepath.Base(os.Args[0])
args := os.Args[1:]
originalWd, err := os.Getwd()
if err != nil {
// It would be very strange to end up here
Ui.Error(fmt.Sprintf("Failed to determine current working directory: %s", err))
return 1
}
// The arguments can begin with a -chdir option to ask Terraform to switch
// to a different working directory for the rest of its work. If that
// option is present then extractChdirOption returns a trimmed args with that option removed.
overrideWd, args, err := extractChdirOption(args)
if err != nil {
Ui.Error(fmt.Sprintf("Invalid -chdir option: %s", err))
return 1
}
if overrideWd != "" {
os.Chdir(overrideWd)
if err != nil {
Ui.Error(fmt.Sprintf("Error handling -chdir option: %s", err))
return 1
}
}
// In tests, Commands may already be set to provide mock commands // In tests, Commands may already be set to provide mock commands
if Commands == nil { if Commands == nil {
initCommands(config, services, providerSrc, unmanagedProviders) // Commands get to hold on to the original working directory here,
// in case they need to refer back to it for any special reason, though
// they should primarily be working with the override working directory
// that we've now switched to above.
initCommands(originalWd, config, services, providerSrc, unmanagedProviders)
} }
// Run checkpoint // Run checkpoint
@ -214,10 +250,6 @@ func wrappedMain() int {
// Make sure we clean up any managed plugins at the end of this // Make sure we clean up any managed plugins at the end of this
defer plugin.CleanupClients() defer plugin.CleanupClients()
// Get the command line args.
binName := filepath.Base(os.Args[0])
args := os.Args[1:]
// Build the CLI so far, we do this so we can query the subcommand. // Build the CLI so far, we do this so we can query the subcommand.
cliRunner := &cli.CLI{ cliRunner := &cli.CLI{
Args: args, Args: args,
@ -433,3 +465,49 @@ func parseReattachProviders(in string) (map[addrs.Provider]*plugin.ReattachConfi
} }
return unmanagedProviders, nil return unmanagedProviders, nil
} }
func extractChdirOption(args []string) (string, []string, error) {
if len(args) == 0 {
return "", args, nil
}
const argName = "-chdir"
const argPrefix = argName + "="
var argValue string
var argPos int
for i, arg := range args {
if !strings.HasPrefix(arg, "-") {
// Because the chdir option is a subcommand-agnostic one, we require
// it to appear before any subcommand argument, so if we find a
// non-option before we find -chdir then we are finished.
break
}
if arg == argName || arg == argPrefix {
return "", args, fmt.Errorf("must include an equals sign followed by a directory path, like -chdir=example")
}
if strings.HasPrefix(arg, argPrefix) {
argPos = i
argValue = arg[len(argPrefix):]
}
}
// When we fall out here, we'll have populated argValue with a non-empty
// string if the -chdir=... option was present and valid, or left it
// empty if it wasn't present.
if argValue == "" {
return "", args, nil
}
// If we did find the option then we'll need to produce a new args that
// doesn't include it anymore.
if argPos == 0 {
// Easy case: we can just slice off the front
return argValue, args[1:], nil
}
// Otherwise we need to construct a new array and copy to it.
newArgs := make([]string, len(args)-1)
copy(newArgs, args[:argPos])
copy(newArgs[argPos:], args[argPos+1:])
return argValue, newArgs, nil
}

View File

@ -74,6 +74,19 @@ type ContextOpts struct {
// initializer. // initializer.
type ContextMeta struct { type ContextMeta struct {
Env string // Env is the state environment Env string // Env is the state environment
// OriginalWorkingDir is the working directory where the Terraform CLI
// was run from, which may no longer actually be the current working
// directory if the user included the -chdir=... option.
//
// If this string is empty then the original working directory is the same
// as the current working directory.
//
// In most cases we should respect the user's override by ignoring this
// path and just using the current working directory, but this is here
// for some exceptional cases where the original working directory is
// needed.
OriginalWorkingDir string
} }
// Context represents all the context that Terraform needs in order to // Context represents all the context that Terraform needs in order to

View File

@ -553,7 +553,15 @@ func (d *evaluationStateData) GetPathAttr(addr addrs.PathAttr, rng tfdiags.Sourc
switch addr.Name { switch addr.Name {
case "cwd": case "cwd":
wd, err := os.Getwd() var err error
var wd string
if d.Evaluator.Meta != nil {
// Meta is always non-nil in the normal case, but some test cases
// are not so realistic.
wd = d.Evaluator.Meta.OriginalWorkingDir
}
if wd == "" {
wd, err = os.Getwd()
if err != nil { if err != nil {
diags = diags.Append(&hcl.Diagnostic{ diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError, Severity: hcl.DiagError,
@ -563,6 +571,21 @@ func (d *evaluationStateData) GetPathAttr(addr addrs.PathAttr, rng tfdiags.Sourc
}) })
return cty.DynamicVal, diags return cty.DynamicVal, diags
} }
}
// The current working directory should always be absolute, whether we
// just looked it up or whether we were relying on ContextMeta's
// (possibly non-normalized) path.
wd, err = filepath.Abs(wd)
if err != nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: `Failed to get working directory`,
Detail: fmt.Sprintf(`The value for path.cwd cannot be determined due to a system error: %s`, err),
Subject: rng.ToHCL().Ptr(),
})
return cty.DynamicVal, diags
}
return cty.StringVal(filepath.ToSlash(wd)), diags return cty.StringVal(filepath.ToSlash(wd)), diags
case "module": case "module":

View File

@ -16,16 +16,15 @@ set of actions generated by a `terraform plan` execution plan.
## Usage ## Usage
Usage: `terraform apply [options] [dir-or-plan]` Usage: `terraform apply [options] [plan]`
By default, `apply` scans the current directory for the configuration By default, `apply` scans the current directory for the configuration
and applies the changes appropriately. However, a path to another configuration and applies the changes appropriately. However, you can optionally give the
or an execution plan can be provided. Explicit execution plan files can be path to a saved plan file that was previously created with
used to split plan and apply into separate steps within [`terraform plan`](plan.html).
[automation systems](https://learn.hashicorp.com/terraform/development/running-terraform-in-automation).
If no explicit plan file is given on the command line, `terraform apply` will If you don't give a plan file on the command line, `terraform apply` will
create a new plan automatically and prompt for approval to apply it. If the create a new plan automatically and then prompt for approval to apply it. If the
created plan does not include any changes to resources or to root module created plan does not include any changes to resources or to root module
output values then `terraform apply` will exit immediately, without prompting. output values then `terraform apply` will exit immediately, without prompting.
@ -83,3 +82,24 @@ The command-line flags are all optional. The list of available flags are:
first and the `.auto.tfvars` files after in alphabetical order. Any files first and the `.auto.tfvars` files after in alphabetical order. Any files
specified by `-var-file` override any values set automatically from files in specified by `-var-file` override any values set automatically from files in
the working directory. This flag can be used multiple times. the working directory. This flag can be used multiple times.
## Passing a Different Configuration Directory
Terraform v0.13 and earlier also accepted a directory path in place of the
plan file argument to `terraform apply`, in which case Terraform would use
that directory as the root module instead of the current working directory.
That usage is still supported in Terraform v0.14, but is now deprecated and we
plan to remove it in Terraform v0.15. If your workflow relies on overriding
the root module directory, use
[the `-chdir` global option](./#switching-working-directory-with--chdir)
instead, which works across all commands and makes Terraform consistently look
in the given directory for all files it would normaly read or write in the
current working directory.
If your previous use of this legacy pattern was also relying on Terraform
writing the `.terraform` subdirectory into the current working directory even
though the root module directory was overridden, use
[the `TF_DATA_DIR` environment variable](environment-variables.html#TF_DATA_DIR)
to direct Terraform to write the `.terraform` directory to a location other
than the current working directory.

View File

@ -14,7 +14,7 @@ evaluating [expressions](/docs/configuration/expressions.html).
## Usage ## Usage
Usage: `terraform console [options] [dir]` Usage: `terraform console [options]`
This command provides an interactive command-line console for evaluating and This command provides an interactive command-line console for evaluating and
experimenting with [expressions](/docs/configuration/expressions.html). experimenting with [expressions](/docs/configuration/expressions.html).
@ -26,9 +26,6 @@ If the current state is empty or has not yet been created, the console can be
used to experiment with the expression syntax and used to experiment with the expression syntax and
[built-in functions](/docs/configuration/functions.html). [built-in functions](/docs/configuration/functions.html).
The `dir` argument specifies the directory of the root module to use.
If a path is not specified, the current working directory is used.
The supported options are: The supported options are:
* `-state=path` - Path to a local state file. Expressions will be evaluated * `-state=path` - Path to a local state file. Expressions will be evaluated

View File

@ -13,12 +13,12 @@ infrastructure.
## Usage ## Usage
Usage: `terraform destroy [options] [dir]` Usage: `terraform destroy [options]`
Infrastructure managed by Terraform will be destroyed. This will ask for Infrastructure managed by Terraform will be destroyed. This will ask for
confirmation before destroying. confirmation before destroying.
This command accepts all the arguments and flags that the [apply This command accepts all the arguments and options that the [apply
command](/docs/commands/apply.html) accepts, with the exception of a plan file command](/docs/commands/apply.html) accepts, with the exception of a plan file
argument. argument.

View File

@ -17,7 +17,7 @@ process.
## Usage ## Usage
Usage: terraform force-unlock LOCK_ID [DIR] Usage: terraform force-unlock LOCK_ID
Manually unlock the state for the defined configuration. Manually unlock the state for the defined configuration.

View File

@ -13,19 +13,13 @@ The `terraform get` command is used to download and update
## Usage ## Usage
Usage: `terraform get [options] [dir]` Usage: `terraform get [options]`
The modules are downloaded into a local `.terraform` folder. This The modules are downloaded into a `.terraform` subdirectory of the current
folder should not be committed to version control. The `.terraform` working directory. Don't commit this directory to your version control
folder is created relative to your current working directory repository.
regardless of the `dir` argument given to this command.
If a module is already downloaded and the `-update` flag is _not_ set, The `get` command supports the following option:
Terraform will do nothing. As a result, it is safe (and fast) to run this
command multiple times.
The command-line flags are all optional. The list of available flags are:
* `-update` - If specified, modules that are already downloaded will be * `-update` - If specified, modules that are already downloaded will be
checked for updates and the updates will be downloaded if present. checked for updates and the updates will be downloaded if present.
* `dir` - Sets the path of the [root module](/docs/modules/index.html#definitions).

View File

@ -16,10 +16,10 @@ The output is in the DOT format, which can be used by
## Usage ## Usage
Usage: `terraform graph [options] [DIR]` Usage: `terraform graph [options]`
Outputs the visual dependency graph of Terraform resources according to Outputs the visual dependency graph of Terraform resources represented by the
configuration files in DIR (or the current directory if omitted). configuration in the current working directory.
The graph is outputted in DOT format. The typical program that can The graph is outputted in DOT format. The typical program that can
read this format is GraphViz, but many web services are also available read this format is GraphViz, but many web services are also available

View File

@ -22,8 +22,7 @@ most likely expect.
To view a list of the available commands at any time, just run terraform with no arguments: To view a list of the available commands at any time, just run terraform with no arguments:
```text ```text
$ terraform Usage: terraform [global options] <subcommand> [args]
Usage: terraform [-version] [-help] <command> [args]
The available commands for execution are listed below. The available commands for execution are listed below.
The most common, useful commands are shown first, followed by The most common, useful commands are shown first, followed by
@ -41,6 +40,8 @@ Common commands:
graph Create a visual graph of Terraform resources graph Create a visual graph of Terraform resources
import Import existing infrastructure into Terraform import Import existing infrastructure into Terraform
init Initialize a Terraform working directory init Initialize a Terraform working directory
login Obtain and save credentials for a remote host
logout Remove locally-stored credentials for a remote host
output Read an output from a state file output Read an output from a state file
plan Generate and show an execution plan plan Generate and show an execution plan
providers Prints a tree of the providers used in the configuration providers Prints a tree of the providers used in the configuration
@ -53,18 +54,24 @@ Common commands:
workspace Workspace management workspace Workspace management
All other commands: All other commands:
0.12upgrade Rewrites pre-0.12 module source code for v0.12
debug Debug output management (experimental) debug Debug output management (experimental)
force-unlock Manually unlock the terraform state force-unlock Manually unlock the terraform state
push Obsolete command for Terraform Enterprise legacy (v1)
state Advanced state management state Advanced state management
Global options (use these before the subcommand, if any):
-chdir=DIR Switch to a different working directory before executing
the given subcommand.
-help Show this help output, or the help for a specified
subcommand.
-version An alias for the "version" subcommand.
``` ```
To get help for any specific command, pass the -h flag to the relevant subcommand. For example, To get help for any specific command, use the -help option to the relevant
to see help about the graph subcommand: subcommand. For example, to see help about the graph subcommand:
```text ```text
$ terraform graph -h $ terraform graph -help
Usage: terraform graph [options] PATH Usage: terraform graph [options] PATH
Outputs the visual graph of Terraform resources. If the path given is Outputs the visual graph of Terraform resources. If the path given is
@ -77,6 +84,39 @@ Usage: terraform graph [options] PATH
to read this format. to read this format.
``` ```
## Switching working directory with `-chdir`
The usual way to run Terraform is to first switch to the directory containing
the `.tf` files for your root module (for example, using the `cd` command), so
that Terraform will find those files automatically without any extra arguments.
In some cases though — particularly when wrapping Terraform in automation
scripts — it can be convenient to run Terraform from a different directory than
the root module directory. To allow that, Terraform supports a global option
`-chdir=...` which you can include before the name of the subcommand you intend
to run:
```
terraform -chdir=environments/production apply
```
The `chdir` option instructs Terraform to change its working directory to the
given directory before running the given subcommand. This means that any files
that Terraform would normally read or write in the current working directory
will be read or written in the given directory instead.
There are two exceptions where Terraform will use the original working directory
even when you specify `-chdir=...`:
* Settings in the [CLI Configuration](cli-config.html) are not for a specific
subcommand and Terraform processes them before acting on the `-chdir`
option.
* In case you need to use files from the original working directory as part
of your configuration, a reference to `path.cwd` in the configuration will
produce the original working directory instead of the overridden working
directory. Use `path.root` to get the root module directory.
## Shell Tab-completion ## Shell Tab-completion
If you use either `bash` or `zsh` as your command shell, Terraform can provide If you use either `bash` or `zsh` as your command shell, Terraform can provide

View File

@ -17,23 +17,18 @@ from version control. It is safe to run this command multiple times.
## Usage ## Usage
Usage: `terraform init [options] [DIR]` Usage: `terraform init [options]`
This command performs several different initialization steps in order to This command performs several different initialization steps in order to
prepare a working directory for use. More details on these are in the prepare the current working directory for use with Terraform. More details on
sections below, but in most cases it is not necessary to worry about these these are in the sections below, but in most cases it is not necessary to worry
individual steps. about these individual steps.
This command is always safe to run multiple times, to bring the working This command is always safe to run multiple times, to bring the working
directory up to date with changes in the configuration. Though subsequent runs directory up to date with changes in the configuration. Though subsequent runs
may give errors, this command will never delete your existing configuration or may give errors, this command will never delete your existing configuration or
state. state.
If no arguments are given, the configuration in the current working directory
is initialized. It is recommended to run Terraform with the current working
directory set to the root directory of the configuration, and omit the `DIR`
argument.
## General Options ## General Options
The following options apply to all of (or several of) the initialization steps: The following options apply to all of (or several of) the initialization steps:
@ -166,3 +161,24 @@ There are some special concerns when running `init` in such an environment,
including optionally making plugins available locally to avoid repeated including optionally making plugins available locally to avoid repeated
re-installation. For more information, see re-installation. For more information, see
[`Running Terraform in Automation`](https://learn.hashicorp.com/terraform/development/running-terraform-in-automation). [`Running Terraform in Automation`](https://learn.hashicorp.com/terraform/development/running-terraform-in-automation).
## Passing a Different Configuration Directory
Terraform v0.13 and earlier also accepted a directory path in place of the
plan file argument to `terraform apply`, in which case Terraform would use
that directory as the root module instead of the current working directory.
That usage is still supported in Terraform v0.14, but is now deprecated and we
plan to remove it in Terraform v0.15. If your workflow relies on overriding
the root module directory, use
[the `-chdir` global option](./#switching-working-directory-with--chdir)
instead, which works across all commands and makes Terraform consistently look
in the given directory for all files it would normaly read or write in the
current working directory.
If your previous use of this legacy pattern was also relying on Terraform
writing the `.terraform` subdirectory into the current working directory even
though the root module directory was overridden, use
[the `TF_DATA_DIR` environment variable](environment-variables.html#TF_DATA_DIR)
to direct Terraform to write the `.terraform` directory to a location other
than the current working directory.

View File

@ -30,12 +30,12 @@ If Terraform detects no changes to resource or to root module output values,
## Usage ## Usage
Usage: `terraform plan [options] [dir]` Usage: `terraform plan [options]`
By default, `plan` requires no flags and looks in the current directory The `plan` subcommand looks in the current working directory for the root module
for the configuration and state file to refresh. configuration.
The command-line flags are all optional. The list of available flags are: The available options are:
* `-compact-warnings` - If Terraform produces any warnings that are not * `-compact-warnings` - If Terraform produces any warnings that are not
accompanied by errors, show them in a more compact form that includes only accompanied by errors, show them in a more compact form that includes only
@ -132,3 +132,24 @@ or keep it at rest for an extended period of time.
Future versions of Terraform will make plan files more Future versions of Terraform will make plan files more
secure. secure.
## Passing a Different Configuration Directory
Terraform v0.13 and earlier accepted an additional positional argument giving
a directory path, in which case Terraform would use that directory as the root
module instead of the current working directory.
That usage is still supported in Terraform v0.14, but is now deprecated and we
plan to remove it in Terraform v0.15. If your workflow relies on overriding
the root module directory, use
[the `-chdir` global option](./#switching-working-directory-with--chdir)
instead, which works across all commands and makes Terraform consistently look
in the given directory for all files it would normaly read or write in the
current working directory.
If your previous use of this legacy pattern was also relying on Terraform
writing the `.terraform` subdirectory into the current working directory even
though the root module directory was overridden, use
[the `TF_DATA_DIR` environment variable](environment-variables.html#TF_DATA_DIR)
to direct Terraform to write the `.terraform` directory to a location other
than the current working directory.

View File

@ -9,36 +9,14 @@ description: |-
# Command: providers # Command: providers
The `terraform providers` command prints information about the providers The `terraform providers` command shows information about the
used in the current configuration. [provider requirements](/docs/configuration/provider-requirements.html) of the
configuration in the current working directory, as an aid to understanding
where each requirement was detected from.
Provider dependencies are created in several different ways: This command also has several subcommands with different purposes, which
are listed in the navigation bar.
* Explicit use of a `terraform.required_providers` block in configuration,
optionally including a version constraint.
* Explicit use of a `provider` block in configuration, optionally including
a version constraint.
* Use of any resource belonging to a particular provider in a `resource` or
`data` block in configuration.
* Existence of any resource instance belonging to a particular provider in
the current _state_. For example, if a particular resource is removed
from configuration, it continues to create a dependency on its provider
until its instances have been destroyed.
This command gives an overview of all of the current dependencies, as an aid
to understanding why a particular provider is needed.
This command is a nested subcommand, meaning that it has further subcommands.
These subcommands are listed to the left.
## Usage ## Usage
Usage: `terraform providers [config-path]` Usage: `terraform providers`
Pass an explicit configuration path to override the default of using the
current working directory.
Please refer to the subcommands to the left for additional usages.

View File

@ -19,12 +19,9 @@ plan or apply.
## Usage ## Usage
Usage: `terraform refresh [options] [dir]` Usage: `terraform refresh [options]`
By default, `refresh` requires no flags and looks in the current directory The `terraform refresh` command accepts the following options:
for the configuration and state file to refresh.
The command-line flags are all optional. The list of available flags are:
* `-backup=path` - Path to the backup file. Defaults to `-state-out` with * `-backup=path` - Path to the backup file. Defaults to `-state-out` with
the ".backup" extension. Disabled by setting to "-". the ".backup" extension. Disabled by setting to "-".

View File

@ -32,12 +32,13 @@ The output format is covered in detail in [JSON Output Format](/docs/internals/j
## Usage ## Usage
Usage: `terraform show [options] [path]` Usage: `terraform show [options] [file]`
You may use `show` with a path to either a Terraform state file or plan You may use `show` with a path to either a Terraform state file or plan
file. If no path is specified, the current state will be shown. file. If you don't specify a file path, Terraform will show the latest state
snapshot.
The command-line flags are all optional. The list of available flags are: This command accepts the following options:
* `-no-color` - Disables output with coloring * `-no-color` - Disables output with coloring

View File

@ -29,20 +29,15 @@ validation without accessing any configured remote backend, use:
$ terraform init -backend=false $ terraform init -backend=false
``` ```
If dir is not specified, then the current directory will be used.
To verify configuration in the context of a particular run (a particular To verify configuration in the context of a particular run (a particular
target workspace, input variable values, etc), use the `terraform plan` target workspace, input variable values, etc), use the `terraform plan`
command instead, which includes an implied validation check. command instead, which includes an implied validation check.
## Usage ## Usage
Usage: `terraform validate [options] [dir]` Usage: `terraform validate [options]`
By default, `validate` requires no flags and looks in the current directory This command accepts the following options:
for the configurations.
The command-line flags are all optional. The available flags are:
- `-json` - Produce output in a machine-readable JSON format, suitable for - `-json` - Produce output in a machine-readable JSON format, suitable for
use in text editor integrations and other automated systems. Always disables use in text editor integrations and other automated systems. Always disables