Let us now recap provisioners in Terraform.
Provisioners provide a way for us to carry out tasks
such as running commands or scripts on remote resources
or locally on the machine where Terraform is installed.
For example, to run a bash script after a resource is created,
we can make use of the remote exec provisioner.
Using the same web server example that we have seen many times so far,
we can pass in an inline script like this.
As you can see from this example, the provisional block goes
directly inside a resource block.
These commands in the inline list will now be run on the remote instance
after it is deployed.
In this example, we are running an app to update, installing, enabling,
and starting engine X on the EC2 instance after it is deployed.
However, keep a note that nearly specifying
the provisional block with the script will not guarantee
that the commands will work.
For it to work, there should be network connectivity
between the local machine and the remote instance.
For Linux instances, this means an SSH connection
and if it's Windows, WINRM connectivity should be established.
Now this can be achieved by making use of appropriate security groups
while creating the remote resources.
In this case, we are creating the security group resource,
but it could also be an existing security group
that's already available in the AWS account.
There should also be authentication mechanism that can be used
to connect to these instances.
Most commonly an SSH key pair that can be used to access the instance.
Again, in this example, we are using an SSH key pair in the resource definition.
Now this key is created by the AWS keeper resource,
but it could also be an existing key pair, which is available in the account.
To facilitate the authentication, we can make use
of the connection block like this.
This is an example of a Linux based AMI as such the connection type is set to SSH.
The host argument is where we specify the public IP address
of the instance that was created and since we are operating directly
inside the resource block, we can make use of an expression,
self.public_ip like this.
This will effectively translate into the public IP address
of the instance that was provisioned.
The user in this case is Ubuntu as that's the default user created
within this AMI and the key we use to connect to is the private key.
Remember that earlier we used the public key pair as an argument
while creating the resource.
Now, when we run Terraform apply, we should see the remote exec provisioner
connecting to the instance using the details of the connection block
and completing the task specified in the inline list.
Next, let's take a look at the local exec provisioner.
Local exec provisioner is used to run tasks on the local machine
where we are running the Terraform binary and not on the resources
which are created by Terraform.
This is especially handy when we want to gather some data
and write into a file locally.
For example, if you want to store the public IP address
of the EC 2 instance, that was just provisioned
into a file called IP.text in the slash [?] file system,
we can run the local exec provisioner like this and just like we saw
with the remote exec provisioner, the provisional block for local exec
also goes inside the resource block.
Once the resource is created after a Terraform apply,
we can see the public IP address recorded into the file.
By default, provisioners are run after the resources are created.
This is the default behavior, and it is called
as a create time provisioner.
We can also make provisioner run before a resource is destroyed.
This is called a destroyed time provisioner.
Now this can be specifically set by making use of the event argument
and specifying its value as destroyed within the provisional block.
Another default behavior of provisioner is that if the command or script
within the provisional block fails, the Terraform apply operation
also errors out.
In this example, an incorrect part using the command causes the script
to fail and results in an error.
This is the default provisional behavior, but it can also be set
by specifically mentioning on failure argument
inside the provisional block like this.
Any resource that is created while the provisional fails is marked
as tainted within Terraform.
For the Terraform apply operation to not fail and the resource
to be created successfully even if the provisioner command
of script fails, we can set the value
of the on failure argument to continue like this.
As the best practice, Terraform recommends to use provisionals
as a last resort, wherever possible, make use of options natively available
for resource types for the provider used.
For example, use user data while creating an EC2 two instance in AWS
and custom data for Azure virtual machines, metadata for GCP, et cetera.