I have noticed that terraform will only run "file", "remote-exec" or "local-exec" on resources once. Once a resource is provisioned if the commands in a "remote-exec" are changed or a file from the provisioner "file" is changed then terraform will not make any changes to the instance. So how to I get terraform to run provisioner "file", "remote-exec" or "local-exec" everytime I run a terraform apply?
For more details:
Often I have had a resource provisioned partially due to an error from "remote-exec" causes terraform to stop (mostly due to me entering in the wrong commands while I'm writing my script). Running terraform again after this will cause the resource previously created to be destroyed and force terraform to create a new resource from scratch. This is also the only way I can run "remote-exec" twice on a resource... by creating it over from scratch.
This is really a drawback to terraform as opposed to ansible, which can do the same exact job as terraform except that it is totally idempotent. When using Ansible with tasks such as "ec2", "shell" and "copy" I can achieve the same tasks as terraform only each of those tasks will be idempotent. Ansible will automatically recognise when it doesn't need to make changes, where it does and because of this it can pick up where a failed ansible-playbook left off without destroying everything and starting from scratch. Terraform lacks this feature.
For reference here is a simple terraform resource block for an ec2 instance that uses both "remote-exec" and "file" provisioners:
resource "aws_instance" "test" {
count = ${var.amt}
ami = "ami-2d39803a"
instance_type = "t2.micro"
key_name = "ansible_aws"
tags {
name = "test${count.index}"
}
#creates ssh connection to consul servers
connection {
user = "ubuntu"
private_key="${file("/home/ubuntu/.ssh/id_rsa")}"
agent = true
timeout = "3m"
}
provisioner "remote-exec" {
inline = [<<EOF
sudo apt-get update
sudo apt-get install curl unzip
echo hi
EOF
]
}
#copying a file over
provisioner "file" {
source = "scripts/test.txt"
destination = "/path/to/file/test.txt"
}
}
Firstly, Terraform cannot model the actions of provisioners as part of a plan because they can in principle take any action.
The local-exec provisioner invokes a local executable after a resource is created. This invokes a process on the machine running Terraform, not on the resource. The remote-exec provisioner invokes a script on a remote resource after it is created.
The remote-exec provisioner invokes a script on a remote resource after it is created. This can be used to run a configuration management tool, bootstrap into a cluster, etc. To invoke a local process, see the local-exec provisioner instead.
The file provisioner copies files or directories from the machine running Terraform to the newly created resource. The file provisioner supports both ssh and winrm type connections. Important: Use provisioners as a last resort. There are better alternatives for most situations.
Came across this thread in my searches and eventually found a solution:
resource "null_resource" "ansible" {
triggers {
key = "${uuid()}"
}
provisioner "local-exec" {
command = "ansible-playbook -i /usr/local/bin/terraform-inventory -u ubuntu playbook.yml --private-key=/home/user/.ssh/aws_user.pem -u ubuntu"
}
}
You can use uuid(), which is unique to every terraform run, to trigger a null resource or provisioner.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With