This article explores considerations for using provisioners in Terraform, emphasizing their complexity and recommending resource-native features for better infrastructure management.
In this lesson, we explore key considerations when using provisioners in Terraform. Provisioners can be very useful for executing tasks such as bootstrapping with a Remote Exec script; however, their use should be limited. Terraform advises caution when using them due to several reasons.
Provisioners often add complexity to your configuration. Their nature of executing arbitrary system-supported commands means that Terraform cannot fully simulate or validate these actions during the planning phase.
Provisioners in Terraform can execute any system-supported command via the command or inline arguments. This flexibility makes them powerful but also creates challenges:
They increase the overall complexity of your Terraform configuration.
Due to the dynamic nature of these commands, Terraform cannot accurately predict the outcome during the plan phase.
For provisioners such as Remote Exec, it is essential to define a connection block to establish network connectivity and authenticate to the target instance. The connection details must be configured correctly on the local machine before the provisioner runs, which might not always be feasible.Consider the following sample configuration:
To mitigate the challenges associated with provisioners, Terraform recommends leveraging resource-native features. For instance, when working with Amazon Elastic Compute Cloud (EC2), you can utilize the User Data feature, ensuring that required tasks are executed during instance launch without an explicit connection block.
While using provisioners like remote-exec or User Data can be helpful, it is recommended to limit post-provisioning tasks. Over-relying on them can lead to configuration drift and harder maintenance.
A best practice is to build custom images that include all the necessary software and configurations from the start. This approach minimizes the need for post-provisioning tasks during instance initialization. For example, instead of installing NGINX during launch with User Data or remote-exec, you could use a custom Ubuntu AMI that already has NGINX installed:
Copy
Ask AI
resource "aws_instance" "webserver" { ami = "ami-0edad43b6fa892279" instance_type = "t2.micro" tags = { Name = "webserver" Description = "An NGINX WebServer on Ubuntu" }}
Templating tools come in handy when creating custom AMIs. You can generate these images by capturing an instance that has the required software and configuration, or by using tools like Packer. Packer provides a declarative approach to image building, and once the custom AMI is built, you can refer to it directly in your Terraform configuration:
Copy
Ask AI
resource "aws_instance" "webserver" { ami = "ami-XYZ" instance_type = "t2.micro" tags = { Name = "webserver" Description = "An NGINX WebServer on Ubuntu" }}
By favoring resource-specific configurations and custom-built images, you can reduce the reliance on provisioners and build a more robust, maintainable infrastructure with Terraform.