Skip to main content

Opa / Conftest Integration

Add policy context to your infrastructure changes by running Conftest/OPA policies and submitting violations as organized policy signals to enhance Overmind's risk analysis.

This GitHub Actionruns your Conftest/OPA policies in parallel with your Terraform analysis, giving you immediate feedback on compliance issues without slowing down your workflow.

screenshot of policies in signals

Quick Start​

Add policy checks to your Overmind workflow in under 2 minutes:

- uses: overmindtech/policy-signals-action@v1
with:
policies-path: './policies'
overmind-api-key: ${{ secrets.OVM_API_KEY }}

What This Action Does​

This GitHub Action enhances your Overmind change analysis by:

  • Running policy checks immediately when a PR is created or updated
  • Submitting violations as organized policy signals to Overmind (runs in parallel with Terraform analysis)
  • Providing instant feedback on compliance issues (typically within 30 seconds)
  • Enriching PR comments with policy context alongside blast radius analysis

How It Works​

Policy violations appear as organized "Policies" signals in your PR analysis. Overmind aggregates Custom Signals by title and handles the generation of the Signal summary automatically.

The action follows this flow:

  1. PR triggers workflow - Both Terraform analysis and policy checks start
  2. Policies run immediately - Conftest evaluates your Terraform plan
  3. Violations become signals - Each violation is submitted as a Custom Signal linked to the PR URL
  4. Overmind aggregates - All signals for that PR are collected together
  5. Progressive updates - PR comment updates as signals arrive
  6. Complete context - Final comment includes both policy and blast radius info

Installation​

Prerequisites​

  • An Overmind account (sign up free)
  • Overmind API key stored as OVM_API_KEY in your GitHub secrets
  • Terraform repository with policy files (Rego format)
  • A GitHub Pull Request (Custom Signals are linked to PRs)

Basic Setup​

Add the action to your workflow (.github/workflows/terraform.yml):

name: Terraform Analysis with Policies
on:
pull_request:
types: [opened, synchronize, reopened]

jobs:
# Your existing terraform job continues unchanged
terraform:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: overmindtech/actions/install-cli@main
- uses: overmindtech/actions/submit-plan@main
with:
ovm-api-key: ${{ secrets.OVM_API_KEY }}
plan-json: ./tfplan.json

# NEW: Add policy checks in parallel
policy-checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: overmindtech/policy-signals-action@v1
with:
policies-path: './policies'
overmind-api-key: ${{ secrets.OVM_API_KEY }}

Create your first policy (policies/s3-tags.rego):

package terraform.s3

deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_s3_bucket"
not resource.change.after.tags.Owner
msg := sprintf("S3 bucket %s missing required 'Owner' tag", [resource.address])
}

deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_s3_bucket"
not resource.change.after.tags.Environment
msg := sprintf("S3 bucket %s missing required 'Environment' tag", [resource.address])
}

Push changes and watch the magic happen! Policy violations will appear in your PR comment within seconds.

Configuration​

Action Inputs​

InputDescriptionRequiredDefault
policies-pathPath to your Conftest/OPA policy filesYes-
overmind-api-keyYour Overmind API keyYes-
terraform-plan-jsonPath to terraform plan JSON (if available)NoAuto-detected
signal-severityDefault severity for policy violations (-5 to +5)No-3
signal-categoryCategory for organizing policy signals in OvermindNoPolicies
ticket-linkCustom URL to link signals to (e.g., Terraform Cloud run)NoGitHub PR URL
fail-on-violationsFail the action if violations are foundNofalse
conftest-versionVersion of Conftest to installNo0.46.0
overmind-cli-versionVersion of Overmind CLI to installNo1.9.4

Advanced Configuration​

- uses: overmindtech/policy-signals-action@v1
with:
policies-path: './policies'
overmind-api-key: ${{ secrets.OVM_API_KEY }}
terraform-plan-json: './tfplan.json'
signal-severity: -4 # Higher risk score for violations
signal-category: 'Security Policies' # Custom category
ticket-link: 'https://app.terraform.io/app/org/workspace/runs/run-xyz' # Terraform Cloud run
fail-on-violations: true # Block PR on policy violations

Terraform Cloud Integration​

The ticket-link input is particularly useful for Terraform Cloud workflows:

- uses: overmindtech/policy-signals-action@v1
with:
policies-path: './policies'
overmind-api-key: ${{ secrets.OVM_API_KEY }}
ticket-link: ${{ env.TF_CLOUD_RUN_URL }} # Links signals to TFC run instead of GitHub PR

Example Policies​

Security Group Rules​

package terraform.security

deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_security_group_rule"
resource.change.after.cidr_blocks[_] == "0.0.0.0/0"
resource.change.after.from_port == 22
msg := sprintf("Security group %s allows SSH from anywhere", [resource.address])
}

Cost Control​

package terraform.cost

allowed_instance_types := [
"t3.micro", "t3.small", "t3.medium"
]

deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_instance"
not resource.change.after.instance_type in allowed_instance_types
msg := sprintf("Instance %s uses non-approved type: %s",
[resource.address, resource.change.after.instance_type])
}

Compliance​

package terraform.compliance

deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_db_instance"
not resource.change.after.storage_encrypted
msg := sprintf("RDS instance %s has unencrypted storage", [resource.address])
}

Testing​

Local Policy Testing (No API Required)​

Test your policies work correctly without submitting to Overmind:

# Test policies catch violations in your terraform plan
./test/test-policies.sh ./policies ./tfplan.json

# This will show you what violations would be caught
# but won't submit anything to Overmind

Integration Testing (Real PR Required)​

To test the complete flow with actual signal submission:

# Set your environment variables
export OVERMIND_API_KEY="your_actual_api_key"
export TICKET_LINK="https://github.com/org/repo/pull/123"

# Run the real submission test
./test/test_real_submission.sh

Important: Custom Signals are always linked to a PR URL. You cannot test signal submission without a real, existing GitHub Pull Request.

FAQ​

How do Custom Signals work without a Change ID?​

Custom Signals are linked to Pull Request URLs, not specific Change IDs. When you submit a signal with a PR URL, Overmind automatically associates it with any changes for that PR, or holds it until a change is created.

Can I test this without a real PR?​

You can test that your policies detect violations locally using Conftest, but to test the full signal submission flow, you need a real GitHub PR. Custom Signals must be linked to an actual PR URL.

What happens if I submit signals before running terraform plan?​

That's the beauty of parallel execution! Signals are aggregated by PR URL, so they'll appear as soon as they're submitted. The PR comment will progressively update as both policy signals and Terraform analysis complete.

Do signals from different jobs get combined?​

Yes! All signals submitted with the same PR URL are automatically aggregated by Overmind, whether they come from policy checks, Terraform analysis, or any other source.

What happens when all policies pass?​

No Custom Signals are submitted! This is the ideal behavior:

  • ✅ Clean runs are silent - You only hear from the policy system when there are issues
  • ✅ No noise in PRs - Policy signals only appear when violations need attention
  • ✅ Better performance - No unnecessary API calls when everything is compliant
  • ✅ Clean Overmind dashboard - Only actual issues create signals

The action logs will show ✓ No policy violations found and complete successfully. Your PR will only show the standard Terraform analysis without any policy-related Custom Signals.