Understanding Terraform Null Resource: What It Does & How to Use It
Malcolm Matalka
Understanding Terraform Null Resource: What It Does & How to Use It
While using Terraform, there will be some situations where you might need to execute tasks that just go beyond what Terraform’s standard resources can perform. For example, once an EC2 instance is created, you might need to run a custom script on that particular instance to install some software like NGINX, or you might also need to execute a task that requires multiple resources to be ready before proceeding with the configuration. These tasks don’t always align well with Terraform’s standard resource types, which simply leads us to rely on some external scripts. For such scenarios, the Terraform null_resource
is a useful solution for executing custom tasks, including those related to application code deployment and configuration.
In this blog, we’ll explain what a null_resource
and trigger
are, how to use them, and explore some real-world use cases for the null_resource
. We will also provide some step by step examples of how to use these null_resources
effectively within your terraform configurations. We will also simplify our infrastructure management with solutions such as Terrateam.
Sign up for the Terrateam’s free trial.
What is null_resource?
The null_resource in Terraform behaves like a regular resource, but it doesn’t create any resources within your infrastructure. Instead, it allows you to run tasks that aren’t linked to any specific cloud infrastructure resources, such as EC2 instances. Whether it’s executing a script or managing dependencies between different resources, the null_resource
helps you handle these actions more efficiently.
For example, if you need to update the DNS records after creating a load balancer, the null_resource
allows you to automate this task without creating any new resources.
Starting with Terraform 1.4, the terraform_data
resource is recommended instead of the null_resource
. In Terraform 1.9 and later, you can simply use the moved
block to switch from null_resource
to terraform_data
. However, the null_resource
is still valuable for many Terraform setups that handle any custom tasks and workflows that don’t need any actual infrastructure.
Creating a null resource
The null_resource
in Terraform executes tasks immediately when you run terraform apply
, but no state is saved afterward. This makes it useful for handling operations that don’t need to track changes over time, such as running one-time scripts or any commands within your workflow.
The null_resource
is easy to implement and follows the same structure as other Terraform resource code blocks. Let’s look at how a null_resource
is typically structured:
Here, the null_resource
allows you to run a command using the command line interface (CLI) within your Terraform workflow without deploying any resources. The provisioner local-exec
runs the command on your machine, and the command argument specifies what to execute. Here, it’s an echo
command, but it could be a script or any other command.
To better understand how null_resource
works, let’s consider you need to deploy an S3 bucket by running a separate Terraform code before provisioning an EC2 instance. In this scenario, create a separate folder called bucket, which contains a main.tf file for creating the S3 bucket. We use the null_resource
to navigate to this folder, initialize the Terraform configuration, and apply it to provision the bucket. Once the bucket is successfully created, proceed to provision the EC2 instance.
Here’s how the Terraform configuration looks for creating the null resource:
In this code, the null_resource
is being used to run a series of commands before provisioning the EC2 instance.
- First, it navigates to the bucket directory, where the Terraform configuration for creating the S3 bucket is stored, allowing the process to proceed with bucket creation.
- Then, it runs
terraform init
andterraform apply --auto-approve
to create the bucket before proceeding to the EC2 instance setup.
After running terraform init
and terraform apply
for the null_resource
configuration, the null_resource
successfully executed its task. It navigated to the bucket directory and applied the separate Terraform configuration to create the S3 bucket.
The bucket, named terrateam-bucket-example
, was created successfully.
Once the bucket was set up, the EC2 instance creation was triggered, and the instance was successfully created.
This simply shows that the workflow was executed in the correct order. First, create the S3 bucket using the separate Terraform code, then create the EC2 instance as specified in the main configuration.
What does the trigger block do for null_resource?
The null_resource
itself doesn’t track any state. By default, it only runs once unless something explicitly changes in the configuration files that Terraform can detect. That’s where triggers
come in; they let you specify the conditions that should trigger the null_resource
to run again.
How Triggers Work
Triggers are essentially key-value pairs within the null_resource
block, which you can set to any dynamic value, such as the ID
of a resource that may change over time. Whenever the value of one of these key-value pairs changes, Terraform treats it as a signal to re-run the null_resource
tasks.
Here’s a basic example of how the trigger
works within the null_resource
block:
In this code:
- The
triggers
argument is set to the EC2 instance ID. If the instance is replaced or modified, the ID will change, triggering thenull_resource
to re-execute. - The
local-exec
provisioner will run the specified command or shell scripts whenever theinstance_id
changes, ensuring that any dependent tasks are automatically handled with the change.
This approach makes sure that tasks, like running any scripts or updating the configurations, are automatically re-executed whenever specific changes occur within your infrastructure.
Using the triggers argument, you can tie custom actions to dynamic updates in your Terraform setup, making sure that your Terraform workflow stays in sync with changes and operates more efficiently.
Use Cases for Null Resource
By now, we’ve seen how the null_resource
works. Let’s take a look at some practical use cases of null resources:
- Custom Software Installation: A common use case for the
null_resource
is installing specific software on an EC2 instance after it’s been created within your infrastructure. For example, after creating an EC2 instance, you may need to install Nginx for web hosting, MySQL for database management, or CloudWatch agents for monitoring. Thenull_resource
allows you to run a script that will install this software on its own once the instance is up and running. This makes sure that any important services like web servers, security groups, databases, or monitoring tools are set up without any effort from your end, making the process faster. - Environment-Specific Application Configuration: Another important use case for the
null_resource
is setting up applications on an environment basis, such as development, staging, or production. Each environment needs specific settings or configurations. For example, in production, you might connect to the database, use API keys, and enable full logging. In development, you might use a test database, mock API keys, and limit logging. Thenull_resource
lets you run scripts to apply these settings automatically. This ensures that the right configurations, like database connections and API keys, are applied to each environment without having to do anything from your end. - Patching and Updates: Regular patching and updating of software are important for maintaining the security and stability of your infrastructure. Using a
null_resource
, you can automate these tasks of applying updates to your instances after they are created. For example, after creating an EC2 instance, thenull_resource
can trigger a script to install the latest security patches and update key software packages like Nginx or MySQL. This makes sure that every instance is compliant with the latest security standards without the need for any intervention from your end.
Example Scenarios
Now, let’s take a look at some practical examples of how to use the null_resource
with different provisioners. Typically, you would use something like this in scenarios where you need to execute some commands on your machine itself, run commands on an EC2 instance, or re-execute tasks when certain values change.
null_resource with Local Provisioner
In this example, we will use the null_resource
with the local-exec
provisioner to run a simple command after an EC2 instance is created. The goal here is to create a text file named terrateam.txt within the same repo once the instance is up and running.
Here is the Terraform code for the same:
In this code:
- The
null_resource
is configured to run thelocal-exec
provisioner, which executes thetouch terrateam.txt
command, creating a file named terrateam.txt locally after the EC2 instance is provisioned. - The
triggers
argument uses the EC2 instance ID (aws_instance.infrasity_ec2.id) to determine when thenull_resource
should run. If the instance is recreated or updated, thenull_resource
re-executes based on this change.
Now run terraform init
to initialize and terraform apply
to deploy the infrastructure and execute the null_resource
block.
Once the EC2 instance is successfully created, the null_resource
triggers the command to create the terrateam.txt file.
After the null_resource
completes, the command is executed, and the terrateam.txt file is created in the same directory where your Terraform configuration is being run, confirming that the task has been executed as intended.
null_resource with Remote Provisioner
Before going to the example, let’s first understand the remote-exec
provisioner. The remote-exec
provisioner allows you to execute commands on a remote machine, such as an Amazon Web Services (AWS) EC2 instance, over an SSH connection. It’s particularly useful when you need to configure or modify a resource after it has been created within your infrastructure. By using the remote-exec
provisioner, you can remotely run scripts, install software, or even perform system updates directly on the instance.
In this example, we will use the null_resource
along with the remote-exec
provisioner to run a command on an existing EC2 instance. The goal is to connect to the instance via SSH and create a file named hello.txt on that remote server with the help of Terraform null resource.
Here’s the Terraform configuration for this example:
In this code:
- The
null_resource
is triggered by changes to the EC2 instance ID, using thetriggers
argument to monitor the instance’s ID - The connection block sets up the SSH connection to the existing EC2 instance. It uses the instance’s public IP, along with the ubuntu user and a private key stored locally, to securely connect to the instance through the AWS management console.
- The
remote-exec
provisioner runs the commandtouch hello.txt
, creating a file on the remote EC2 instance. This allows you to automate tasks that run directly on the infrastructure once it’s provisioned.
Once the infrastructure is provisioned using terraform init
and terraform apply
, the null_resource
will then connect to the remote EC2 instance via SSH and execute the touch hello.txt
command to create the file on that remote machine.
This example shows how null_resource
can automate tasks on an existing infrastructure by using the remote-exec
provisioner. It’s particularly useful when you need to run scripts or perform configuration tasks on remote servers after they’ve been provisioned.
Using Trigger to Execute null_resource Every Time
We’ve seen how the null_resource
can handle both local tasks and remote commands, depending on the setup and the workflow requirements. Now let’s move on to another example where we will use triggers to control the re-execution of null_resource
.
In this example, we’ll use the null_resource
with a dynamic trigger that ensures it re-executes every time a specific key-value pair changes. This is useful when you need to rerun tasks regularly or when any specific values in your setup change.
Here’s how the trigger works:
In this code:
- The
triggers
argument uses thetimestamp()
function as the value. Sincetimestamp()
returns the current time, it changes every timeterraform apply
is run, making sure that thenull_resource
will always re-execute. This forces thenull_resource
to be replaced on every deployment due to the trigger change. - The
local-exec
provisioner runs the commandecho 'This is running due to a trigger change'
, confirming that the task is executed every time thetimestamp()
changes. In eachterraform apply
, thenull_resource
is destroyed and recreated, re-executing the local command each time.
Once you have initialized the configuration with terraform init
, you can run the terraform apply
multiple times. Each time you run terraform apply
, the null_resource
will execute again, as the timestamp()
function changes with every apply.
You can see that with each terraform apply
, the id
is being changed again and again due to the dynamic timestamp()
. This forces the null_resource
to be replaced, triggering the task to run each time.
All of the examples above just show how null_resource
can be used to run local tasks, perform commands on remote instances, and trigger tasks whenever something changes, making it a useful tool for automating different actions in Terraform.
Now, as your infrastructure scales and you collaborate with multiple team members, managing your Terraform configuration, such as deploying cloud services and null_resource tasks, becomes a bit complex. Meanwhile, null resources are frequently used to run custom scripts, manage dependencies, or handle tasks that fall outside of the standard Terraform resources. It is crucial to review the changes deployed to the infrastructure with each pull request which might simply lead you to some errors, especially when ensuring that everything is properly triggered and executed if left unchecked. Now, to simplify the management of the deployments and ensure that the code is correctly reviewed before deployments are made, teams use solutions such as Terrateam
Null Resources Creation with Terrateam
Terrateam easily manages your infrastructure as code, collaborates with your team, and automates your deployment processes by integrating directly with your GitHub repository. Terrateam automates the plan and apply steps while providing a built-in review process. This ensures that every change, whether it’s a null_resource
task or another infrastructure update, must undergo a thorough review before deployment. This allows teams to collaborate efficiently while minimizing risks and ensuring consistency across environments. With Terrateam, you can push your changes, review them in the pull request, and let the platform take care of the rest, ensuring that even complex workflows run smoothly without manual intervention.
Terrateam Installation
Now, Let’s see how to get Terrateam up and running with your Terraform configurations.
To begin, head over to the Terrateam GitHub application, and you can easily integrate Terrateam into your github workflow.
Once installed, you’ll choose the GitHub organizations and repositories that contain your Terraform code, and you’ll be ready to go in minutes.
Next, you’ll need to set up Terrateam’s GitHub Actions workflow. Download the terrateam.yml file and place it in the .github/workflows directory of your repository’s default branch, which is main or master. If the directory doesn’t exist, create one, and you’re set.
Once that’s in place, you’ll need to configure the AWS resources required by Terrateam. A Terraform module and a CloudFormation template are available to easily create everything Terrateam needs. You can choose the setup method that works best for you:
- Terraform
- AWS CLI
- AWS Console
To keep it simple, let’s look at the AWS CLI method for creating the required resources.
Run the following command to create the CloudFormation stack for Terrateam. Be sure to replace GITHUB_ORG
with your GitHub organization name:
Once the stack is created, you’ll need to configure Terrateam for OIDC authentication.
To set up Terrateam to use OIDC with AWS, create a .terrateam/config.yml file at the root of your Terraform repository and simply add this yaml file, and remember to replace AWS_ACCOUNT_ID
with your actual AWS account ID:
This configures Terrateam to use AWS OIDC authentication before running any actions. With the setup complete, we can now move forward with using Terrateam to manage our Terraform deployments.
Deploying your resources with Terrateam
Let’s start by deploying a null_resource
using Terrateam, by which you can observe how Terrateam manages plan and apply processes through GitHub without any extra effort. Within your GitHub repository where Terrateam is installed, create a new branch. This branch will be where we place our Terraform configuration. In this branch, create a file called main.tf and add the following null_resource
configuration:
This code will simply echo “Terrateam deployment in progress!” as a test to ensure the process works smoothly. After adding the code, commit and push the main.tf file to the master/main branch of your repository. Then, create a pull request from your branch to the default branch. This is where Terrateam steps in to manage the Terraform process.
Once the pull request is created, Terrateam will run a terraform plan
to show you a preview of the changes. No need for manual commands; Terrateam handles this for you.
To apply the changes, simply comment terrateam apply on the pull request.
Terrateam will trigger the terraform apply process, executing the null_resource
and printing the “Terrateam deployment in progress!” message.
After the changes are applied and everything looks good, you can merge the pull request.
By following these steps, you’ve now completed your first Terrateam deployment. This simple setup shows how Terrateam can take care of your Terraform workflows, from planning to applying changes, directly through GitHub.
Sign up for Terrateam now and simplify your Terraform automation!
Conclusion
By now, you should have a solid understanding of how to use Terraform’s null_resource
for tasks, from running local commands to executing scripts on remote instances and handling tasks that need to be re-executed with triggers. We’ve covered practical use cases that show the flexibility of null_resource
and walked through examples that demonstrate how it can fit into your Terraform workflow and simplify it using tools such as Terrateam.