What is Terraform null Resource?

Terraform null_resource is one of the features which is underused by the DevOps professional. When I looked at Terraform's documentation for null_resource I felt they have not put much effort into explaining null_resource in more detail.

But when I tried the null_resource by myself I really felt to use it more often inside my Terraform project. In this blog, I will talk about null_resource and what are the different use cases for it.

What is null_resource? - As in the name you see a prefix null which means this resource will not exist on your Cloud Infrastructure(AWS, Google Cloud, Azure). The reason is there is no terraform state associated with it, due to you can update the null_resource inside your Terraform file as many times as you can.

Terraform null_resource can be used in the following scenario -

  1. Run shell command
  2. You can use it along with local provisioner and remote provisioner
  3. It can also be used with Terraform Module, Terraform count, Terraform Data source, Local variables
  4. It can even be used for output block

We will try to address all the possible use cases for null_resource in this blog post with the help of examples.

Read More on -

How to handle null value with default value?

Table of Content

  1. How does Terraform null_resource work?
  2. What is trigger inside null_resource?
  3. Use Case 1 - null_resource with local provisioner
  4. Use Case 2 - null_resource with remote provisioner
  5. Use Case 3 - Using trigger to execute null_resource everytime
  6. Advantages of null_resource
  7. Conclusion


1. How does Terraform null_resource work?

Terraform null_resource does not have a state which means it will be executed as soon as you run $ terraform apply command but no state will be saved.

Example - Please refer to the following code of null_resource -

  1. Keep in mind when you execute $terraform apply command the null_resource will always execute it once.
 1#main.tf
 2
 3provider "aws" {
 4  region     = "eu-central-1"
 5  shared_credentials_files = ["/Users/rahulwagh/.aws/credentials"]
 6}
 7
 8resource "aws_instance" "ec2_example" {
 9  ami           = "ami-0767046d1677be5a0"
10  instance_type =  "t2.micro"
11  tags = {
12    Name = "Terraform EC2 "
13  }
14}
15
16# The following null resource will print message "Hello World"
17resource "null_resource" "null_resource_simple" {
18  provisioner "local-exec" {
19    command = "echo Hello World"
20  }
21}

Output -

Terraform null_resource local-exec execute command



2. What is the trigger inside null_resource?

The trigger is a block inside the null_resource which holds key-value pair. But to understand the trigger please have look at the following points -

  1. As the name suggest trigger, it will execute local-exec, remote-exec or data block.
  2. trigger will only work when it detects the change in the key-value pair
  3. trigger can only work once if key-value is changed once but on the other hand if the key-value pair changes its value every time it will execute the every time you run $terraform apply command.

Example-

 1#main.tf 
 2
 3provider "aws" {
 4  region     = "eu-central-1"
 5  shared_credentials_files = ["/Users/rahulwagh/.aws/credentials"]
 6}
 7
 8resource "aws_instance" "ec2_example" {
 9  ami           = "ami-0767046d1677be5a0"
10  instance_type =  "t2.micro"
11  tags = {
12    Name = "Terraform EC2 "
13  }
14}
15
16# The following null resource has the trigger
17resource "null_resource" "null_resource_simple" {
18  
19  # This trigger will only execute once when it detects the instance id of EC2 instance 
20  triggers = {
21    id = aws_instance.ec2_example.id    # to execute it every time replace - id = time()
22  }
23  provisioner "local-exec" {
24    command = "echo Hello World"
25  }
26}

**Note - Further down the post we will take a look at how to execute trigger every time.



Use Case 1 - null_resource with local provisioner

In the previous section, we have seen a very basic example of local-exec provisioner where we have executed hello world program. But with null resource and local-exec provisioner you can do more.

Here are a few examples -

1. Run multiple commands- The local-exec provisioner is not limited to running single command but instead, you can run multiple commands within local-exec provisioner using the null_resource.

 1resource "null_resource" "null_resource_simple" {
 2  
 3  triggers = {
 4    id = aws_instance.ec2_example.id  
 5  }
 6  provisioner "local-exec" {
 7    command = <<-EOT
 8      chmod +x install-istio.sh  
 9      ./install-istio.sh
10    EOT
11  }
12}

2. Create a file- As you know local-exec provisioner gives you the liberty to execute shell commands, so you can pretty much do everything possible with your terminal. For example, you can create the file -

 1 resource "null_resource" "null_resource_simple" {
 2
 3  triggers = {
 4    id = aws_instance.ec2_example.id
 5  }
 6  provisioner "local-exec" {
 7    command = <<-EOT
 8      touch hello-world.txt
 9    EOT
10  }
11}

I hope the above two examples are sufficient enough for you to explore various possibilities of null_resource along with local-exec provisioner.



Use Case 2 - null_resource with remote provisioner

The second use case for null_resource is to use it with remote_provisioner. With the help of remote_provisioner you can execute commands on the remote machine. To explain a bit further we are going to take an example in which -

  1. First we will setup the ec2 instance
  2. Write null_resource and remote-exec provisioner
  3. Use the remote-exec provisioner to connect remote EC2 instance
  4. After a successful connection create a txt file.

Here is how the terraform code would look like -

 1resource "aws_instance" "ec2_example" {
 2  ami           = "ami-0767046d1677be5a0"
 3  instance_type =  "t2.micro"
 4  tags = {
 5    Name = "Terraform EC2 "
 6  }
 7}
 8
 9resource "null_resource" "null_resource_with_remote_exec" {
10
11  triggers = {
12    id = aws_instance.ec2_example.id
13  }
14  
15  provisioner "remote-exec" {
16    inline = [
17      "touch hello.txt",
18      "echo helloworld remote provisioner >> hello.txt",
19    ]
20  }
21  
22  connection {
23    type        = "ssh"
24    host        = ec2_example.id
25    user        = "ubuntu"
26    private_key = file("/home/rahul/Jhooq/keys/aws/aws_key")
27    timeout     = "4m"
28  }
29
30}


Use Case 3 - Using trigger to execute null_resource everytime

In the final use case, we are going to see how to execute trigger every time inside the null_resource. Always remember trigger inside the null_resource always retains and key-value pair. If there is an update in the value of key-value pair then null_resource will execute every time.

Here is a Terraform code example -

 1# main.tf
 2
 3provider "aws" {
 4  region     = "eu-central-1"
 5  shared_credentials_files = ["/Users/rahulwagh/.aws/credentials"]
 6}
 7
 8resource "aws_instance" "ec2_example" {
 9  ami           = "ami-0767046d1677be5a0"
10  instance_type =  "t2-micro"
11  tags = {
12    Name = "Terraform EC2 "
13  }
14}
15
16# This null_resource will be executed everytime because of id = time().
17resource "null_resource" "null_resource_simple" {
18  
19  # Look carefully in the trigger we have assigned time() which we change value every time you run $terraform apply command.
20  triggers = {
21    id = time()
22  }
23
24  provisioner "local-exec" {
25    command = "echo Hello World"
26  }
27} 

time() - Time function is just an example in which we always get a different value and due to which the local-exec provisioner gets executed all the time.


Advantages of null_resource

Here are some the advantages of the null_resource -

  1. Don't create any infrastructure resources - It allows you to perform actions that don't create any infrastructure resources, such as running a local script or making an API call. This can be useful for tasks that need to be performed as part of your infrastructure deployment, but don't involve creating or modifying resources.

  2. Automate tasks and integrate with external systems - It provides a convenient way to automate tasks and integrate with external systems as part of your infrastructure deployment. This can save you time and effort by allowing you to automate repetitive tasks, and integrate your infrastructure with other systems and services.

  3. Modular and reusable infrastructure code - It allows you to create modular and reusable infrastructure code, by separating the actions that create resources from the actions that perform other tasks. This can make your infrastructure more maintainable and easier to understand, as it follows the principle of separating concerns.

  4. Variety of provisioners - It allows you to use a variety of provisioners, depending on your needs and preferences. For example, you can use the local-exec provisioner to run a local script, the http provisioner to make an HTTP request, or the remote-exec provisioner to run a script on a remote host. This flexibility allows you to choose the most appropriate provisioner for each task.

  5. Powerful and Flexible - Overall, the null_resource in Terraform provides a powerful and flexible way to perform actions that don't create infrastructure resources, allowing you to automate tasks and integrate with external systems as part of your infrastructure deployment.

Conclusion

I hope this blog will help you to understand the null_resource and trigger in a simple way. There are various applications and use cases for null_resources and they might differ based on your project needs. But the idea of this blog is to cover the basics use cases so that it is easy to understand.

To read further you can refer to Terraform's official documentation on null_resource.

Posts in this series