PlanSignal
PlanSignal is a Visual Studio Code extension for reviewing a saved Terraform plan before a human decides whether to apply it. It highlights destructive operations and common security-sensitive changes, assigns an attention score, and provides recommended review actions.
PlanSignal never runs terraform apply and never changes infrastructure. It executes only terraform show -json <saved-plan> and reads bounded Terraform .tf source files inside the selected workspace when policy values are unknown.
Features
- Counts creates, updates, deletes, replacements, and reads.
- Flags destructive changes and raises data-bearing resource deletion/replacement to critical severity.
- Checks for new unrestricted IPv4/IPv6 exposure, IAM changes and wildcard permissions, public storage exposure, disabled backup/recovery protections, weakened transport security, logging changes, and encryption-related changes. Protective S3 settings such as enabling encryption/versioning or blocking public access are not treated as risks.
- Adds a recommendation to every finding and sorts findings by severity.
- Includes plan-derived principal, action, and resource details for public S3 policies; protocol, ports, and source CIDRs for public ingress; and action/resource fields for wildcard IAM grants. Overall attention reflects the highest finding severity; the numeric score remains a separate prioritization aid.
- Detects removal or weakening of existing S3 encryption, versioning, and public-access-block controls in plan before/after changes.
- Reports plan metadata and warnings for incomplete/error states, resource drift, and output changes.
- Works offline for risk analysis; no AI key or network connection is required.
- Keeps the plan review as a human decision. A clean report is not proof that a plan is safe.
Supported checks (initial AWS coverage)
The following are explicit heuristics, not an exhaustive AWS policy evaluator:
| Area |
Checks currently covered |
| S3 |
Explicit Allow bucket-policy statements with wildcard principals; S3 GetObject, PutObject, broad s3:*/* actions; wildcard resources and bucket/object ARN scope; common public ACL values; S3 public-access-block controls removed or disabled; server-side encryption configuration removed/disabled; versioning disabled/suspended/removed |
| IAM |
Newly introduced wildcard Action or Resource values in policy statements; wildcard principals in IAM role trust policies; readable statement values included as evidence |
| Security groups/networking |
Unrestricted IPv4/IPv6 inbound CIDRs on common nested AWS security-group and VPC ingress-rule shapes; TCP/22 identified as SSH and TCP/3389 as RDP; protocol, ports, and CIDRs included; egress is excluded from inbound exposure findings |
| CloudWatch/logging |
CloudWatch log-group deletion and reduced configured retention; deletion of supported S3 logging, flow-log, and CloudTrail resources; explicitly disabled logging flags |
| Backups |
Explicitly disabled backup/recovery flags; reduced numeric RDS-style backup retention; removal of supported controls where plan values show the previous state |
| Encryption |
Explicit false encryption flags; removal of the supported S3 bucket encryption configuration resource |
| Destructive changes |
Resource delete and replacement; database-like resource deletion/replacement is elevated by a name/type heuristic |
Unknown values, sensitivity masks, malformed policy JSON, unsupported plan formats, and truncated analysis are surfaced as warnings. For policies, PlanSignal distinguishes an inline policy expression configured on aws_s3_bucket from the separate aws_s3_bucket_policy resource; provider-computed bucket policy attributes without a configured expression are not described as an unknown inline policy. If the planned policy is wholly unknown, PlanSignal may parse the matching .tf resource block from inside the selected workspace and inspect literal policy structure. Any resulting finding is explicitly marked as Terraform-source-derived, not as a confirmed final planned value. Interpolated values such as a not-yet-known bucket ARN remain unknown; references are never evaluated or executed. Source inspection skips symlinks and generated/vendor directories and is bounded to 500 .tf files, 1 MB per file, 10 MB total, and 20 directory levels. Partial after_unknown masks are applied before analyzing known evidence, and unknown action/resource fields are not emitted as known evidence. Providers expose different schemas; checks only run for recognized value shapes and must not be interpreted as universal AWS coverage.
Install and run from source
Requirements: Node.js, npm, Visual Studio Code, and Terraform available on PATH (or set terraformPlanSignal.terraformPath).
npm install
npm test
Open this folder in VS Code and press F5 to launch an Extension Development Host. In a Terraform workspace, create a saved plan:
terraform plan -out=tfplan
In the Extension Development Host, open the Command Palette and run PlanSignal: Review Terraform Plan. Enter tfplan (or another saved plan inside the workspace).
The extension runs:
terraform show -json <saved-plan>
It refuses plan paths outside the open workspace folder, including paths that resolve outside it through a symbolic link. For unknown S3 policy values, it may also read matching .tf files under that workspace; it does not follow symlinks during source inspection. It does not invoke terraform plan, terraform apply, or any infrastructure-changing command.
Settings
terraformPlanSignal.terraformPath: Terraform executable; default terraform.
terraformPlanSignal.planFile: default saved plan path; default tfplan.
Tests
npm run lint checks the TypeScript source. npm test compiles the extension and runs the analyzer regression suite, Terraform-source inspection tests, and an extension-command process-interception test. The safety test invokes the registered review command with a fake Terraform executable and asserts the only process invocation is terraform show -json <saved-plan>; other child-process APIs are rejected. Fixtures cover valid empty/malformed plans, create/update/delete/replace/read/no-op actions, specific AWS policy and network shapes, private/unknown/sensitive values, removal of security controls, score consistency, and negative cases. The suite does not replace testing against sanitized JSON emitted by each target Terraform and provider version.
Limitations and safe use
The checks are heuristic and provider-agnostic. Known AWS and common network-rule JSON shapes are handled, but provider schemas vary, and unknown or computed values can limit what a plan reveals. PlanSignal warns when relevant network or policy values are unknown; it does not replace provider-specific policy evaluation or an independent review of the full plan. Values Terraform marks sensitive are masked before analysis output. The risk score is a deterministic prioritization aid, not a calibrated probability. Analysis is bounded to 20 MiB of terraform show -json output, 10,000 resource changes, and a 120-second command timeout; over-limit plans fail or receive an explicit incomplete-analysis warning rather than being presented as clean. Always inspect the full Terraform plan, provider documentation, and your organization's change process. Treat Terraform plan JSON as sensitive; do not commit saved plans or generated JSON.
Publishing
Before publishing:
- Create or verify the Visual Studio Marketplace publisher whose identifier matches
publisher in package.json (PlanSignal); this account-side ownership cannot be checked by the local build.
- Confirm the extension identifier, version, VS Code engine compatibility, repository, issue tracker, license, README, changelog, and security contact/policy are current. The extension requires VS Code
^1.90.0.
- Run the tests and create/inspect the installable VSIX:
npm run package
npx @vscode/vsce ls --tree
Marketplace publication requires publisher membership and an explicit publish step; the package script does not publish automatically. For automated publishing, prefer Marketplace Trusted Publishing with GitHub Actions OIDC (vsce publish --oidc) or Microsoft Entra ID in Azure Pipelines over storing a long-lived Personal Access Token. See the official publishing guide for current account and workflow requirements.