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.
This commit is contained in:
Martin Atkins 2019-09-05 15:50:04 -07:00
parent 72f9385285
commit e0d72930fa
9 changed files with 170 additions and 7 deletions

View File

@ -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:

View File

@ -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`.

View File

@ -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

View File

@ -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:

View File

@ -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"
}
}

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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.