Introduction
Sensitive values in HCP Terraform workspaces (such as API tokens, cloud credentials, or internal platform variables) are protected from display in Terraform UI outputs, plans, and state when marked as sensitive.
However, these protections do not apply to arbitrary shell commands executed during a run. If a Terraform configuration includes a provisioner (for example, local-exec) that prints environment variables or secrets, those values may be written to stdout and captured in run logs or external CI/CD systems.
If a Terraform configuration executes shell commands (for example, via local-exec) that print environment variables, sensitive values may be written to:
HCP Terraform run logs
Self-hosted agent logs
External CI/CD systems
This article outlines two secure approaches depending on your objective:
Debug safely while redacting sensitive values
Completely prevent such commands from execution
Expected Outcome
After completing this guide:
Terraform runs that attempt to use provisioner, sensitive commands , providers will be blocked.
Sensitive environment variables (including internal tokens such as
ATLAS_TOKENandTFC_AGENT_MAGIC_COOKIE) cannot be exposed through shell output if blocked.Security controls are enforced consistently across workspaces.
Why This Happens (Expected Behaviour)
The Sensitive flag in Terraform:
Masks values in plan/apply output generated by Terraform
Prevents values from appearing in state or UI output
However:
-
Terraform does not inspect or redact stdout/stderr produced by:
local-execremote-execUser scripts
Agents execute commands with access to the full runtime environment
Any command such as
env,printenv, orsetwill expose all environment variables
Use Case 1: Secure Debugging with Redacted Output
Scenario
You need to debug environment-related issues on an HCP Terraform self-hosted/remote agent but want to avoid exposing secrets in logs.
Recommended Approach
Instead of printing the full environment, explicitly redact sensitive patterns like below:
resource "null_resource" "environment_diagnostic" {
triggers = {
always_run = timestamp()
}
provisioner "local-exec" {
interpreter = ["/bin/bash", "-c"]
command = <<EOT
echo "--- START ENV DUMP ---"
env | sort | sed -E '
s/^(ATLAS_TOKEN|TFC_AGENT_MAGIC_COOKIE)=.*/\1=[REDACTED]/;
s/^(TF_VAR_.*)=.*/\1=[REDACTED]/;
s/^((AWS|AZURE|GOOGLE|ARM|GCP)_.*)=.*/\1=[REDACTED]/;
s/^(.*_TOKEN)=.*/\1=[REDACTED]/;
'
echo "--- END ENV DUMP ---"
EOT
}
}Outcome
Environment variables are visible for troubleshooting
Debugging is possible with reduced risk
-
Sensitive values are masked before being written to logs like below shared in screenshot.
Important Notes
Redaction is pattern-based and must be maintained carefully
This method reduces risk but does not eliminate it entirely
Use only in controlled debugging scenarios
Use Case 2: Completely Prevent Provisioners/ sensitive commands from execution (Recommended for Production)
Scenario
Your organization wants to prevent any possibility of credential exfiltration through provisioners or shell commands by completely blocking it in early stages before the run.
Recommended Approach
Use Sentinel/OPA policy enforcement to block the provisioners, providers or the commands as per your requirements across all workspaces or agent pools.
Please refer the GitHub repo that demonstrates how Sentinel policies can be applied at a more granular level : https://github.com/hashicorp/terraform-sentinel-policies/tree/main/cloud-agnostic
Example Sentinel policy to Block Provisioners :
import "tfconfig/v2" as tfconfig
# 1. Collect ALL resources from ALL modules (Root + Children)
all_resources = tfconfig.resources
# 2. Filter for any resource using a forbidden provisioner
violating_resources = filter all_resources as address, r {
any (r.provisioners else []) as p {
p.type == "local-exec" or p.type == "remote-exec"
}
}
# 3. Print details if a violation is found so you can see it in the logs
for violating_resources as address, r {
print("CRITICAL SECURITY VIOLATION: Resource '" + address + "' contains a forbidden provisioner (local-exec/remote-exec).")
print("Action: Remove the provisioner to proceed with the run.")
}
# 4. Final Rule
main = rule {
length(violating_resources) is 0
}Set the policy enforcement level to Hard Mandatory at the organization(globally) or projects/workspace level. Navigate to Organization Settings → Policy Sets -> Create a new Policy Set --> Attach it to:
All workspaces/projects (recommended), or
Specific workspaces/projects
Outcome
Runs containing provisioners fail during policy checks
The apply phase never starts
Commands cannot execute on HCP Terraform agents
Environment variables cannot be exfiltrated through shell output
This is the most secure and recommended production approach. Please refer the below screenshot for reference.
Additional Information
Managing Sensitive Data
https://developer.hashicorp.com/terraform/language/manage-sensitive-dataPolicy Enforcement (Sentinel)
https://developer.hashicorp.com/terraform/cloud-docs/policy-enforcementHCP Terraform Agents Overview
https://developer.hashicorp.com/terraform/cloud-docs/agents