A reason to stop using the Terraform null_resource

The Terraform null_resource was introduced in 2014 and, according to Hashicorp¹, it is the 3rd most-downloaded provider in the Terraform Registry.

This resource is widely used because it acts as an empty container or resource shell. Inside this container, users can define and execute arbitrary code or scripts without being tied to a specific resource lifecycle.

One particular use case for the null_resource in Terraform is to trigger external processes or perform actions that are not directly related to any particular infrastructure resource, such as running a custom script or executing a remote command on a server.

The example below shows how to print “Hello World” to the terminal every time the ID of the EC2 instance changes.

resource "aws_instance" "ec2_example" {  
  ami           = "ami-XXXXXXXX"  
  instance_type =  "t2.micro"  
}  

  
resource "null_resource" "null_resource_hello" {  
  # Defines when the provisioner should be executed  
  triggers = {  
    # The provisioner is executed then the `id` of the EC2 instance changes  
    id = aws_instance.ec2_example.id  
  }  
  provisioner "local-exec" {  
    command = "echo Hello World"  
  }  
}

Now, if the null_resource is so popular and useful, why should we stop using it?

Well… the reason is…

A native replacement for the null_resource

Yes, a reason for you to stop using the null_resource is that the new Terraform v1.4 natively supports an empty container resource². It also means you don’t need to configure the null provider anymore, which will make your code a little cleaner.

The name of the new resource is terraform_data. It is available through a built-in provider terraform.io/builtin/terraform, and according to the announcement¹, it supports all the same capabilities as the null_resource.

Below is an example of how to implement the same code previously presented using the null_resource, but with the terraform_data resource instead.

resource "aws_instance" "ec2_example" {  
  ami           = "ami-XXXXXXXX"  
  instance_type =  "t2.micro"  
}  
  
resource "terraform_data" "tf_data_resource_hello" {  
  # Defines when the provisioner should be executed  
  triggers_replace = [  
    # The provisioner is executed then the `id` of the EC2 instance changes  
    aws_instance.ec2_example.id  
  ]  
  
  provisioner "local-exec" {  
    command = "echo Hello World"  
  }  
}

There are two main differences between the terraform_data and the null_resource.

  • The resource is named terraform_data instead of null_resource
  • The terraform_data trigger is called triggers_replace instead of triggers.

When you should not use the terraform_data resource?

The general rule from the null_resource also applies to the terraform_data. It should be used sparingly only when there is no other suitable option available. Examples of when you should not use the resource are:

  1. When a more specific resource is available: If there is a Terraform resource that is specifically designed to perform the task you need, it’s generally better to use that resource instead of the terraform_data.
  2. When you need to maintain state: The terraform_data does not maintain state, meaning it cannot be used to track changes over time or enforce certain conditions.
  3. When you need to manage complex configurations in provisioned servers: Instead, consider using dedicated configuration management tools such as Chef, Puppet, or Ansible. These tools are specifically designed for managing complex configurations and can provide better visibility, and scalability than the terraform_data.

Extra resources:

¹ https://www.hashicorp.com/blog/terraform-1-4-improves-the-cli-experience-for-terraform-cloud

² https://developer.hashicorp.com/terraform/language/resources/terraform -data?product_intent=terraform