Terraform Provisioners #
什么是 Provisioner? #
Provisioner 是在资源创建或销毁时执行操作的机制。它允许你在资源创建后进行额外的配置,如在 EC2 实例上安装软件、配置服务等。
text
┌─────────────────────────────────────────────────────────────┐
│ Provisioner 类型 │
├─────────────────────────────────────────────────────────────┤
│ │
│ local-exec 在运行 Terraform 的机器上执行命令 │
│ remote-exec 在远程资源上执行命令 │
│ │
│ 执行时机: │
│ - creation 资源创建后执行 │
│ - destruction 资源销毁前执行 │
│ │
└─────────────────────────────────────────────────────────────┘
local-exec #
基本语法 #
hcl
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
provisioner "local-exec" {
command = "echo ${self.public_ip} > instance_ip.txt"
}
}
环境变量 #
hcl
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
provisioner "local-exec" {
command = "echo $INSTANCE_IP"
environment = {
INSTANCE_IP = self.public_ip
}
}
}
执行脚本 #
hcl
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
provisioner "local-exec" {
command = "${path.module}/scripts/configure.sh"
environment = {
PUBLIC_IP = self.public_ip
REGION = var.region
}
}
}
调用 AWS CLI #
hcl
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
provisioner "local-exec" {
command = "aws s3 cp s3://my-bucket/config.json /tmp/config.json"
}
}
销毁时执行 #
hcl
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
provisioner "local-exec" {
when = destroy
command = "echo 'Instance ${self.id} is being destroyed'"
}
}
remote-exec #
基本语法 #
hcl
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
connection {
type = "ssh"
user = "ec2-user"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
provisioner "remote-exec" {
inline = [
"sudo yum update -y",
"sudo yum install -y httpd",
"sudo systemctl start httpd"
]
}
}
使用脚本 #
hcl
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
connection {
type = "ssh"
user = "ec2-user"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
provisioner "remote-exec" {
script = "${path.module}/scripts/setup.sh"
}
}
上传脚本并执行 #
hcl
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
connection {
type = "ssh"
user = "ec2-user"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
provisioner "file" {
source = "${path.module}/scripts/setup.sh"
destination = "/tmp/setup.sh"
}
provisioner "remote-exec" {
inline = [
"chmod +x /tmp/setup.sh",
"sudo /tmp/setup.sh"
]
}
}
Connection 配置 #
SSH 连接 #
hcl
connection {
type = "ssh"
user = "ec2-user"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
port = 22
timeout = "5m"
}
使用密码 #
hcl
connection {
type = "ssh"
user = "admin"
password = var.ssh_password
host = self.public_ip
}
WinRM 连接 #
hcl
connection {
type = "winrm"
user = "Administrator"
password = var.admin_password
host = self.public_ip
port = 5985
https = false
timeout = "10m"
}
使用跳板机 #
hcl
connection {
type = "ssh"
user = "ec2-user"
private_key = file("~/.ssh/id_rsa")
host = self.private_ip
bastion_host = var.bastion_host
bastion_user = "ec2-user"
bastion_private_key = file("~/.ssh/id_rsa")
}
在 Provisioner 中定义连接 #
hcl
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
provisioner "remote-exec" {
connection {
type = "ssh"
user = "ec2-user"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
inline = [
"echo 'Hello, World!'"
]
}
}
file Provisioner #
上传文件 #
hcl
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
connection {
type = "ssh"
user = "ec2-user"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
provisioner "file" {
source = "${path.module}/config/app.conf"
destination = "/tmp/app.conf"
}
}
上传目录 #
hcl
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
connection {
type = "ssh"
user = "ec2-user"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
provisioner "file" {
source = "${path.module}/config/"
destination = "/tmp/config/"
}
}
上传内容 #
hcl
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
connection {
type = "ssh"
user = "ec2-user"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
provisioner "file" {
content = "Hello, ${var.environment}!"
destination = "/tmp/hello.txt"
}
}
执行时机 #
创建时执行 #
hcl
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
provisioner "local-exec" {
when = create
command = "echo 'Instance created'"
}
}
销毁时执行 #
hcl
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
provisioner "local-exec" {
when = destroy
command = "echo 'Instance ${self.id} destroyed'"
}
}
失败处理 #
on_failure #
hcl
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
provisioner "local-exec" {
command = "exit 1"
on_failure = continue
}
}
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
provisioner "local-exec" {
command = "exit 1"
on_failure = fail
}
}
最佳实践 #
1. 优先使用 user_data #
hcl
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
user_data = <<-EOF
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
EOF
}
2. 使用配置管理工具 #
hcl
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
user_data = templatefile("${path.module}/user_data.sh.tpl", {
environment = var.environment
})
}
3. 使用 Packer 构建镜像 #
text
┌─────────────────────────────────────────────────────────────┐
│ 推荐架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. Packer 构建预配置镜像 │
│ - 安装软件 │
│ - 配置服务 │
│ - 安全加固 │
│ │
│ 2. Terraform 部署基础设施 │
│ - 创建资源 │
│ - 使用预配置镜像 │
│ - 最小化 provisioner 使用 │
│ │
│ 3. 配置管理工具 │
│ - Ansible │
│ - Chef │
│ - Puppet │
│ │
└─────────────────────────────────────────────────────────────┘
4. 仅在必要时使用 #
text
┌─────────────────────────────────────────────────────────────┐
│ Provisioner 使用场景 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 适合使用: │
│ ✅ 记录资源信息到本地文件 │
│ ✅ 调用外部 API │
│ ✅ 触发 CI/CD 流水线 │
│ ✅ 临时测试环境 │
│ │
│ 不推荐使用: │
│ ❌ 安装软件包 │
│ ❌ 配置服务 │
│ ❌ 部署应用 │
│ ❌ 生产环境配置 │
│ │
│ 替代方案: │
│ - user_data │
│ - Packer │
│ - Ansible/Chef/Puppet │
│ - 容器化 │
│ │
└─────────────────────────────────────────────────────────────┘
完整示例 #
记录实例信息 #
hcl
resource "aws_instance" "web" {
count = var.instance_count
ami = data.aws_ami.amazon_linux.id
instance_type = var.instance_type
subnet_id = var.subnet_id
tags = {
Name = "${var.project_name}-web-${count.index + 1}"
}
provisioner "local-exec" {
command = <<-EOT
echo "${self.public_ip}" >> ${path.module}/instance_ips.txt
EOT
}
provisioner "local-exec" {
when = destroy
command = <<-EOT
sed -i "/${self.public_ip}/d" ${path.module}/instance_ips.txt
EOT
}
}
触发配置更新 #
hcl
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
provisioner "local-exec" {
command = <<-EOT
curl -X POST ${var.webhook_url} \
-H "Content-Type: application/json" \
-d '{"instance_id": "${self.id}", "public_ip": "${self.public_ip}"}'
EOT
}
}
下一步 #
掌握了 Provisioners 后,接下来学习 工作空间,了解如何管理多个环境!
最后更新:2026-03-29