Skip to content
DevOps AI ToolKit
Newsletter
All prompts
AI for Terraform Difficulty: Advanced ClaudeChatGPTCursor

Terraform External & HTTP Data Source Security Review Prompt

Review Terraform external, http, and local-exec data sources for injection, secret leakage, idempotency, and supply-chain risk.

Target user
Platform and security engineers auditing IaC escape hatches
Difficulty
Advanced
Tools
Claude, ChatGPT, Cursor

The prompt

You are a senior Terraform engineer and security reviewer who treats every `external`, `http`, and provisioner escape hatch as untrusted until proven safe.

I will provide:
- HCL using `data "external"`, `data "http"`, `local-exec`/`remote-exec` provisioners, or the `template`/`archive` external calls
- Where it runs (local, CI runner, TFC agent) and what identity it runs as
- What the data feeds into (resource args, IAM policy, user_data, etc.)

Your job — review for:

1. **Command / argument injection**:
   - `data "external"` programs built from interpolated variables that could inject flags or shell.
   - `local-exec` using `command` with unquoted interpolations instead of `environment {}` blocks.
   - Any value flowing from a variable/data source into a shell string.
2. **Secret leakage**:
   - Secrets passed as CLI args (visible in `ps`, logs, plan output) vs env/stdin.
   - `data "external"` results stored in **plaintext in state** — flag any secret returned this way.
   - `sensitive = true` missing on outputs that carry the result.
3. **Supply-chain / trust**:
   - `data "http"` to a URL without TLS pinning, checksum, or auth — flag as an untrusted fetch that runs every plan.
   - External programs pulled from `$PATH` on the runner rather than a pinned, checked-in script.
4. **Idempotency & plan-time side effects**:
   - `data "external"` and `data "http"` run during **plan** — flag anything with side effects (writes, mutations, cost).
   - Non-deterministic output causing perpetual diffs.
5. **Failure & error handling**:
   - External program contract: must emit valid JSON of strings on stdout, exit non-zero with a message on stderr.
   - `http` responses not checking `status_code` before use.
6. **The better-native question**:
   - Is there a first-class provider/resource that removes the escape hatch entirely? Recommend it.

For each finding give: severity, the exact risky line, an exploit/failure sketch, and a concrete safer rewrite.

Mark DESTRUCTIVE: any interpolation reaching a shell, any secret placed on a command line or returned into state, and any plan-time fetch/exec with side effects.

---

HCL: [PASTE]
Runs as: [identity / runner]
Feeds into: [DESCRIBE]

Run this prompt with AI

Test it, get an AI-improved version, or compare models — live in the Prompt Workspace. No copy-paste.

Why this prompt works

external, http, and local-exec are the escape hatches teams reach for when a provider is missing a feature — and they are where injection, secret leakage, and plan-time side effects hide. This prompt reviews each hatch the way a security engineer would, and pushes back toward native resources.

How to use it

  1. Find every escape hatch in the module.
  2. Trace the data flow from variable to shell/URL to state.
  3. Rewrite to env/stdin, pinned scripts, and checksummed fetches.
  4. Prefer a native resource wherever one exists.

Useful commands

# Find the escape hatches
grep -rEn 'data "external"|data "http"|local-exec|remote-exec' .

# Confirm whether a returned value landed in state as plaintext
terraform show -json | jq -r '.values.root_module.resources[]
  | select(.type == "external") | .values.result'

Injection: command built from interpolation

# RISKY: variable flows into a shell string
resource "null_resource" "bad" {
  provisioner "local-exec" {
    command = "aws s3 cp s3://bucket/${var.key} ./out"   # key can inject flags/shell
  }
}

# SAFER: pass values via environment, keep the command static
resource "null_resource" "good" {
  provisioner "local-exec" {
    command     = "./scripts/fetch.sh"
    environment = { OBJECT_KEY = var.key }   # no interpolation in the command
  }
}

Secrets: never on the command line or into state

# RISKY: secret on argv (leaks to ps, logs) and result lands in state
data "external" "token" {
  program = ["bash", "-c", "get-token --password ${var.password}"]
}

# SAFER: secret via env/stdin; if you must surface it, mark it sensitive
data "external" "token" {
  program = ["${path.module}/scripts/get-token.sh"]   # reads secret from env
}

output "token" {
  value     = data.external.token.result["token"]
  sensitive = true
}

HTTP data source: check status and trust

data "http" "allowlist" {
  url = "https://config.internal.acme.dev/allowlist.json"
  request_headers = { Accept = "application/json" }
}

locals {
  # Fail loudly instead of feeding a 500 body into config
  allowlist = data.http.allowlist.status_code == 200 ? jsondecode(data.http.allowlist.response_body) : null
}

check "allowlist_reachable" {
  assert {
    condition     = data.http.allowlist.status_code == 200
    error_message = "Allowlist endpoint returned ${data.http.allowlist.status_code}."
  }
}

Review severity guide

Finding                                            Severity
------------------------------------------------   --------
Secret on argv / returned into state               Critical
Variable interpolated into a shell command         High
Plan-time http/external with side effects          High
http fetch without status/TLS/checksum check       Medium
External program from $PATH (not pinned)           Medium
Non-idempotent output -> perpetual diff            Low/Medium

Common findings this catches

  • Secrets in command strings surfacing in CI logs and plan output.
  • data "external" returning credentials stored plaintext in state.
  • data "http" to an unauthenticated URL re-fetched every plan.
  • Provisioners that could be a native resource (use the provider instead).
  • External programs violating the stdin/stdout JSON contract, failing opaquely.

When to escalate

  • A secret confirmed written to state → rotate it and scrub state history.
  • An escape hatch running as an over-privileged runner identity → tighten IAM.
  • A fetch dependency with no integrity control → move it behind a checked-in, pinned artifact.

Related prompts

More Terraform prompts & error guides

Browse every Terraform prompt and troubleshooting guide in one place.

Free download · 368-page PDF

Reading prompts? Get all 500 in one free PDF

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.