diff --git a/internal/command/add.go b/internal/command/add.go index bb989e31e..a49adc2ee 100644 --- a/internal/command/add.go +++ b/internal/command/add.go @@ -248,11 +248,10 @@ func (c *AddCommand) Run(rawArgs []string) int { } diags = diags.Append(view.Resource(args.Addr, schema, localProviderConfig, stateVal)) + c.View.Diagnostics(diags) if diags.HasErrors() { - c.View.Diagnostics(diags) return 1 } - return 0 } @@ -260,21 +259,27 @@ func (c *AddCommand) Help() string { helpText := ` Usage: terraform [global options] add [options] ADDRESS - Generates a blank resource template. With no additional options, - the template will be displayed in the terminal. + Generates a blank resource template. With no additional options, Terraform + will write the result to standard output. Options: --from-state=true Fill the template with values from an existing resource. - Defaults to false. + -from-state Fill the template with values from an existing resource + instance tracked in the state. By default, Terraform will + emit only placeholder values based on the resource type. --out=string Write the template to a file. If the file already - exists, the template will be appended to the file. + -out=string Write the template to a file, instead of to standard + output. --optional=true Include optional attributes. Defaults to false. + -optional Include optional arguments. By default, the result will + include only required arguments. --provider=provider Override the configured provider for the resource. Conflicts - with -from-state + -provider=provider Override the provider configuration for the resource, + using the absolute provider configuration address syntax. + + This is incompatible with -from-state, because in that + case Terraform will use the provider configuration already + selected in the state. ` return strings.TrimSpace(helpText) } diff --git a/internal/command/add_test.go b/internal/command/add_test.go index 18661d1cc..117c55255 100644 --- a/internal/command/add_test.go +++ b/internal/command/add_test.go @@ -59,7 +59,14 @@ func TestAdd_basic(t *testing.T) { fmt.Println(output.Stderr()) t.Fatalf("wrong exit status. Got %d, want 0", code) } - expected := `resource "test_instance" "new" { + expected := `# NOTE: The "terraform add" command is currently experimental and offers only a +# starting point for your resource configuration, with some limitations. +# +# The behavior of this command may change in future based on feedback, possibly +# in incompatible ways. We don't recommend building automation around this +# command at this time. If you have feedback about this command, please open +# a feature request issue in the Terraform GitHub repository. +resource "test_instance" "new" { value = null # REQUIRED string } ` @@ -85,7 +92,14 @@ func TestAdd_basic(t *testing.T) { fmt.Println(output.Stderr()) t.Fatalf("wrong exit status. Got %d, want 0", code) } - expected := `resource "test_instance" "new" { + expected := `# NOTE: The "terraform add" command is currently experimental and offers only a +# starting point for your resource configuration, with some limitations. +# +# The behavior of this command may change in future based on feedback, possibly +# in incompatible ways. We don't recommend building automation around this +# command at this time. If you have feedback about this command, please open +# a feature request issue in the Terraform GitHub repository. +resource "test_instance" "new" { value = null # REQUIRED string } ` @@ -117,7 +131,14 @@ func TestAdd_basic(t *testing.T) { t.Fatalf("wrong exit status. Got %d, want 0", code) } output := done(t) - expected := `resource "test_instance" "new" { + expected := `# NOTE: The "terraform add" command is currently experimental and offers only a +# starting point for your resource configuration, with some limitations. +# +# The behavior of this command may change in future based on feedback, possibly +# in incompatible ways. We don't recommend building automation around this +# command at this time. If you have feedback about this command, please open +# a feature request issue in the Terraform GitHub repository. +resource "test_instance" "new" { ami = null # OPTIONAL string id = null # OPTIONAL string value = null # REQUIRED string @@ -145,9 +166,17 @@ func TestAdd_basic(t *testing.T) { } // The provider happycorp/test has a localname "othertest" in the provider configuration. - expected := `resource "test_instance" "new" { + expected := `# NOTE: The "terraform add" command is currently experimental and offers only a +# starting point for your resource configuration, with some limitations. +# +# The behavior of this command may change in future based on feedback, possibly +# in incompatible ways. We don't recommend building automation around this +# command at this time. If you have feedback about this command, please open +# a feature request issue in the Terraform GitHub repository. +resource "test_instance" "new" { provider = othertest.alias - value = null # REQUIRED string + + value = null # REQUIRED string } ` @@ -317,7 +346,14 @@ func TestAdd(t *testing.T) { t.Fatalf("wrong exit status. Got %d, want 0", code) } - expected := `resource "test_instance" "new" { + expected := `# NOTE: The "terraform add" command is currently experimental and offers only a +# starting point for your resource configuration, with some limitations. +# +# The behavior of this command may change in future based on feedback, possibly +# in incompatible ways. We don't recommend building automation around this +# command at this time. If you have feedback about this command, please open +# a feature request issue in the Terraform GitHub repository. +resource "test_instance" "new" { ami = null # OPTIONAL string disks = [{ # OPTIONAL list of object mount_point = null # REQUIRED string @@ -354,7 +390,14 @@ func TestAdd(t *testing.T) { t.Fatalf("wrong exit status. Got %d, want 0", code) } - expected := `resource "test_instance" "new" { + expected := `# NOTE: The "terraform add" command is currently experimental and offers only a +# starting point for your resource configuration, with some limitations. +# +# The behavior of this command may change in future based on feedback, possibly +# in incompatible ways. We don't recommend building automation around this +# command at this time. If you have feedback about this command, please open +# a feature request issue in the Terraform GitHub repository. +resource "test_instance" "new" { value = null # REQUIRED string network_interface { # REQUIRED block } @@ -382,7 +425,14 @@ func TestAdd(t *testing.T) { t.Fatalf("wrong exit status. Got %d, want 0", code) } - expected := `resource "test_instance" "new" { + expected := `# NOTE: The "terraform add" command is currently experimental and offers only a +# starting point for your resource configuration, with some limitations. +# +# The behavior of this command may change in future based on feedback, possibly +# in incompatible ways. We don't recommend building automation around this +# command at this time. If you have feedback about this command, please open +# a feature request issue in the Terraform GitHub repository. +resource "test_instance" "new" { id = null # REQUIRED string } ` @@ -410,7 +460,14 @@ func TestAdd(t *testing.T) { t.Fatalf("wrong exit status. Got %d, want 0", code) } - expected := `resource "test_instance" "new" { + expected := `# NOTE: The "terraform add" command is currently experimental and offers only a +# starting point for your resource configuration, with some limitations. +# +# The behavior of this command may change in future based on feedback, possibly +# in incompatible ways. We don't recommend building automation around this +# command at this time. If you have feedback about this command, please open +# a feature request issue in the Terraform GitHub repository. +resource "test_instance" "new" { id = null # REQUIRED string } ` @@ -511,7 +568,14 @@ func TestAdd_from_state(t *testing.T) { t.Fatalf("wrong exit status. Got %d, want 0", code) } - expected := `resource "test_instance" "new" { + expected := `# NOTE: The "terraform add" command is currently experimental and offers only a +# starting point for your resource configuration, with some limitations. +# +# The behavior of this command may change in future based on feedback, possibly +# in incompatible ways. We don't recommend building automation around this +# command at this time. If you have feedback about this command, please open +# a feature request issue in the Terraform GitHub repository. +resource "test_instance" "new" { ami = "ami-123456" disks = [ { diff --git a/internal/command/views/add.go b/internal/command/views/add.go index 4b1c96d70..233ae7f6e 100644 --- a/internal/command/views/add.go +++ b/internal/command/views/add.go @@ -39,11 +39,21 @@ type addHuman struct { func (v *addHuman) Resource(addr addrs.AbsResourceInstance, schema *configschema.Block, pc addrs.LocalProviderConfig, stateVal cty.Value) error { var buf strings.Builder + + buf.WriteString(`# NOTE: The "terraform add" command is currently experimental and offers only a +# starting point for your resource configuration, with some limitations. +# +# The behavior of this command may change in future based on feedback, possibly +# in incompatible ways. We don't recommend building automation around this +# command at this time. If you have feedback about this command, please open +# a feature request issue in the Terraform GitHub repository. +`) + buf.WriteString(fmt.Sprintf("resource %q %q {\n", addr.Resource.Resource.Type, addr.Resource.Resource.Name)) if pc.LocalName != addr.Resource.Resource.ImpliedProvider() || pc.Alias != "" { buf.WriteString(strings.Repeat(" ", 2)) - buf.WriteString(fmt.Sprintf("provider = %s\n", pc.StringCompact())) + buf.WriteString(fmt.Sprintf("provider = %s\n\n", pc.StringCompact())) } if stateVal.RawEquals(cty.NilVal) { diff --git a/internal/command/views/add_test.go b/internal/command/views/add_test.go index 1ad0cc747..c0986e4da 100644 --- a/internal/command/views/add_test.go +++ b/internal/command/views/add_test.go @@ -27,9 +27,17 @@ func TestAddResource(t *testing.T) { t.Fatal(err.Error()) } - expected := `resource "test_instance" "foo" { + expected := `# NOTE: The "terraform add" command is currently experimental and offers only a +# starting point for your resource configuration, with some limitations. +# +# The behavior of this command may change in future based on feedback, possibly +# in incompatible ways. We don't recommend building automation around this +# command at this time. If you have feedback about this command, please open +# a feature request issue in the Terraform GitHub repository. +resource "test_instance" "foo" { provider = mytest - ami = null # OPTIONAL string + + ami = null # OPTIONAL string disks = { # OPTIONAL object mount_point = null # OPTIONAL string size = null # OPTIONAL string @@ -66,11 +74,19 @@ func TestAddResource(t *testing.T) { t.Fatal(err.Error()) } - expected := `resource "test_instance" "foo" { + expected := `# NOTE: The "terraform add" command is currently experimental and offers only a +# starting point for your resource configuration, with some limitations. +# +# The behavior of this command may change in future based on feedback, possibly +# in incompatible ways. We don't recommend building automation around this +# command at this time. If you have feedback about this command, please open +# a feature request issue in the Terraform GitHub repository. +resource "test_instance" "foo" { provider = mytest - ami = "ami-123456789" - disks = {} # sensitive - id = null + + ami = "ami-123456789" + disks = {} # sensitive + id = null } ` output := done(t) diff --git a/website/docs/cli/commands/add.html.md b/website/docs/cli/commands/add.html.md index 711ba73d2..73364f06d 100644 --- a/website/docs/cli/commands/add.html.md +++ b/website/docs/cli/commands/add.html.md @@ -8,14 +8,34 @@ description: |- # Command: add -The `terraform add` command generates a resource configuration template with -`null` placeholder values for all attributes, unless the `-from-state` flag is -used. By default, the template only includes required resource attributes; the -`-optional` flag tells Terraform to also include any optional attributes. +The `terraform add` command generates a starting point for the configuration +of a particular resource. -When `terraform add` used with the `-from-state` will _not_ print sensitive -values. You can use `terraform show ADDRESS` to see all values, including -sensitive values, recorded in state for the given resource address. +~> **Warning:** This command is currently experimental. Its exact behavior and +command line arguments are likely to change in future releases based on +feedback. We don't recommend building automation around the current design of +this command, but it's safe to use directly in a development environment +setting. + +By default, Terraform will include only the subset of arguments that are marked +by the provider as being required, and will use `null` as a placeholder for +their values. You can then replace `null` with suitable expressions in order +to make the arguments valid. + +If you use the `-optional` option then Terraform will also include arguments +that the provider declares as optional. You can then either write a suitable +expression for each argument or remove the arguments you wish to leave unset. + +If you use the `-from-state` option then Terraform will instead generate a +configuration containing expressions which will produce the same values as +the corresponding resource instance object already tracked in the Terraform +state, if for example you've previously imported the object using +[`terraform import`](import.html). + +-> **Note:** If you use `-from-state`, the result will not include expressions +for any values which are marked as sensitive in the state. If you want to +see those, you can inspect the state data directly using +`terraform state show ADDRESS`. ## Usage @@ -27,14 +47,35 @@ already exist in the configuration. Addresses are in This command accepts the following options: -`-from-state` - populate the template with values from a resource -already in state. Sensitive values are redacted. +* `-from-state` - Fill the template with values from an existing resource + instance already tracked in the state. By default, Terraform will emit only + placeholder values based on the resource type. -`-optional` - include optional attributes in the template. +* `-optional` - Include optional arguments. By default, the result will + include only required arguments. -`-out=FILENAME` - writes the template to the given filename. If the file already -exists, the template will be added to the end of the file. +* `-out=FILENAME` - Write the template to a file, instead of to standard + output. -`-provider=provider` - override the configured provider for the resource. By -default, Terraform will use the configured provider for the given resource type, -and that is the best behavior in most cases. +* `-provider=provider` - Override the provider configuration for the resource, +using the absolute provider configuration address syntax. + + Absolute provider configuration syntax uses the full source address of + the provider, rather than a local name declared in the relevant module. + For example, to select the aliased provider configuration "us-east-1" + of the official AWS provider, use: + + ``` + -provider='provider["hashicorp/aws"].us-east-1' + ``` + + or, if you are using the Windows command prompt, use Windows-style escaping + for the quotes inside the address: + + ``` + -provider=provider[\"hashicorp/aws\"].us-east-1 + ``` + + This is incompatible with `-from-state`, because in that case Terraform + will use the provider configuration already selected in the state, which + is the provider configuration that most recently managed the object.