You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Terraform data external在plan和apply阶段均执行脚本

问题分析与解决方案

核心问题拆解

你遇到的问题本质是Terraform执行阶段的特性冲突:

  • data.external仅在plan阶段执行,生成plan文件后,apply tf.plan时会复用plan中固化的data结果,不会重新运行脚本
  • null_resource的local-exec仅在资源变更时触发,且执行时机晚于Provider初始化,无法满足postgresql/kubernetes等Provider操作前创建隧道的需求
  • Provider依赖模块输出失效,是因为Terraform初始化Provider时,模块输出还未计算,依赖关系无法传递到初始化阶段

可行解决方案:用terraform_data实现双阶段触发

Terraform 1.3+引入的terraform_data资源,支持在plan和apply阶段都执行脚本,配合triggers_replace和Provider级depends_on,可以完美解决你的需求。

1. 重构隧道脚本模块(my_script/main.tf)

variable "environment" {
  description = "运行环境标识"
  type        = string
}

# Plan阶段触发隧道创建
terraform_data "plan_tunnel" {
  triggers_replace = {
    # 用时间戳确保每次plan都触发脚本
    plan_timestamp = timestamp()
  }

  provisioner "local-exec" {
    command = "bash ${path.module}/my_script.sh ${var.environment}"
  }
}

# Apply阶段触发隧道创建(包括使用plan文件的场景)
terraform_data "apply_tunnel" {
  triggers_replace = {
    # 依赖plan阶段的触发值,确保执行顺序
    apply_timestamp = timestamp()
    plan_trigger    = terraform_data.plan_tunnel.triggers_replace.plan_timestamp
  }

  provisioner "local-exec" {
    command = "bash ${path.module}/my_script.sh ${var.environment}"
  }
}

output "tunnel_ready" {
  value = terraform_data.apply_tunnel.id
}

2. 完善隧道脚本(my_script/my_script.sh)

#!/bin/bash
environment="$1"

# 替换为你的实际隧道创建逻辑,比如SSM端口转发
echo "创建${environment}环境的隧道..."
# 示例:aws ssm start-session --target i-xxxxxx --document-name AWS-StartPortForwardingSession --parameters '{"portNumber": ["5432"], "localPortNumber": ["5432"]}' &

# 记录隧道PID,方便后续清理
echo $$ > /tmp/tunnel_${environment}.pid

# 返回JSON格式结果(可选,给Terraform输出用)
echo "{\"environment\": \"$environment\", \"tunnel_pid\": $$}"

3. 主模块中绑定Provider依赖

让postgresql/kubernetes等Provider明确依赖隧道模块的输出,确保Provider初始化前隧道已就绪:

module "ssm_tunnel" {
  source      = "../my_script"
  environment = "production"
}

# PostgreSQL Provider依赖隧道
provider "postgresql" {
  host            = "localhost"
  port            = 5432
  username        = "admin"
  password        = var.db_password
  database        = "app_db"
  # 强制Provider在隧道创建后再初始化
  depends_on      = [module.ssm_tunnel.tunnel_ready]
}

# Kubernetes Provider同理
provider "kubernetes" {
  config_path = "~/.kube/config"
  depends_on  = [module.ssm_tunnel.tunnel_ready]
}

关键说明

  • triggers_replace:通过时间戳或动态值,强制每次plan/apply都重新执行terraform_data的脚本
  • Provider级depends_on:Terraform 0.13+支持该特性,确保Provider初始化前隧道脚本已执行完成
  • 隧道生命周期:脚本中记录PID后,可在terraform_data中添加destroy-time的local-exec,实现Terraform销毁时自动关闭隧道

验证效果

  1. 执行terraform plan -out tf.plan:plan_tunnel会触发脚本创建隧道
  2. 执行terraform apply tf.plan:apply_tunnel会再次触发脚本,且Provider依赖隧道输出,确保操作前隧道就绪
  3. 直接执行terraform apply:两个阶段的脚本都会正常触发

内容的提问来源于stack exchange,提问作者Dustin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 01:37:07