From 0328d116c5d37ef181f1317e46b3f6b7125ceaca Mon Sep 17 00:00:00 2001 From: James Bardin Date: Mon, 13 Feb 2023 17:28:01 -0500 Subject: [PATCH 1/6] initial terraform_data docs --- website/data/language-nav-data.json | 4 + .../language/resources/terraform-data.mdx | 95 +++++++++++++++++++ website/layouts/language.erb | 4 + 3 files changed, 103 insertions(+) create mode 100644 website/docs/language/resources/terraform-data.mdx diff --git a/website/data/language-nav-data.json b/website/data/language-nav-data.json index 35f869ea92..e82b199ff1 100644 --- a/website/data/language-nav-data.json +++ b/website/data/language-nav-data.json @@ -94,6 +94,10 @@ "path": "resources/provisioners/remote-exec" } ] + }, + { + "title": "The terraform_data Resource", + "path": "resources/terraform-data" } ] }, diff --git a/website/docs/language/resources/terraform-data.mdx b/website/docs/language/resources/terraform-data.mdx new file mode 100644 index 0000000000..c573c0feee --- /dev/null +++ b/website/docs/language/resources/terraform-data.mdx @@ -0,0 +1,95 @@ +--- +page_title: The terraform_data Managed Resource +description: >- + Retrieves the root module output values from a Terraform state snapshot stored + in a remote backend. +--- + +# The `terraform_data` Resource + +The `terraform_data` implements the standard resource lifecycle, but does not directly take any other actions. +You can use the `terraform_data` resource without requiring or configuring a provider. It is always available through a built-in provider with the [source address](/language/providers/requirements#source-addresses) `terraform.io/builtin/terraform`. + +The `terraform_data` resource is useful for storing values which need to follow a manage resource lifecycle, and for triggering provisioners when there is no other logical managed resource in which to place them. + + +## Example Usage (data for `replace_triggered_by`) + +```hcl +variable "revision" { + default = 1 +} + +resource "terraform_data" "replacement" { + input = var.revision +} + +# This resource has no convenient attribute which forces replacement, +# but can now be replaced by any change to the revision variable value. +resource "example_database" "test" { + lifecycle { + replace_triggered_by = [terraform_data.replacement] + } +} +``` + +## Example Usage (`null_resource` replacement) + +```hcl +resource "aws_instance" "web" { + # ... +} + +# A use-case for terraform_data is as a do-nothing container +# for arbitrary actions taken by a provisioner. +resource "terraform_data" "bootstrap" { + triggers_replace = aws_instance.web.id + + connection { + host = aws_instance.web.public_ip + } + + provisioner "remote-exec" { + inline = [ + "bootstrap-host.sh, + ] + } +} +``` + +## Example Usage (force computed value) + +```hcl +resource "example_resource" "test" { + name = "foo" +} + +resource "terraform_data" "example" { + input = example_resource.test.name +} + +# This resource has a problem where it will fail during plan if the +# contains list of resources has not yet been created. +resource "example_broken_collection" "test" { + # terraform_data.example.output will stand in as a computed attribute + # for example_resource.test, which will be unknown until after apply. + contains = [terraform_data.example.output] +} +``` + + +## Argument Reference + +The following arguments are supported: + +* `input` - (Optional) A value which will be stored in the instance state, and reflected in the `output` attribute after apply. + +* `triggers_replace` - (Optional) A value which is stored in the instance state, and will force replacement when the value changes. + +## Attributes Reference + +In addition to the above, the following attributes are exported: + +* `id` - A string value unique to the resource instance. + +* `output` - The computed value derived from the `input` argument. During a plan where `output` is unknown, it will still be of the same type as `input`. diff --git a/website/layouts/language.erb b/website/layouts/language.erb index 0d5c25e8c4..8c685a9a7f 100644 --- a/website/layouts/language.erb +++ b/website/layouts/language.erb @@ -142,6 +142,10 @@ +
  • + Terraform Data Resource +
  • + From df65d160225dff11f4f58508d7559d2c298493c8 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 14 Feb 2023 09:15:36 -0500 Subject: [PATCH 2/6] replace null_resource with terraform_data Replace references to null_resource with terraform_data, and link the use of terraform_data from replace_triggered_by. --- website/data/language-nav-data.json | 2 +- .../language/meta-arguments/lifecycle.mdx | 2 + .../resources/provisioners/local-exec.mdx | 4 +- .../resources/provisioners/null_resource.mdx | 38 +++++++------------ .../language/resources/terraform-data.mdx | 2 +- website/layouts/language.erb | 2 +- 6 files changed, 20 insertions(+), 30 deletions(-) diff --git a/website/data/language-nav-data.json b/website/data/language-nav-data.json index e82b199ff1..22f3999d28 100644 --- a/website/data/language-nav-data.json +++ b/website/data/language-nav-data.json @@ -96,7 +96,7 @@ ] }, { - "title": "The terraform_data Resource", + "title": "The terraform_data Resource Type", "path": "resources/terraform-data" } ] diff --git a/website/docs/language/meta-arguments/lifecycle.mdx b/website/docs/language/meta-arguments/lifecycle.mdx index cd38ffeb09..ecfb7cafe4 100644 --- a/website/docs/language/meta-arguments/lifecycle.mdx +++ b/website/docs/language/meta-arguments/lifecycle.mdx @@ -144,6 +144,8 @@ The arguments available within a `lifecycle` block are `create_before_destroy`, } ``` + If a local value, module output, or variable needs to be tracked by `replace_triggered_by`, it can be stored in a [`terraform_data`](language/resources/terraform-data) managed resource. + ## Custom Condition Checks You can add `precondition` and `postcondition` blocks with a `lifecycle` block to specify assumptions and guarantees about how resources and data sources operate. The following examples creates a precondition that checks whether the AMI is properly configured. diff --git a/website/docs/language/resources/provisioners/local-exec.mdx b/website/docs/language/resources/provisioners/local-exec.mdx index 952c131aa4..83241ffa43 100644 --- a/website/docs/language/resources/provisioners/local-exec.mdx +++ b/website/docs/language/resources/provisioners/local-exec.mdx @@ -67,7 +67,7 @@ The following arguments are supported: ### Interpreter Examples ```hcl -resource "null_resource" "example1" { +resource "terraform_data" "example1" { provisioner "local-exec" { command = "open WFH, '>completed.txt' and print WFH scalar localtime" interpreter = ["perl", "-e"] @@ -76,7 +76,7 @@ resource "null_resource" "example1" { ``` ```hcl -resource "null_resource" "example2" { +resource "terraform_data" "example2" { provisioner "local-exec" { command = "Get-Date > completed.txt" interpreter = ["PowerShell", "-Command"] diff --git a/website/docs/language/resources/provisioners/null_resource.mdx b/website/docs/language/resources/provisioners/null_resource.mdx index fc270e364c..8722938bec 100644 --- a/website/docs/language/resources/provisioners/null_resource.mdx +++ b/website/docs/language/resources/provisioners/null_resource.mdx @@ -1,23 +1,22 @@ --- page_title: Provisioners Without a Resource description: >- - A null_resource allows you to configure provisioners that are not directly - associated with a single existing resource. + A terraform_data managed resource allows you to configure provisioners that + are not directly associated with a single existing resource. --- # Provisioners Without a Resource -[null]: https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource - If you need to run provisioners that aren't directly associated with a specific resource, you can associate them with a `null_resource`. -Instances of [`null_resource`][null] are treated like normal resources, but they -don't do anything. Like with any other resource, you can configure -[provisioners](/language/resources/provisioners/syntax) and [connection -details](/language/resources/provisioners/connection) on a `null_resource`. You can also -use its `triggers` argument and any meta-arguments to control exactly where in -the dependency graph its provisioners will run. +Instances of [`terraform_data`](language/resources/terraform-data) are treated +like normal resources, but they don't do anything. Like with any other resource +type, you can configure [provisioners](/language/resources/provisioners/syntax) +and [connection details](/language/resources/provisioners/connection) on a +`terraform_data` resource. You can also use its `triggers` argument and any +meta-arguments to control exactly where in the dependency graph its +provisioners will run. ~> **Important:** Use provisioners as a last resort. There are better alternatives for most situations. Refer to [Declaring Provisioners](/language/resources/provisioners/syntax) for more details. @@ -31,16 +30,14 @@ resource "aws_instance" "cluster" { # ... } -resource "null_resource" "cluster" { - # Changes to any instance of the cluster requires re-provisioning - triggers = { - cluster_instance_ids = "${join(",", aws_instance.cluster.*.id)}" - } +resource "terraform_data" "cluster" { + # Replacement of any instance of the cluster requires re-provisioning + triggers_replace = aws_instance.cluster.[*].id # Bootstrap script can run on any instance of the cluster # So we just choose the first in this case connection { - host = "${element(aws_instance.cluster.*.public_ip, 0)}" + host = aws_instance.cluster.[0].public_ip } provisioner "remote-exec" { @@ -51,12 +48,3 @@ resource "null_resource" "cluster" { } } ``` - -## Argument Reference - -In addition to meta-arguments supported by all resources, `null_resource` -supports the following specific arguments: - -- `triggers` - A map of values which should cause this set of provisioners to - re-run. Values are meant to be interpolated references to variables or - attributes of other resources. diff --git a/website/docs/language/resources/terraform-data.mdx b/website/docs/language/resources/terraform-data.mdx index c573c0feee..703c92d4fc 100644 --- a/website/docs/language/resources/terraform-data.mdx +++ b/website/docs/language/resources/terraform-data.mdx @@ -1,5 +1,5 @@ --- -page_title: The terraform_data Managed Resource +page_title: The terraform_data Managed Resource Type description: >- Retrieves the root module output values from a Terraform state snapshot stored in a remote backend. diff --git a/website/layouts/language.erb b/website/layouts/language.erb index 8c685a9a7f..56e5884f4e 100644 --- a/website/layouts/language.erb +++ b/website/layouts/language.erb @@ -143,7 +143,7 @@
  • - Terraform Data Resource + Terraform Data Resource Type
  • From f35625820ac17201a83eeb58b49c1f1c5f75106a Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 14 Feb 2023 14:10:07 -0500 Subject: [PATCH 3/6] Update website/docs/language/resources/terraform-data.mdx Co-authored-by: Martin Atkins --- website/docs/language/resources/terraform-data.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/language/resources/terraform-data.mdx b/website/docs/language/resources/terraform-data.mdx index 703c92d4fc..d249db9e24 100644 --- a/website/docs/language/resources/terraform-data.mdx +++ b/website/docs/language/resources/terraform-data.mdx @@ -5,7 +5,7 @@ description: >- in a remote backend. --- -# The `terraform_data` Resource +# The `terraform_data` Managed Resource Type The `terraform_data` implements the standard resource lifecycle, but does not directly take any other actions. You can use the `terraform_data` resource without requiring or configuring a provider. It is always available through a built-in provider with the [source address](/language/providers/requirements#source-addresses) `terraform.io/builtin/terraform`. From 1d0fc1c6c8975ac6ffc53ca32f55f285cc2db1e0 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 14 Feb 2023 14:11:19 -0500 Subject: [PATCH 4/6] Update website/docs/language/meta-arguments/lifecycle.mdx Co-authored-by: Martin Atkins --- website/docs/language/meta-arguments/lifecycle.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/language/meta-arguments/lifecycle.mdx b/website/docs/language/meta-arguments/lifecycle.mdx index ecfb7cafe4..300654528e 100644 --- a/website/docs/language/meta-arguments/lifecycle.mdx +++ b/website/docs/language/meta-arguments/lifecycle.mdx @@ -144,7 +144,7 @@ The arguments available within a `lifecycle` block are `create_before_destroy`, } ``` - If a local value, module output, or variable needs to be tracked by `replace_triggered_by`, it can be stored in a [`terraform_data`](language/resources/terraform-data) managed resource. + `replace_triggered_by` allows only resource addresses because the decision is based on the planned actions for all of the given resources. Plain values such as local values or input variables do not have planned actions of their own, but you can treat them with a resource-like lifecycle by using them with [the `terraform_data` resource type](/language/resources/terraform-data). ## Custom Condition Checks From 0a25e40aeb9f070a485990ba464659b1d80d0f5f Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 14 Feb 2023 14:50:50 -0500 Subject: [PATCH 5/6] updates from review --- .../language/resources/terraform-data.mdx | 26 +++++-------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/website/docs/language/resources/terraform-data.mdx b/website/docs/language/resources/terraform-data.mdx index d249db9e24..d51ef41004 100644 --- a/website/docs/language/resources/terraform-data.mdx +++ b/website/docs/language/resources/terraform-data.mdx @@ -15,6 +15,12 @@ The `terraform_data` resource is useful for storing values which need to follow ## Example Usage (data for `replace_triggered_by`) + +[The `replace_triggered_by` lifecycle argument](/language/meta-arguments/lifecycle#replace_triggered_by) requires all of the given addresses to be for resources, because the decision to force replacement is based on the planned actions for all of the mentioned resources. + +Plain data values such as [Local Values](/language/values/locals) and [Input Variables](/language/values/variables) don't have any side-effects to plan against and so they aren't valid in `replace_triggered_by`. You can use `terraform_data`'s behavior of planning an action each time `input` changes to _indirectly_ use a plain value to trigger replacement. + + ```hcl variable "revision" { default = 1 @@ -57,26 +63,6 @@ resource "terraform_data" "bootstrap" { } ``` -## Example Usage (force computed value) - -```hcl -resource "example_resource" "test" { - name = "foo" -} - -resource "terraform_data" "example" { - input = example_resource.test.name -} - -# This resource has a problem where it will fail during plan if the -# contains list of resources has not yet been created. -resource "example_broken_collection" "test" { - # terraform_data.example.output will stand in as a computed attribute - # for example_resource.test, which will be unknown until after apply. - contains = [terraform_data.example.output] -} -``` - ## Argument Reference From 5adea6d58e29a19f12477ce1a152a9f8b5669ebb Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 14 Feb 2023 15:05:41 -0500 Subject: [PATCH 6/6] change provisioner example in terraform_data The existing example is already covered in the "Provisioners Without a Resource" section, so make this one slightly different by triggering a local-exec on multiple resources. --- .../language/resources/terraform-data.mdx | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/website/docs/language/resources/terraform-data.mdx b/website/docs/language/resources/terraform-data.mdx index d51ef41004..4e03d3d80b 100644 --- a/website/docs/language/resources/terraform-data.mdx +++ b/website/docs/language/resources/terraform-data.mdx @@ -46,19 +46,20 @@ resource "aws_instance" "web" { # ... } +resource "aws_instance" "database" { + # ... +} + # A use-case for terraform_data is as a do-nothing container # for arbitrary actions taken by a provisioner. resource "terraform_data" "bootstrap" { - triggers_replace = aws_instance.web.id - - connection { - host = aws_instance.web.public_ip - } + triggers_replace = [ + aws_instance.web.id, + aws_instance.database.id + ] - provisioner "remote-exec" { - inline = [ - "bootstrap-host.sh, - ] + provisioner "local-exec" { + command = "bootstrap-hosts.sh" } } ```