Provision EC2 β upload script β execute script automatically
π Project Structure
terraform-provisioners-lab/
β
βββ main.tf
βββ variables.tf
βββ outputs.tf
βββ providers.tf
βββ terraform.tfvars
β
βββ scripts/
β βββ install_nginx.sh
β
βββ ssh/
βββ id_rsa.pub # your public key
1οΈβ£ providers.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
}
2οΈβ£ variables.tf
variable "aws_region" {
description = "AWS region"
type = string
}
variable "instance_type" {
description = "EC2 instance type"
type = string
}
variable "key_name" {
description = "SSH key name"
type = string
}
variable "public_key_path" {
description = "Path to public key"
type = string
}
variable "private_key_path" {
description = "Path to private key"
type = string
}
variable "ami_owner" {
description = "AMI owner"
type = string
default = "amazon"
}
3οΈβ£ terraform.tfvars (NO hardcoding in code)
aws_region = "us-east-2"
instance_type = "t3.micro"
key_name = "terraform-provisioner-key"
public_key_path = "ssh/id_rsa.pub"
private_key_path = "ssh/id_rsa"
4οΈβ£ main.tf
πΉ Key Pair
resource "aws_key_pair" "this" {
key_name = var.key_name
public_key = file(var.public_key_path)
}
πΉ Get Latest AMI (NO hardcoding)
data "aws_ami" "amazon_linux" {
most_recent = true
owners = [var.ami_owner]
filter {
name = "name"
values = ["al2023-ami-*-x86_64"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
πΉ Security Group
resource "aws_security_group" "this" {
name = "terraform-provisioner-sg"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
πΉ EC2 + Provisioners
resource "aws_instance" "this" {
ami = data.aws_ami.amazon_linux.id
instance_type = var.instance_type
key_name = aws_key_pair.this.key_name
vpc_security_group_ids = [aws_security_group.this.id]
tags = {
Name = "provisioner-lab"
}
connection {
type = "ssh"
user = "ec2-user"
private_key = file(var.private_key_path)
host = self.public_ip
}
# π FILE provisioner
provisioner "file" {
source = "scripts/install_nginx.sh"
destination = "/home/ec2-user/install_nginx.sh"
}
# βοΈ REMOTE EXEC provisioner
provisioner "remote-exec" {
inline = [
"chmod +x /home/ec2-user/install_nginx.sh",
"sudo /home/ec2-user/install_nginx.sh"
]
}
}
5οΈβ£ outputs.tf
output "public_ip" {
value = aws_instance.this.public_ip
}
output "url" {
value = "http://${aws_instance.this.public_ip}"
}
6οΈβ£ scripts/install_nginx.sh
#!/bin/bash
sudo yum update -y
sudo yum install -y nginx
sudo systemctl start nginx
sudo systemctl enable nginx
echo "<h1>Provisioner Lab Success</h1>" | sudo tee /usr/share/nginx/html/index.html
π HOW TO RUN
terraform init
terraform plan
terraform apply
β VERIFY
Open browser:
http://<PUBLIC_IP>
You should see:
Provisioner Lab Success
β οΈ IMPORTANT (INTERVIEW GOLD)
β Why provisioners are NOT recommended?
- Not idempotent
- Hard to debug
- Break Terraform model
β Better alternatives:
- user_data
- Packer (AMI baking)
- Ansible
π¬ Interview Answer (SHORT)
βProvisioners in Terraform are used to execute scripts or commands on local or remote machines during resource creation. However, they are considered a last resort because they are not idempotent and can break declarative infrastructure principles. Preferred alternatives include user_data, configuration management tools, or pre-baked images.β
Top comments (0)