tfc-plan-json

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Terraform Cloud Plan JSON

Terraform Cloud 计划JSON

Download and analyze structured plan JSON output from Terraform Cloud runs for detailed resource change analysis.
从Terraform Cloud的运行任务中下载并分析结构化的计划JSON输出,以进行详细的资源变更分析。

Prerequisites

前提条件

bash
export TFE_TOKEN="your-api-token"        # User or team token with admin workspace access
export TFE_ADDRESS="app.terraform.io"    # Optional
bash
export TFE_TOKEN="your-api-token"        # 具有工作区管理员权限的用户或团队令牌
export TFE_ADDRESS="app.terraform.io"    # 可选

Core Commands

核心命令

Download Plan JSON

下载计划JSON

bash
#!/bin/bash
set -euo pipefail

TOKEN="${TFE_TOKEN:?TFE_TOKEN not set}"
BASE_URL="https://${TFE_ADDRESS:-app.terraform.io}/api/v2"
RUN_ID="${1:?Usage: $0 <run-id> [output-file]}"
OUTPUT="${2:-plan.json}"
bash
#!/bin/bash
set -euo pipefail

TOKEN="${TFE_TOKEN:?TFE_TOKEN未设置}"
BASE_URL="https://${TFE_ADDRESS:-app.terraform.io}/api/v2"
RUN_ID="${1:?使用方式: $0 <运行ID> [输出文件]}"
OUTPUT="${2:-plan.json}"

Download with redirect following (API returns 307)

跟随重定向下载(API返回307状态码)

curl -Lsf --header "Authorization: Bearer $TOKEN"
-o "$OUTPUT"
"$BASE_URL/runs/$RUN_ID/plan/json-output"
echo "Plan JSON saved to: $OUTPUT"
undefined
curl -Lsf --header "Authorization: Bearer $TOKEN"
-o "$OUTPUT"
"$BASE_URL/runs/$RUN_ID/plan/json-output"
echo "计划JSON已保存至: $OUTPUT"
undefined

Download via Plan ID

通过计划ID下载

bash
TOKEN="${TFE_TOKEN:?TFE_TOKEN not set}"
PLAN_ID="plan-xyz789"

curl -Lsf --header "Authorization: Bearer $TOKEN" \
  -o plan.json \
  "https://app.terraform.io/api/v2/plans/$PLAN_ID/json-output"
bash
TOKEN="${TFE_TOKEN:?TFE_TOKEN未设置}"
PLAN_ID="plan-xyz789"

curl -Lsf --header "Authorization: Bearer $TOKEN" \
  -o plan.json \
  "https://app.terraform.io/api/v2/plans/$PLAN_ID/json-output"

Analysis Commands

分析命令

Resource Change Summary

资源变更汇总

bash
curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq '{
    terraform_version: .terraform_version,
    format_version: .format_version,
    summary: {
      create: [.resource_changes[] | select(.change.actions | contains(["create"]))] | length,
      update: [.resource_changes[] | select(.change.actions | contains(["update"]))] | length,
      delete: [.resource_changes[] | select(.change.actions | contains(["delete"]))] | length,
      replace: [.resource_changes[] | select(.change.actions | contains(["delete", "create"]))] | length,
      read: [.resource_changes[] | select(.change.actions | contains(["read"]))] | length,
      no_op: [.resource_changes[] | select(.change.actions == ["no-op"])] | length
    }
  }'
bash
curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq '{
    terraform_version: .terraform_version,
    format_version: .format_version,
    summary: {
      create: [.resource_changes[] | select(.change.actions | contains(["create"]))] | length,
      update: [.resource_changes[] | select(.change.actions | contains(["update"]))] | length,
      delete: [.resource_changes[] | select(.change.actions | contains(["delete"]))] | length,
      replace: [.resource_changes[] | select(.change.actions | contains(["delete", "create"]))] | length,
      read: [.resource_changes[] | select(.change.actions | contains(["read"]))] | length,
      no_op: [.resource_changes[] | select(.change.actions == ["no-op"])] | length
    }
  }'

List Resources Being Created

列出待创建的资源

bash
curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq -r '.resource_changes[] | select(.change.actions | contains(["create"])) | .address'
bash
curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq -r '.resource_changes[] | select(.change.actions | contains(["create"])) | .address'

List Resources Being Destroyed

列出待销毁的资源

bash
curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq -r '.resource_changes[] | select(.change.actions | contains(["delete"])) | .address'
bash
curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq -r '.resource_changes[] | select(.change.actions | contains(["delete"])) | .address'

List Resources Being Updated

列出待更新的资源

bash
curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq -r '.resource_changes[] | select(.change.actions | contains(["update"])) | .address'
bash
curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq -r '.resource_changes[] | select(.change.actions | contains(["update"])) | .address'

Resources Being Replaced

待替换的资源

bash
curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq -r '.resource_changes[] | select(.change.actions | contains(["delete", "create"])) |
    "\(.address) (replace due to: \(.action_reason // "unknown"))"'
bash
curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq -r '.resource_changes[] | select(.change.actions | contains(["delete", "create"])) |
    "\(.address) (替换原因: \(.action_reason // "未知"))"'

Detailed Resource Changes

详细资源变更

bash
curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq '.resource_changes[] | select(.change.actions != ["no-op"]) | {
    address: .address,
    actions: .change.actions,
    before: .change.before,
    after: .change.after
  }'
bash
curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq '.resource_changes[] | select(.change.actions != ["no-op"]) | {
    address: .address,
    actions: .change.actions,
    before: .change.before,
    after: .change.after
  }'

Show What's Changing in a Specific Resource

查看特定资源的变更内容

bash
RESOURCE="aws_instance.web"

curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq --arg addr "$RESOURCE" '
    .resource_changes[] | select(.address == $addr) | {
      address: .address,
      actions: .change.actions,
      before: .change.before,
      after: .change.after,
      after_unknown: .change.after_unknown
    }'
bash
RESOURCE="aws_instance.web"

curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq --arg addr "$RESOURCE" '
    .resource_changes[] | select(.address == $addr) | {
      address: .address,
      actions: .change.actions,
      before: .change.before,
      after: .change.after,
      after_unknown: .change.after_unknown
    }'

Provider Versions Used

使用的提供程序版本

bash
curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq '.configuration.provider_config | to_entries | map({
    provider: .key,
    version: .value.version_constraint
  })'
bash
curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq '.configuration.provider_config | to_entries | map({
    provider: .key,
    version: .value.version_constraint
  })'

Output Values

输出值

bash
curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq '.output_changes | to_entries | map({
    name: .key,
    actions: .value.actions,
    sensitive: .value.after_sensitive
  })'
bash
curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq '.output_changes | to_entries | map({
    name: .key,
    actions: .value.actions,
    sensitive: .value.after_sensitive
  })'

Variables Used

使用的变量

bash
curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq '.variables | keys'
bash
curl -Lsf --header "Authorization: Bearer $TFE_TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output" | \
  jq '.variables | keys'

Complete Analysis Script

完整分析脚本

bash
#!/bin/bash
set -euo pipefail

TOKEN="${TFE_TOKEN:?TFE_TOKEN not set}"
RUN_ID="${1:?Usage: $0 <run-id>}"

PLAN=$(curl -Lsf --header "Authorization: Bearer $TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output")

echo "=== Plan Analysis for $RUN_ID ==="
echo ""
echo "Terraform Version: $(echo "$PLAN" | jq -r '.terraform_version')"
echo ""

echo "Resource Changes:"
echo "  Create:  $(echo "$PLAN" | jq '[.resource_changes[] | select(.change.actions | contains(["create"]))] | length')"
echo "  Update:  $(echo "$PLAN" | jq '[.resource_changes[] | select(.change.actions | contains(["update"]))] | length')"
echo "  Delete:  $(echo "$PLAN" | jq '[.resource_changes[] | select(.change.actions | contains(["delete"]))] | length')"
echo "  Replace: $(echo "$PLAN" | jq '[.resource_changes[] | select(.change.actions | contains(["delete", "create"]))] | length')"
echo ""

echo "Resources to Create:"
echo "$PLAN" | jq -r '.resource_changes[] | select(.change.actions | contains(["create"])) | "  - " + .address'

echo ""
echo "Resources to Destroy:"
echo "$PLAN" | jq -r '.resource_changes[] | select(.change.actions | contains(["delete"])) | "  - " + .address'

echo ""
echo "Resources to Update:"
echo "$PLAN" | jq -r '.resource_changes[] | select(.change.actions | contains(["update"])) | "  - " + .address'
bash
#!/bin/bash
set -euo pipefail

TOKEN="${TFE_TOKEN:?TFE_TOKEN未设置}"
RUN_ID="${1:?使用方式: $0 <运行ID>}"

PLAN=$(curl -Lsf --header "Authorization: Bearer $TOKEN" \
  "https://app.terraform.io/api/v2/runs/$RUN_ID/plan/json-output")

echo "=== 运行ID $RUN_ID 的计划分析 ==="
echo ""
echo "Terraform版本: $(echo "$PLAN" | jq -r '.terraform_version')"
echo ""

echo "资源变更统计:"
echo "  创建:  $(echo "$PLAN" | jq '[.resource_changes[] | select(.change.actions | contains(["create"]))] | length')"
echo "  更新:  $(echo "$PLAN" | jq '[.resource_changes[] | select(.change.actions | contains(["update"]))] | length')"
echo "  删除:  $(echo "$PLAN" | jq '[.resource_changes[] | select(.change.actions | contains(["delete"]))] | length')"
echo "  替换: $(echo "$PLAN" | jq '[.resource_changes[] | select(.change.actions | contains(["delete", "create"]))] | length')"
echo ""

echo "待创建的资源:"
echo "$PLAN" | jq -r '.resource_changes[] | select(.change.actions | contains(["create"])) | "  - " + .address'

echo ""
echo "待销毁的资源:"
echo "$PLAN" | jq -r '.resource_changes[] | select(.change.actions | contains(["delete"])) | "  - " + .address'

echo ""
echo "待更新的资源:"
echo "$PLAN" | jq -r '.resource_changes[] | select(.change.actions | contains(["update"])) | "  - " + .address'

Plan JSON Structure

计划JSON结构

The plan JSON output follows Terraform's JSON plan format:
json
{
  "format_version": "1.2",
  "terraform_version": "1.5.0",
  "planned_values": { ... },
  "resource_changes": [
    {
      "address": "aws_instance.web",
      "mode": "managed",
      "type": "aws_instance",
      "name": "web",
      "provider_name": "registry.terraform.io/hashicorp/aws",
      "change": {
        "actions": ["create"],
        "before": null,
        "after": { ... },
        "after_unknown": { ... },
        "before_sensitive": false,
        "after_sensitive": { ... }
      }
    }
  ],
  "output_changes": { ... },
  "configuration": { ... },
  "variables": { ... }
}
计划JSON输出遵循Terraform的JSON计划格式:
json
{
  "format_version": "1.2",
  "terraform_version": "1.5.0",
  "planned_values": { ... },
  "resource_changes": [
    {
      "address": "aws_instance.web",
      "mode": "managed",
      "type": "aws_instance",
      "name": "web",
      "provider_name": "registry.terraform.io/hashicorp/aws",
      "change": {
        "actions": ["create"],
        "before": null,
        "after": { ... },
        "after_unknown": { ... },
        "before_sensitive": false,
        "after_sensitive": { ... }
      }
    }
  ],
  "output_changes": { ... },
  "configuration": { ... },
  "variables": { ... }
}

Change Actions

变更操作

  • ["create"]
    - Resource will be created
  • ["delete"]
    - Resource will be destroyed
  • ["update"]
    - Resource will be updated in-place
  • ["delete", "create"]
    - Resource will be replaced
  • ["read"]
    - Data source will be read
  • ["no-op"]
    - No changes
  • ["create"]
    - 资源将被创建
  • ["delete"]
    - 资源将被销毁
  • ["update"]
    - 资源将被就地更新
  • ["delete", "create"]
    - 资源将被替换
  • ["read"]
    - 将读取数据源
  • ["no-op"]
    - 无变更

Important Notes

重要说明

  • Requires Terraform 0.12+ for JSON output support
  • Returns 204 No Content if plan hasn't completed yet
  • Follow redirects - API returns HTTP 307 to temporary download URL
  • Temporary URL - Download URL is valid for ~1 minute
  • Admin access required - Need admin permissions on the workspace
  • 需要Terraform 0.12+ 以支持JSON输出
  • 返回204 No Content 表示计划尚未完成
  • 跟随重定向 - API会返回HTTP 307重定向到临时下载链接
  • 临时链接 - 下载链接的有效期约为1分钟
  • 需要管理员权限 - 需拥有工作区的管理员权限

Error Handling

错误处理

204 No Content

204 No Content

Plan hasn't completed yet. Check run status first.
计划尚未完成,请先检查运行状态。

401 Unauthorized

401 Unauthorized

Token lacks admin workspace access or is invalid.
令牌无效或缺少工作区管理员权限。

404 Not Found

404 Not Found

Run doesn't exist or you don't have permission.
运行任务不存在或您无访问权限。

See Also

相关链接

  • tfc-run-logs
    : Get plan/apply logs (human-readable)
  • tfc-run-status
    : Quick status check for a run
  • tfc-list-runs
    : List recent runs in a workspace
  • tfc-run-logs
    : 获取计划/应用日志(人类可读格式)
  • tfc-run-status
    : 快速检查运行任务状态
  • tfc-list-runs
    : 列出工作区中的近期运行任务