Terraform Provisioners are used to execute scripts or commands on the local machine or on the created resources after they have been created.
Types of Terraform Provisioners
- file
- local-exec
- remote-exec
1. file
Provisioner
The file
provisioner is used to copy files or directories from the local machine to a remote resource, such as a virtual machine (VM), after it has been created.
Example:
resource "aws_instance" "example" {
ami = "ami-0abcdef1234567890"
instance_type = "t2.micro"
provisioner "file" {
source = "local_script.sh"
destination = "/tmp/remote_script.sh"
}
connection {
type = "ssh"
user = "ec2-user"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
}
2. local-exec
Provisioner
The local-exec
provisioner runs commands or scripts on the local machine where Terraform is being run, after the resource has been created.
Example:
resource "aws_instance" "example" {
ami = "ami-0abcdef1234567890"
instance_type = "t2.micro"
provisioner "local-exec" {
command = "echo ${aws_instance.example.public_ip} > ip_address.txt"
}
}
3. remote-exec
Provisioner
The remote-exec
provisioner runs commands or scripts on the remote resource (e.g., a VM) after it has been created. This requires SSH or WinRM connection information.
Example:
resource "aws_instance" "example" {
ami = "ami-0abcdef1234567890"
instance_type = "t2.micro"
provisioner "remote-exec" {
inline = [
"sudo apt-get update",
"sudo apt-get install -y nginx"
]
}
connection {
type = "ssh"
user = "ec2-user"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
}
Explanation
- file:
- Purpose: Copy files or directories from the local machine to the remote resource.
- When to Use: When you need to transfer configuration files, scripts, or other assets to a VM after it’s been created.
- local-exec:
- Purpose: Run commands or scripts on the local machine after the resource has been created.
- When to Use: When you need to perform actions on the local machine, such as logging information, sending notifications, or triggering other local processes.
- remote-exec:
- Purpose: Run commands or scripts on the remote resource (e.g., VM) after it has been created.
- When to Use: When you need to configure or set up software on the VM, such as installing packages, configuring services, or running custom scripts.
Complete Example Using All Three Provisioners
main.tf:
provider "aws" {
region = "us-west-2"
}
resource "aws_instance" "example" {
ami = "ami-0abcdef1234567890"
instance_type = "t2.micro"
# Copy a file from local machine to VM
provisioner "file" {
source = "local_script.sh"
destination = "/tmp/remote_script.sh"
}
# Run a local command after the instance is created
provisioner "local-exec" {
command = "echo ${aws_instance.example.public_ip} > ip_address.txt"
}
# Run remote commands on the VM
provisioner "remote-exec" {
inline = [
"sudo apt-get update",
"sudo apt-get install -y nginx"
]
}
connection {
type = "ssh"
user = "ec2-user"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
}
Summary
- file Provisioner: Copies files from the local machine to the VM.
- local-exec Provisioner: Runs commands or scripts on the local machine after the resource creation.
- remote-exec Provisioner: Runs commands or scripts on the remote VM after it has been created.
What is Connection Block in Terraform?
The connection
block specifies the connection details that Terraform uses to communicate with the remote resource. It includes information such as the connection type (SSH or WinRM), user credentials, and host details.
Example of a Connection Block
Here’s a breakdown of a typical connection
block for an SSH connection:
Example of a Connection Block
Here’s a breakdown of a typical connection
block for an SSH connection:
resource "aws_instance" "example" {
ami = "ami-0abcdef1234567890"
instance_type = "t2.micro"
# Provisioners would go here...
connection {
type = "ssh" # The connection type (SSH in this case)
user = "ec2-user" # The username to use for the connection
private_key = file("~/.ssh/id_rsa") # The path to the private key for authentication
host = self.public_ip # The public IP address of the instance
}
}
Explanation of Connection Block Fields
- type: Specifies the type of connection. Common values are
ssh
for Secure Shell connections andwinrm
for Windows Remote Management connections. - user: The username to use for the connection. For AWS EC2 instances, this is often
ec2-user
orubuntu
, depending on the AMI. - private_key: The path to the private key file used for SSH authentication. This is usually specified using the
file()
function to read the private key from a file on the local machine. - host: The hostname or IP address of the remote resource. Typically, this is set to the public IP address of the instance (
self.public_ip
).
Example Using All Three Provisioners with Connection Block
Let’s revisit the complete example using all three provisioners with a connection block:
main.tf:
provider "aws" {
region = "us-west-2"
}
resource "aws_instance" "example" {
ami = "ami-0abcdef1234567890"
instance_type = "t2.micro"
# Copy a file from local machine to VM
provisioner "file" {
source = "local_script.sh"
destination = "/tmp/remote_script.sh"
}
# Run a local command after the instance is created
provisioner "local-exec" {
command = "echo ${aws_instance.example.public_ip} > ip_address.txt"
}
# Run remote commands on the VM
provisioner "remote-exec" {
inline = [
"sudo apt-get update",
"sudo apt-get install -y nginx"
]
}
connection {
type = "ssh"
user = "ec2-user"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
}
Summary
- Connection Block: Specifies how Terraform connects to the remote resource.
- type: The connection type (e.g.,
ssh
orwinrm
). - user: The username to use for the connection.
- private_key: The private key for SSH authentication.
- host: The hostname or IP address of the remote resource.
- type: The connection type (e.g.,
List of connection types in Terraform remote-exec provisioner?
SSH (Secure Shell):
- Type:
"ssh"
- Description: Used for connecting to Unix-based operating systems, including Linux and macOS.
- Commonly Used With: Linux and Unix-based cloud instances.
- Example:
connection {
type = "ssh"
user = "your_username"
private_key = file("~/.ssh/your_private_key.pem")
host = aws_instance.example.public_ip
}
WinRM (Windows Remote Management):
- Type:
"winrm"
- Description: Used for connecting to Windows-based operating systems.
- Commonly Used With: Windows cloud instances.
- Example:
connection {
type = "winrm"
host = aws_instance.example.private_ip
user = "Administrator"
password = "your_password"
}
To establish a WinRM connection over HTTPS with SSL and ignore certificate validation, you can modify the connection block as follows:
connection {
type = "winrm"
host = aws_instance.example.private_ip
user = "Administrator"
password = "your_password"
https = true # Enable HTTPS
insecure = true # Ignore SSL certificate validation (for testing purposes, not recommended in production)
port = 5986 # Use the default HTTPS port for WinRM
cacert = "/path/to/ca.crt" # Optional path to a CA certificate file (if required)
cert = "/path/to/client.crt" # Optional path to a client certificate file (if required)
key = "/path/to/client.key" # Optional path to the client certificate's private key file (if required)
timeout = "5m" # Set a timeout for the WinRM connection
max_retries = 3 # Maximum number of connection retries
}
Explanation of the options:
https
: Set totrue
to enable HTTPS for the WinRM connection.insecure
: Set totrue
to ignore SSL certificate validation. This option is typically used for testing and debugging but is not recommended for production due to security concerns.port
: Use5986
as the default port for HTTPS WinRM connections.cacert
: Optional path to a CA certificate file. Use this if your WinRM server’s certificate is signed by a custom CA.cert
: Optional path to a client certificate file. Use this if client certificate authentication is required.key
: Optional path to the private key file corresponding to the client certificate.timeout
: Set a timeout for the WinRM connection (e.g.,"5m"
for 5 minutes).max_retries
: Maximum number of connection retries in case of failures.
Terrafrom – Example Code for remote-exec, local-exec & file provisioner
resource "aws_instance" "first-ec2" {
ami = "ami-03d5c68bab01f3496" # us-west-2
instance_type = "t2.micro"
key_name = "rajesh-last"
tags = {
Name = "RajeshKumar"
}
connection {
type = "ssh"
user = "ubuntu"
private_key = file("rajesh-last.pem")
#host = aws_instance.web.public_ip
host = self.public_ip
}
provisioner "local-exec" {
command = "touch devopsschool-local"
}
provisioner "remote-exec" {
inline = [
"sudo apt-get update",
"sudo apt-get install apache2 -y",
"sudo systemctl start apache2",
]
}
provisioner "file" {
source = "terraform.tfstate.backup"
destination = "/tmp/terraform.tfstate.backup"
}
}
Live Output details for above example:
Terraform init
Terraform Plan
Terraform validate
Terraform apply
Terraform apply getting failed due to below error
aws_instance.first-ec2 (remote-exec): SSH Agent: false
aws_instance.first-ec2 (remote-exec): Checking Host Key: false
aws_instance.first-ec2 (remote-exec): Target Platform: unix
aws_instance.first-ec2: Still creating... [5m17s elapsed]
╷
│ Error: remote-exec provisioner error
│
│ with aws_instance.first-ec2,
│ on provisioner.tf line 21, in resource "aws_instance" "first-ec2":
│ 21: provisioner "remote-exec" {
│
│ timeout - last error: dial tcp 54.144.229.232:22: i/o timeout
The error occurring due to inbound and outbound rules
Check Security Group Rules – Verify that the security group rules are correctly applied and allow inbound SSH (port 22) traffic.
- Edit Inbound Rules:
- Click on the Inbound Rules tab.
- Click Edit inbound rules.
- Add a rule to allow inbound SSH traffic:
- Rule #: Choose an appropriate rule number (e.g., 100).
- Type: SSH.
- Protocol: TCP (should be automatically selected with SSH type).
- Port Range: 22.
- Source: 0.0.0.0/0 (or restrict it to your IP address for security).
- Allow/Deny: Allow.
- Click Save changes.
- Edit Outbound Rules:
- Click on the Outbound Rules tab.
- Click Edit outbound rules.
- Add a rule to allow outbound SSH traffic:
- Rule #: Choose an appropriate rule number (e.g., 100).
- Type: SSH.
- Protocol: TCP (should be automatically selected with SSH type).
- Port Range: 22.
- Destination: 0.0.0.0/0 (or restrict it to your IP address for security).
- Allow/Deny: Allow.
- Click Save changes.
Example Screenshots for Reference
Inbound Rules Example
Rule # | Type | Protocol | Port Range | Source | Allow/Deny |
---|---|---|---|---|---|
100 | SSH | TCP | 22 | 0.0.0.0/0 | Allow |
Outbound Rules Example
Rule # | Type | Protocol | Port Range | Destination | Allow/Deny |
---|---|---|---|---|---|
100 | SSH | TCP | 22 | 0.0.0.0/0 | Allow |
check connection from CMD if able to connect the VM
ssh -i "C:/Users/gufra/Downloads/.ssh/Terraform-Demo.pem" ubuntu@54.175.186.33
Run again Terraform apply
completed successfully
we can see folder created in local and also copy files in remote machine
in Local
In remote:
Also apache installed in remote machine