From e0d72930fa453a1fde9b44e45a70b581ba1b609d Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 5 Sep 2019 15:50:04 -0700 Subject: [PATCH] website: Warn against using provisioners For a long time now we've been advising against the use of provisioners, but our documentation for them is pretty prominent on the website in comparision to the better alternatives, and so it's little surprise that many users end up making significant use of them. Although in the longer term a change to our information architecture would probably address this even better, this is an attempt to be explicit about the downsides of using provisioners and to prominently describe the alternatives that are available for common use-cases, along with some reasons why we consider them to be better. I took the unusual step here of directly linking to specific provider documentation pages about the alternatives, even though we normally try to keep the core documentation provider-agnostic, because otherwise that information tends to be rather buried in the provider documentation and thus the reader would be reasonable to use provisioners just because we're not giving specific enough alternative recommendations. --- website/docs/provisioners/chef.html.markdown | 4 + .../provisioners/connection.html.markdown | 6 +- website/docs/provisioners/file.html.markdown | 4 + .../docs/provisioners/habitat.html.markdown | 5 + website/docs/provisioners/index.html.markdown | 143 +++++++++++++++++- .../provisioners/local-exec.html.markdown | 4 + .../docs/provisioners/puppet.html.markdown | 4 + .../provisioners/remote-exec.html.markdown | 3 + .../docs/provisioners/salt-masterless.html.md | 4 + 9 files changed, 170 insertions(+), 7 deletions(-) diff --git a/website/docs/provisioners/chef.html.markdown b/website/docs/provisioners/chef.html.markdown index 67c34b59a3..245db62a6d 100644 --- a/website/docs/provisioners/chef.html.markdown +++ b/website/docs/provisioners/chef.html.markdown @@ -12,6 +12,10 @@ The `chef` provisioner installs, configures and runs the Chef Client on a remote resource. The `chef` provisioner supports both `ssh` and `winrm` type [connections](/docs/provisioners/connection.html). +-> **Note:** Provisioners should only be used as a last resort. For most +common situations there are better alternatives. For more information, see +[the main Provisioners page](./). + ## Requirements The `chef` provisioner has some prerequisites for specific connection types: diff --git a/website/docs/provisioners/connection.html.markdown b/website/docs/provisioners/connection.html.markdown index 56dabe442d..e2931d7235 100644 --- a/website/docs/provisioners/connection.html.markdown +++ b/website/docs/provisioners/connection.html.markdown @@ -11,6 +11,10 @@ description: |- Many provisioners require access to the remote resource. For example, a provisioner may need to use SSH or WinRM to connect to the resource. +-> **Note:** Provisioners should only be used as a last resort. For most +common situations there are better alternatives. For more information, see +[the main Provisioners page](./). + Terraform uses a number of defaults when connecting to a resource, but these can be overridden using a `connection` block in either a `resource` or `provisioner`. Any `connection` information provided in a `resource` will apply @@ -62,7 +66,7 @@ provisioner "file" { * `password` - The password we should use for the connection. In some cases this is specified by the provider. -* `host` - (Required) The address of the resource to connect to. This is usually specified by the provider. +* `host` - (Required) The address of the resource to connect to. * `port` - The port to connect to. Defaults to `22` when using type `ssh` and defaults to `5985` when using type `winrm`. diff --git a/website/docs/provisioners/file.html.markdown b/website/docs/provisioners/file.html.markdown index c3b8602e83..5b3c80b0c2 100644 --- a/website/docs/provisioners/file.html.markdown +++ b/website/docs/provisioners/file.html.markdown @@ -12,6 +12,10 @@ The `file` provisioner is used to copy files or directories from the machine executing Terraform to the newly created resource. The `file` provisioner supports both `ssh` and `winrm` type [connections](/docs/provisioners/connection.html). +-> **Note:** Provisioners should only be used as a last resort. For most +common situations there are better alternatives. For more information, see +[the main Provisioners page](./). + ## Example usage ```hcl diff --git a/website/docs/provisioners/habitat.html.markdown b/website/docs/provisioners/habitat.html.markdown index f96239b8ef..8ed2edb2c9 100644 --- a/website/docs/provisioners/habitat.html.markdown +++ b/website/docs/provisioners/habitat.html.markdown @@ -9,6 +9,11 @@ description: |- # Habitat Provisioner The `habitat` provisioner installs the [Habitat](https://habitat.sh) supervisor and loads configured services. This provisioner only supports Linux targets using the `ssh` connection type at this time. + +-> **Note:** Provisioners should only be used as a last resort. For most +common situations there are better alternatives. For more information, see +[the main Provisioners page](./). + ## Requirements The `habitat` provisioner has some prerequisites for specific connection types: diff --git a/website/docs/provisioners/index.html.markdown b/website/docs/provisioners/index.html.markdown index b2ab93911a..c3a536ce1d 100644 --- a/website/docs/provisioners/index.html.markdown +++ b/website/docs/provisioners/index.html.markdown @@ -8,25 +8,156 @@ description: |- # Provisioners +Provisioners can be used to model specific actions on the local machine or on +a remote machine in order to prepare servers or other infrastructure objects +for service. + +## Provisioners are a Last Resort + +Terraform includes the concept of provisioners as a measure of pragmatism, +knowing that there will always be certain behaviors that can't be directly +represented in Terraform's declarative model. + +However, they also add a considerable amount of complexity and uncertainty to +Terraform usage. Firstly, Terraform cannot model the actions of provisioners +as part of a plan because they can in principle take any action. Secondly, +successful use of provisioners requires coordinating many more details than +Terraform usage usually requires: direct network access to your servers, +issuing Terraform credentials to log in, making sure that all of the necessary +external software is installed, etc. + +The following sections describe some situations which can be solved with +provisioners in principle, but where better solutions are also available. We do +not recommend using provisioners for any of the use-cases described in the +following sections. + +Even if your specific use-case is not described in the following sections, we +still recommend attempting to solve it using other techniques first, and use +provisioners only if there is no other option. + +### Passing data into virtual machines and other compute resources + +When deploying virtual machines or other similar compute resources, we often +need to pass in data about other related infrastructure that the software on +that server will need to do its job. + +The various provisioners that interact with remote servers over SSH or WinRM +can potentially be used to pass such data by logging in to the server and +providing it directly, but most cloud computing platforms provide mechanisms +to pass data to instances at the time of their creation such that the data +is immediately available on system boot. For example: + +* Alibaba Cloud: `user_data` on + [`alicloud_instance`](/docs/providers/alicloud/r/instance.html) + or [`alicloud_launch_template`](/docs/providers/alicloud/r/launch_template.html). +* Amazon EC2: `user_data` or `user_data_base64` on + [`aws_instance`](/docs/providers/aws/r/instance.html), + [`aws_launch_template`](/docs/providers/aws/r/launch_template.html), + and [`aws_launch_configuration`](/docs/providers/aws/r/launch_configuration.html). +* Amazon Lightsail: `user_data` on + [`aws_lightsail_instance`](/docs/providers/aws/r/lightsail_instance.html). +* Microsoft Azure: `custom_data` on + [`azurerm_virtual_machine`](/docs/providers/azurerm/r/virtual_machine.html) + or [`azurerm_virtual_machine_scale_set`](/docs/providers/azurerm/r/virtual_machine_scale_set.html). +* Google Cloud Platform: `metadata` on + [`google_compute_instance`](/docs/providers/google/r/compute_instance.html) + or [`google_compute_instance_group`](/docs/providers/google/r/compute_instance_group.html). +* Oracle Cloud Infrastructure: `metadata` or `extended_metadata` on + [`oci_core_instance`](/docs/providers/oci/r/core_instance.html) + or [`oci_core_instance_configuration`](/docs/providers/oci/r/core_instance_configuration.html). +* VMWare vSphere: Attach a virtual CDROM to + [`vsphere_virtual_machine`](/docs/providers/vsphere/r/virtual_machine.html) + using the `cdrom` block, containing a file called `user-data.txt`. + +Many official Linux distribution disk images include software called +[cloud-init](https://cloudinit.readthedocs.io/en/latest/) that can automatically +process in various ways data passed via the means described above, allowing +you to run arbitrary scripts and do basic system configuration immediately +during the boot process and without the need to access the machine over SSH. + +If you are building custom machine images, you can make use of the "user data" +or "metadata" passed by the above means in whatever way makes sense to your +application, by referring to your vendor's documentation on how to access the +data at runtime. + +This approach is _required_ if you intend to use any mechanism in your cloud +provider for automatically launching and destroying servers in a group, +because in that case individual servers will launch unattended while Terraform +is not around to provision them. + +Even if you're deploying individual servers directly with Terraform, passing +data this way will allow faster boot times and simplify deployment by avoiding +the need for direct network access from Terraform to the new server and for +remote access credentials to be provided. + +### Running configuration management software + +As a convenience to users who are forced to use generic operating system +distribution images, Terraform includes a number of specialized provisioners +for launching specific configuration management products. + +We strongly recommend not using these, and instead running system configuration +steps during a custom image build process. For example, +[HashiCorp Packer](https://packer.io/) offers a similar complement of +configuration management provisioners and can run their installation steps +during a separate build process, before creating a system disk image that you +can deploy many times. + +If you are using configuration management software that has a centralized server +component, you will need to delay the _registration_ step until the final +system is booted from your custom image. To achieve that, use one of the +mechanisms described above to pass the necessary information into each instance +so that it can register itself with the configuration management server +immediately on boot, without the need to accept commands from Terraform over +SSH or WinRM. + +### First-class Terraform provider functionality may be available + +It is technically possible to use the `local-exec` provisioner to run the CLI +for your target system in order to create, update, or otherwise interact with +remote objects in that system. + +If you are trying to use a new feature of the remote system that isn't yet +supported in its Terraform provider, that might be the only option. However, +if there _is_ provider support for the feature you intend to use, prefer to +use that provider functionality rather than a provisioner so that Terraform +can be fully aware of the object and properly manage ongoing changes to it. + +Even if the functionality you need is not available in a provider today, we +suggest to consider `local-exec` usage a temporary workaround and to also +open an issue in the relevant provider's repository to discuss adding +first-class provider support. Provider development teams often prioritize +features based on interest, so opening an issue is a way to record your +interest in the feature. + Provisioners are used to execute scripts on a local or remote machine as part of resource creation or destruction. Provisioners can be used to bootstrap a resource, cleanup before destroy, run configuration management, etc. -Provisioners are added directly to any resource: +## How to use Provisioners + +-> **Note:** Provisioners should only be used as a last resort. For most +common situations there are better alternatives. For more information, see +the sections above. + +If you are certain that provisioners are the best way to solve your problem +after considering the advice in the sections above, you can add a +`provisioner` block inside the `resource` block for your compute instance. ```hcl resource "aws_instance" "web" { # ... provisioner "local-exec" { - command = "echo ${self.private_ip} > file.txt" + command = "echo The server's IP address is ${self.private_ip}" } } ``` -For provisioners other than local execution, you must specify -[connection settings](/docs/provisioners/connection.html) so Terraform knows -how to communicate with the resource. +The `local-exec` provisioner requires no other configuration, but most other +provisioners must connect to the remote system using SSH or WinRM. +You must write [a `connection` block](./connection.html) so that Terraform +will know how to communicate with the server. ## Creation-Time Provisioners @@ -127,7 +258,7 @@ resource "aws_instance" "web" { # ... provisioner "local-exec" { - command = "echo ${self.private_ip} > file.txt" + command = "echo The server's IP address is ${self.private_ip}" on_failure = "continue" } } diff --git a/website/docs/provisioners/local-exec.html.markdown b/website/docs/provisioners/local-exec.html.markdown index 8472d23123..2f8cf628f4 100644 --- a/website/docs/provisioners/local-exec.html.markdown +++ b/website/docs/provisioners/local-exec.html.markdown @@ -18,6 +18,10 @@ Note that even though the resource will be fully created when the provisioner is run, there is no guarantee that it will be in an operable state - for example system services such as `sshd` may not be started yet on compute resources. +-> **Note:** Provisioners should only be used as a last resort. For most +common situations there are better alternatives. For more information, see +[the main Provisioners page](./). + ## Example usage ```hcl diff --git a/website/docs/provisioners/puppet.html.markdown b/website/docs/provisioners/puppet.html.markdown index f211a72ab4..f4d5981452 100644 --- a/website/docs/provisioners/puppet.html.markdown +++ b/website/docs/provisioners/puppet.html.markdown @@ -12,6 +12,10 @@ The `puppet` provisioner installs, configures and runs the Puppet agent on a remote resource. The `puppet` provisioner supports both `ssh` and `winrm` type [connections](/docs/provisioners/connection.html). +-> **Note:** Provisioners should only be used as a last resort. For most +common situations there are better alternatives. For more information, see +[the main Provisioners page](./). + ## Requirements The `puppet` provisioner has some prerequisites for specific connection types: diff --git a/website/docs/provisioners/remote-exec.html.markdown b/website/docs/provisioners/remote-exec.html.markdown index 87b45ebe85..3085bb3359 100644 --- a/website/docs/provisioners/remote-exec.html.markdown +++ b/website/docs/provisioners/remote-exec.html.markdown @@ -14,6 +14,9 @@ into a cluster, etc. To invoke a local process, see the `local-exec` [provisioner](/docs/provisioners/local-exec.html) instead. The `remote-exec` provisioner supports both `ssh` and `winrm` type [connections](/docs/provisioners/connection.html). +-> **Note:** Provisioners should only be used as a last resort. For most +common situations there are better alternatives. For more information, see +[the main Provisioners page](./). ## Example usage diff --git a/website/docs/provisioners/salt-masterless.html.md b/website/docs/provisioners/salt-masterless.html.md index ad439918fb..a502749061 100644 --- a/website/docs/provisioners/salt-masterless.html.md +++ b/website/docs/provisioners/salt-masterless.html.md @@ -13,6 +13,10 @@ Type: `salt-masterless` The `salt-masterless` Terraform provisioner provisions machines built by Terraform using [Salt](http://saltstack.com/) states, without connecting to a Salt master. The `salt-masterless` provisioner supports `ssh` [connections](/docs/provisioners/connection.html). +-> **Note:** Provisioners should only be used as a last resort. For most +common situations there are better alternatives. For more information, see +[the main Provisioners page](./). + ## Requirements The `salt-masterless` provisioner has some prerequisites. `cURL` must be available on the remote host.