9 – Terraform Provisioners in Azure : Local-Exec vs Remote-Exec vs File Provisioner (Hands-On Guide)

When I started learning Terraform, I wondered:

Terraform can create infrastructure… but how do we run scripts, install software, or copy files after a VM is created?

That is where Terraform Provisioners come into the picture.

In this hands-on mini project I implemented:

  • Local-Exec Provisioner
  • Remote-Exec Provisioner
  • File Provisioner

and understood their real purpose, limitations, and practical usage.

Table of Contents

  1. Project Goal
  2. Architecture Overview
  3. Step 1 – Create Core Azure Infrastructure
  4. Step 2 – Create VM and Verify SSH
  5. Step 3 – Local-Exec Provisioner
  6. Step 4 – Remote-Exec Provisioner
  7. Debug Steps and Errors Faced
  8. Step 5 – File Provisioner
  9. Understanding Provisioners
  10. Important Reality
  11. Final Learning Outcome

Project Goal

Build an Azure Linux VM using Terraform and:

  1. Run a command on my local PC during deployment
  2. Install Nginx inside the VM automatically
  3. Copy a configuration file from my laptop to the VM

Architecture Overview

The infrastructure consists of:

  • Resource Group
  • Virtual Network and Subnet
  • Network Security Group (SSH + HTTP)
  • Public IP
  • Network Interface
  • Linux Virtual Machine

Step 1 – Create Core Azure Infrastructure

Resource Group

resource "azurerm_resource_group" "rg" {
  name     = "rgminipro878933"
  location = "Central US"
}

Virtual Network & Subnet

resource "azurerm_virtual_network" "vnet" {
  name                = "vnetminipro7678678"
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
}

Network Security Group

Inbound rules were added to allow:

  • Port 22 → SSH
  • Port 80 → HTTP

Step 2 – Create VM and Verify SSH

Generate SSH Keys

ssh-keygen -t rsa -b 4096

Create Linux VM

The VM was created using azurerm_linux_virtual_machine with SSH key authentication.

Test Connection

ssh -i key1 azureuser@<public-ip>

✔ SSH login successful.


Step 3 – Local-Exec Provisioner

What Local-Exec Means

Local-exec runs a command on:

The machine where Terraform is executed
NOT inside the Azure VM.

Implementation

provisioner "local-exec" {
  command = "echo Deployment started at ${timestamp()} > deployment.log"
}

Result

A file deployment.log was created on my laptop — proof that the command executed locally.

Real-World Uses

  • Trigger Ansible after Terraform
  • Call REST API or webhook
  • Notify Slack/Email
  • Generate inventory files
  • Write audit logs

Step 4 – Remote-Exec Provisioner

Purpose

Run commands inside the VM after creation.

Goal

Install Nginx and deploy a simple webpage automatically.

Implementation

provisioner "remote-exec" {
  inline = [
    "while [ ! -f /var/lib/cloud/instance/boot-finished ]; do sleep 2; done",
    "sudo apt-get update -y",
    "sudo apt-get install -y nginx",
    "echo '<h1>Terraform Provisioner Demo Working!</h1>' | sudo tee /var/www/html/index.html",
    "sudo systemctl restart nginx"
  ]
}

Result

Opening:

👉 http://<public-ip>/

displayed the custom webpage ✔

Debug Lesson

Initially nginx was not installed because:

  • VM was not fully ready
  • apt was locked by cloud-init

Adding a wait for:

/var/lib/cloud/instance/boot-finished

fixed the issue.

Debug Steps and Errors Faced

While implementing this project, I faced several real-world issues. These are the exact steps that helped me troubleshoot.

SSH Key Permission Issue on Windows

Azure SSH login failed initially because Windows was treating the private key as insecure.

Fix: Restrict key permissions in PowerShell

icacls <key file path> /inheritance:r
icacls <key file path> /grant:r "$($env:USERNAME):(R)"
icacls <key file path> /remove "Authenticated Users" "BUILTIN\Users" "Everyone"

After this, SSH worked correctly:

ssh -i <key file path> azureuser@<public ip>

Important: The key must be stored on an NTFS formatted drive (not FAT/external USB) for permissions to work.


Web Page Not Loading After Remote-Exec

Even though Terraform apply was successful, the browser showed:

ERR_CONNECTION_REFUSED

Debug Steps Inside VM

  1. SSH into the VM
ssh -i key1 azureuser@<public-ip>
  1. Check if nginx is installed
which nginx
sudo systemctl status nginx
  1. Test locally inside VM
curl http://localhost

Root Cause

  • Remote-exec ran before the VM was fully ready
  • cloud-init was still configuring the system
  • apt was locked at the time of execution

Fix Implemented

Added wait for cloud-init before installing nginx:

while [ ! -f /var/lib/cloud/instance/boot-finished ]; do sleep 2; done

After this change, the webpage loaded correctly.


Lesson Learned

Terraform showing “Apply complete” does not always mean:

  • Software is installed
  • Services are running
  • VM is fully ready

Provisioners need proper waiting and validation logic.


Step 5 – File Provisioner

Purpose

Copy files from local machine → VM.

Implementation

provisioner "file" {
  source      = "configs/sample.conf"
  destination = "/home/azureuser/sample.conf"
}

Verification in VM

ls -l /home/azureuser
cat sample.conf

✔ File successfully transferred.


Understanding Provisioners

Local-Exec

  • Runs on local computer
  • Used for logs, notifications, triggers

Remote-Exec

  • Runs inside the VM
  • Installs software, configures OS

File Provisioner

  • Copies files to remote system

Important Reality

Terraform provisioners are:

  • ❌ Not guaranteed
  • ❌ Not idempotent
  • ❌ Not recommended for production

Better Alternatives

  • cloud-init
  • Custom VM images
  • Ansible
  • Azure VM Extensions

Final Learning Outcome

This mini project helped me understand:

  • How Terraform builds infrastructure
  • Difference between the 3 provisioners
  • Debugging real deployment issues
  • Basic Linux + Azure networking

It connected multiple skills:

Terraform + Azure + Linux + Automation

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

TechMilestoneHub

Build Skills, Unlock Milestones

This is a test – edited from front page