How To Use Generic Provisioner In Terraform?

Reading Time: 4 minutes

In this blog, we will be learning about what is provisioner and how to use it in terraform configuration files.

What are Provisioners?

Provisioners in terraform, allow executing specific actions on a local or a remote server. We can add provisioners to any resource directly.
The generic provisioners are the ones that are independent of any cloud provider and can be used without them. They are of three types:

  • file
  • local-exec
  • remote-exec

File Provisioner

The file type allows to transfer or copy the files or directories to the remote host. This provisioner supports ssh or a winrm connection to support the file transferring over the remote servers.

resource "aws_instance" "ssh-ec2" {
  ami           ="ami-2757f631"
  instance_type = "t2.micro"
    key_name= "aws_key"
    subnet_id = "${aws_subnet.subnet.id}"
    vpc_security_group_ids = [aws_security_group.ssh.id]

provisioner "file" {
    source      = "/home/knoldus/Downloads/spring-boot-example-main/target/hello-0.0.1-SNAPSHOT.jar"
    destination = "/home/ubuntu/file"
} 
connection {
    type        = "ssh"
    host        = self.public_ip
    user        = "ubuntu"
    private_key = file("/home/knoldus/Downloads/aws_key.pem")
}
  tags = {
    Name = "Instance-provisioner"
  }
}

resource "aws_security_group" "ssh" {
  vpc_id = "${aws_vpc.vpc.id}"
  egress = [
    {
      cidr_blocks      = [ "0.0.0.0/0"]
      description      = ""
      from_port        = 0
      ipv6_cidr_blocks = []
      prefix_list_ids  = []
      protocol         = "-1"
      security_groups  = []
      self             = false
      to_port          = 0
    }
  ]
 ingress                = [
   {
     cidr_blocks      = [ "0.0.0.0/0"]
     description      = ""
     from_port        = 22
     ipv6_cidr_blocks = []
     prefix_list_ids  = []
     protocol         = "tcp"
     security_groups  = []
     self             = false
     to_port          = 22
  }
  ]
}

In the above code, we have created an ec2 instance with an ssh connection by using aws key pair and then used the provisioner of type ‘file’. We have used it to transfer a jar file from the local to the remote system. The source is used to define the file that needs to be uploaded on the remote instance and the destination defines the destined folder on the instance where it needs to be uploaded.

Remote-exec Provisioner

Remote-exec is a provisioner that allows to execute commands/scripts on the remote server. We can run the scripts that we want to run on the remote instance after its creation. For example, we will upload the jar file of our application on the instance and use ‘remote-exec’ to execute the commands to run that file in the remote system, i.e., ec2 instance.

resource "aws_instance" "ssh-ec2" {
  ami           ="ami-2757f631"
  instance_type = "t2.micro"
    key_name= "aws_key"
    subnet_id = "${aws_subnet.subnet.id}"
    vpc_security_group_ids = [aws_security_group.ssh.id]

provisioner "file" {
    source      = "/home/knoldus/Downloads/spring-boot-example-main/target/hello-0.0.1-SNAPSHOT.jar"
    destination = "/home/ubuntu/file.jar"
} 
connection {
    type        = "ssh"
    host        = self.public_ip
    user        = "ubuntu"
    private_key = file("/home/knoldus/Downloads/aws_key.pem")
}

provisioner "remote-exec" {
  inline = [
    "sudo apt-get update",
    "sudo apt-get install openjdk-8-jdk -y",
    "sudo java -jar file.jar",
  ]
}

  tags = {
    Name = "Instance-provisioner"
  }
}

resource "aws_security_group" "ssh" {
  vpc_id = "${aws_vpc.vpc.id}"
  egress = [
    {
      cidr_blocks      = [ "0.0.0.0/0"]
      description      = ""
      from_port        = 0
      ipv6_cidr_blocks = []
      prefix_list_ids  = []
      protocol         = "-1"
      security_groups  = []
      self             = false
      to_port          = 0
    }
  ]
 ingress                = [
   {
     cidr_blocks      = [ "0.0.0.0/0"]
     description      = ""
     from_port        = 22
     ipv6_cidr_blocks = []
     prefix_list_ids  = []
     protocol         = "tcp"
     security_groups  = []
     self             = false
     to_port          = 22
  }
  ]
}

In the above code, we have used the similar example as of the ‘file’ provisioner and In extension to it, we have added the ‘remote-exec’ provisioner block. In this, we have used the command to install java in the created instance and then run the jar file that we transferred using the ‘file’ provisioner. Below is the screenshot of the output that we will get when we use the ‘terraform apply‘ command on the above configuration file. This shows that the jar file is running effectively on the created instance.

Local-exec Provisioner

As the name ‘local-exec’ suggests, this allows executing commands/scripts on the local system. This invokes a script or a command on the system on which terraform is running instead of the resource created. This provisioner does not require an ssh or a winrm connection as it runs on local system.

resource "aws_instance" "ssh-ec2" {
  ami           ="ami-2757f631"
  instance_type = "t2.micro"

  tags = {
    Name = "Instance-Provisioner"
  }

  provisioner "local-exec" {
    command = "echo ${self.private_ip} >> local-exec_IPs.txt"
  }
}

In the above code, we have created an ec2 instance and then used the ‘local-exec’. We have used it to store the private IP of the instance created above. It will store it in a file named local-exec_IPs.txt in the terraform directory on the local system. We can use this provisioner with the ‘command’ argument and also with various other arguments like ‘environment’ or ‘interpreter’.

The screenshot below shows the text file created in the terraform directory and is showing the IP of the instance created.

Conclusion

So, we have covered the three types of generic provisioners that we use in terraform also with an example of each. For further details of provisions, refer to this link.