Terraform File Provisioner
Step-00: Provisioner Concepts
- Generic Provisioners
- file
- local-exec
- remote-exec
- Provisioner Timings
- Creation-Time Provisioners (by default)
- Destroy-Time Provisioners
- Provisioner Failure Behavior
- continue
- fail
- Provisioner Connections
- Provisioner Without a Resource (Null Resource)
Pre-requisites - SSH Keys
- Create SSH Keys for Azure VM Instance if not created
# Create Folder
cd terraform-manifests/
mkdir ssh-keys
# Create SSH Key
cd ssh-ekys
ssh-keygen \
-m PEM \
-t rsa \
-b 4096 \
-C "azureuser@myserver" \
-f terraform-azure.pem
Important Note: If you give passphrase during generation, during everytime you login to VM, you also need to provide passphrase.
# List Files
ls -lrt ssh-keys/
# Files Generated after above command
Public Key: terraform-azure.pem.pub -> Rename as terraform-azure.pub
Private Key: terraform-azure.pem
# Permissions for Pem file
chmod 400 terraform-azure.pem
- Connection Block for provisioners uses this to connect to newly created Azure VM instance to copy files using
file provisioner
, execute scripts using remote-exec provisioner
Step-01: Introduction
- Understand about File Provisioners
- Create Provisioner Connections block required for File Provisioners
- We will also discuss about Creation-Time Provisioners (by default)
- Understand about Provisioner Failure Behavior
- continue
- fail
- Discuss about Destroy-Time Provisioners
Step-02: File Provisioner & Connection Block
- Understand about file provisioner & Connection Block
- Connection Block
- We can have connection block inside resource block for all provisioners
- [or] We can have connection block inside a provisioner block for that respective provisioner
- Self Object
- Important Technical Note: Resource references are restricted here because references create dependencies. Referring to a resource by name within its own block would create a dependency cycle.
- Expressions in provisioner blocks cannot refer to their parent resource by name. Instead, they can use the special self object.
- The self object represents the provisioner's parent resource, and has all of that resource's attributes.
# Connection Block for Provisioners to connect to Azure Virtual Machine
connection {
type = "ssh"
host = self.public_ip_address # Understand what is "self"
user = self.admin_username
password = ""
private_key = file("${path.module}/ssh-keys/terraform-azure.pem")
}
Step-03: Create multiple provisioners of various types
- Creation-Time Provisioners:
- By default, provisioners run when the resource they are defined within is created.
Creation-time provisioners
are only run during creation, not during updating or any other lifecycle.
- They are meant as a means to perform bootstrapping of a system.
- If a
creation-time provisioner
fails, the resource is marked as tainted
.
- A tainted resource will be planned for
destruction and recreation
upon the next terraform apply
.
- Terraform does this because a
failed provisioner
can leave a resource in a semi-configured state.
- Because Terraform cannot reason about what the provisioner does, the only way to ensure proper creation of a resource is to recreate it. This is
tainting
.
- You can change this behavior by setting the
on_failure
attribute, which is covered in detail below.
# File Provisioner-1: Copies the file-copy.html file to /tmp/file-copy.html
provisioner "file" {
source = "apps/file-copy.html"
destination = "/tmp/file-copy.html"
}
# File Provisioner-2: Copies the string in content into /tmp/file.log
provisioner "file" {
content = "VM Host Name: ${self.computer_name}" # Understand what is "self"
destination = "/tmp/file.log"
}
# File Provisioner-3: Copies the app1 folder to /tmp - FOLDER COPY
provisioner "file" {
source = "apps/app1"
destination = "/tmp"
}
# File Provisioner-4: Copies all files and folders in apps/app2 to /tmp - CONTENTS of FOLDER WILL BE COPIED
provisioner "file" {
source = "apps/app2/" # when "/" at the end is added - CONTENTS of FOLDER WILL BE COPIED
destination = "/tmp"
}
# Terraform Initialize
terraform init
# Terraform Validate
terraform validate
# Terraform Format
terraform fmt
# Terraform Plan
terraform plan
# Terraform Apply
terraform apply -auto-approve
# Verify - Login to Azure Virtual Machine Instance
ssh -i ssh-keys/terraform-azure.pem azureuser@IP_ADDRESSS_OF_YOUR_VM
ssh -i ssh-keys/terraform-azure.pem azureuser@20.185.30.127
Verify /tmp for all files copied
cd /tmp
ls -lrta /tmp
# Clean-up
terraform destroy -auto-approve
rm -rf terraform.tfsate*
Observation:
Q1: Why do we need to destroy and move with next steps?
A1:
1. Provisioners can be created during resource creation-time or destroy-time.
2. With that said, we need to test failure case of a provisioner which will faill "terraform apply".
3. We will understand that in next few steps.
Step-05: Failure Behavior: Understand Decision making when provisioner fails (continue / fail)
- By default, provisioners that fail will also cause the Terraform apply itself to fail. The on_failure setting can be used to change this. The allowed values are:
- continue: Ignore the error and continue with creation or destruction.
- fail: (Default Behavior) Raise an error and stop applying (the default behavior). If this is a creation provisioner, taint the resource.
- Try copying a file to Apache static content folder "/var/www/html" using file-provisioner
- This will fail because, the user you are using to copy these files is "azureuser" for Azure linux vm. This user don't have access to folder "/var/www/html/" top copy files.
- We need to use sudo to do that.
- All we know is we cannot copy it directly, but we know we have already copied this file in "/tmp" using file provisioner
- Try two scenarios
- No
on_failure
attribute (Same as on_failure = fail
) - default what happens It will Raise an error and stop applying. If this is a creation provisioner, it will taint the resource.
- When
on_failure = continue
, will continue creating resources
- Verify: Verify
terraform.tfstate
for "status": "tainted"
/*
# Enable this during Step-05-01 Test-1
# File Provisioner-5: Copies the file-copy.html file to /var/www/html/file-copy.html where "azureuser" don't have permission to copy
# This provisioner will fail but we don't want to taint the resource, we want to continue on_failure
provisioner "file" {
source = "apps/file-copy.html"
destination = "/var/www/html/file-copy.html"
#on_failure = continue # Enable this during Step-05-01 Test-2
}
*/
Step-05-01: Fail Case
# Test-1: Without on_failure attribute which will fail terraform apply
# Copies the file-copy.html file to /var/www/html/file-copy.html
provisioner "file" {
source = "apps/file-copy.html"
destination = "/var/www/html/file-copy.html"
}
# Terraform Validate
terraform validate
# Terraform Plan
terraform plan
# Terraform Apply
terraform apply -auto-approve
# Verify:
Verify terraform.tfstate for "status": "tainted"
## Sample Failure Log
azurerm_linux_virtual_machine.mylinuxvm: Provisioning with 'file'...
azurerm_linux_virtual_machine.mylinuxvm: Still creating... [3m0s elapsed]
╷
│ Error: file provisioner error
│
│ with azurerm_linux_virtual_machine.mylinuxvm,
│ on c6-linux-virtual-machine.tf line 71, in resource "azurerm_linux_virtual_machine" "mylinuxvm":
│ 71: provisioner "file" {
│
│ Upload failed: scp: /var/www/html/file-copy.html: Permission denied
╵
Step-05-02: Continue Case
- Uncomment
on_failure = continue
# Test-2: With on_failure = continue
# Copies the file-copy.html file to /var/www/html/file-copy.html
provisioner "file" {
source = "apps/file-copy.html"
destination = "/var/www/html/file-copy.html"
on_failure = continue
}
# Terraform Validate
terraform validate
# Terraform Plan
terraform plan
# Terraform Apply
terraform apply -auto-approve
# Verify
1. Login to Azure VM Instance
ssh -i ssh-keys/terraform-azure.pem azureuser@<VM-PUBLIC-IP>
ssh -i ssh-keys/terraform-azure.pem azureuser@20.102.55.82
2. Verify /tmp - for all files copied
3. Verify /var/www/html - file-copy.html should not be copied
4. File Provisioner didn't do job of file copy but still it didn't get fail due to the fact that we used "on_failure = continue"
Step-06: Clean-Up Resources & local working directory
# Terraform Destroy
terraform destroy -auto-approve
# Delete Terraform files
rm -rf .terraform*
rm -rf terraform.tfstate*
Step-07: Roll back change for Student seamless demo
# c6-linux-virtual-machine.tf
Comment last File Provisioner so that it will be enabled when required by students during the step by step process.
Step-07: Destroy Time Provisioners
- Discuss about this concept
- Destroy Time Provisioners
- Inside a provisioner when you add this statement
when = destroy
it will provision this during the resource destroy time
resource "azurerm_linux_virtual_machine" "mylinuxvm" {
# ...
provisioner "local-exec" {
when = destroy
command = "echo 'Destroy-time provisioner'"
}
}