Skip to main content

GitHub PR Checks Integration

Shadow Executor can automatically scan infrastructure changes in pull requests and enforce policies before code is merged.

Overview

The GitHub PR Checks integration:

  • Scans infrastructure changes - Parses Terraform plans and CloudFormation templates in PRs
  • Evaluates policies - Runs infrastructure changes through Shadow Executor policy engine
  • Posts PR comments - Provides detailed feedback directly in the pull request
  • Fails CI checks - Blocks merging when blocking policies are triggered
  • Provides audit trail - All policy decisions are logged and HMAC-signed

Supported Infrastructure Formats

Terraform

Shadow Executor can parse Terraform plan JSON files to extract:

  • Resource creates, updates, and deletions
  • Resource tags
  • Operation parameters

Supported resources:

  • AWS RDS (CreateDBInstance, DeleteDBInstance, ModifyDBInstance)
  • AWS S3 (CreateBucket, DeleteBucket, PutObject, DeleteObject)
  • AWS IAM (CreateUser, CreateRole, CreatePolicy, DeleteUser, DeleteRole)
  • AWS Lambda (CreateFunction, DeleteFunction)
  • AWS DynamoDB (CreateTable, DeleteTable)
  • AWS EC2 (RunInstances, TerminateInstances)

CloudFormation

Shadow Executor can parse CloudFormation templates (JSON/YAML) to extract:

  • Resource definitions
  • Resource tags
  • Resource properties

Setup

1. Create Policy File

Create .shadow-exec.policy.yaml in your repository root:

version: "1.0"
name: GitHub PR Policy
rules:
- id: SE-001
name: Block production database deletion
severity: CRITICAL
action: BLOCK
match:
service: rds
operation: DeleteDBInstance
resource_tags:
Environment: production

- id: SE-002
name: Require approval for IAM changes
severity: HIGH
action: REQUIRE_APPROVAL
match:
service: iam
operation: [CreateRole, CreatePolicy, AttachUserPolicy]

- id: SE-003
name: Warn on S3 bucket deletion
severity: MEDIUM
action: WARN
match:
service: s3
operation: DeleteBucket

2. Add GitHub Workflow

Create .github/workflows/shadow-exec-pr-check.yml:

name: Shadow Executor PR Check

on:
pull_request:
branches: [main, develop]
paths:
- '**.tf'
- '**.tfvars'
- '**shadow-exec.policy.yaml'

permissions:
contents: read
pull-requests: write

jobs:
policy-check:
name: Infrastructure Policy Check
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Setup Terraform
uses: hashicorp/setup-terraform@v3

- name: Terraform Init
run: terraform init

- name: Terraform Plan
run: terraform plan -out=tfplan

- name: Convert plan to JSON
run: terraform show -json tfplan > tfplan.json

- name: Install Shadow Executor
run: npm install -g @shadow-executor/github

- name: Run Shadow Executor
run: |
npx shadow-exec-pr-check \
--policy .shadow-exec.policy.yaml \
--terraform-plan tfplan.json \
--fail-on-block true \
--github-token ${{ secrets.GITHUB_TOKEN }}

Usage

Programmatic API

You can use the Shadow Executor GitHub integration programmatically:

import { runPolicyCheck } from '@shadow-executor/github';

const result = await runPolicyCheck({
policyPath: './shadow-exec.policy.yaml',
terraformPlanPath: './tfplan.json',
failOnBlock: true,
postComment: true,
githubToken: process.env.GITHUB_TOKEN,
});

console.log(`Passed: ${result.passed}`);
console.log(`Blocked: ${result.blockedCount}`);
console.log(`Warnings: ${result.warnCount}`);
console.log(result.comment); // Markdown comment for PR

Policy Check Result

The runPolicyCheck function returns a detailed result:

interface GitHubActionResult {
passed: boolean; // Whether check passed
changes: InfrastructureChange[]; // All detected changes
decisions: PolicyDecision[]; // Policy decisions
blockedCount: number; // Number of BLOCK actions
warnCount: number; // Number of WARN actions
approvalRequiredCount: number; // Number of REQUIRE_APPROVAL actions
comment: string; // Markdown comment for PR
summary: string; // One-line summary for CI
}

PR Comment Format

Shadow Executor posts structured comments on pull requests:

Blocked Operations

## Shadow Executor Policy Check

**Status:** ❌ BLOCKED

### 🚫 Blocked Operations (1)

- **SE-001** - Block production database deletion
- Operation: `rds:DeleteDBInstance`
- Resource: `arn:aws:rds:us-east-1:123456789012:db:prod-db`
- Tags: `Environment=production`
- File: `infrastructure/database.tf:42`
- Reason: Matched rule SE-001: Delete operation on production RDS instance

**Recommendations:**
- Review the destructive operations listed above
- Verify these changes are intentional and approved
- Consider updating policies if these operations should be allowed
- Require manual approval for production changes

---
**Summary:** 1 infrastructure change(s) evaluated | 1 blocked | 0 warnings | 0 require approval

Warnings

## Shadow Executor Policy Check

**Status:** ✅ PASSED

### ⚠️ Warnings (1)

- **SE-002**: s3:DeleteBucket on `my-logs-bucket`
- Deleting S3 buckets can cause data loss

---
**Summary:** 1 infrastructure change(s) evaluated | 0 blocked | 1 warnings | 0 require approval

Configuration Options

failOnBlock

  • Type: boolean
  • Default: true
  • Description: Fail CI check when BLOCK action is triggered

failOnAlert

  • Type: boolean
  • Default: true
  • Description: Fail CI check when BLOCK_AND_ALERT action is triggered

postComment

  • Type: boolean
  • Default: false
  • Description: Post detailed comment on PR (requires githubToken)

agentId

  • Type: string
  • Default: "github-pr-check"
  • Description: Agent identifier for audit trail

Terraform Integration

Generate JSON Plan

Shadow Executor requires Terraform plan in JSON format:

terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json

Supported Operations

ResourceCreateDeleteUpdate
RDS Instance
RDS Cluster
S3 Bucket
S3 Object-
IAM User
IAM Role
IAM Policy-
Lambda Function
DynamoDB Table
EC2 Instance-

CloudFormation Integration

Parse Template

Shadow Executor can parse CloudFormation templates directly:

const result = await runPolicyCheck({
policyPath: './shadow-exec.policy.yaml',
cloudFormationTemplatePath: './template.json',
});

Both Terraform and CloudFormation

You can check both in a single run:

const result = await runPolicyCheck({
policyPath: './shadow-exec.policy.yaml',
terraformPlanPath: './tfplan.json',
cloudFormationTemplatePath: './template.json',
});

Troubleshooting

No changes detected

Symptom: PR check always passes with "No infrastructure changes detected"

Solutions:

  1. Verify Terraform plan path is correct
  2. Ensure plan was generated: terraform plan -out=tfplan
  3. Confirm JSON conversion: terraform show -json tfplan > tfplan.json
  4. Check file exists in workflow: ls -la tfplan.json

Policy not blocking operations

Symptom: Operations that should be blocked are passing

Solutions:

  1. Verify policy file syntax: cat .shadow-exec.policy.yaml
  2. Check service names match (e.g., db not rds for aws_db_instance)
  3. Verify tag matching is exact (case-sensitive)
  4. Review rule ordering (first match wins)

Parse errors

Symptom: "Invalid Terraform plan" or "Invalid CloudFormation template"

Solutions:

  1. Validate Terraform plan: terraform show -json tfplan | jq .
  2. Validate CloudFormation: aws cloudformation validate-template --template-body file://template.json
  3. Ensure files are valid JSON (not corrupted)

Example Workflows

Require Approval for Production Changes

version: "1.0"
name: Production Safety
rules:
- id: PROD-001
name: Require approval for production changes
severity: HIGH
action: REQUIRE_APPROVAL
match:
resource_tags:
Environment: production

Block Destructive Operations

version: "1.0"
name: Destructive Operation Protection
rules:
- id: DEST-001
name: Block all delete operations
severity: CRITICAL
action: BLOCK
match:
operation:
- DeleteDBInstance
- DeleteBucket
- DeleteTable
- TerminateInstances

Warn on Cost-Heavy Resources

version: "1.0"
name: Cost Control
rules:
- id: COST-001
name: Warn on large EC2 instances
severity: MEDIUM
action: WARN
match:
service: ec2
operation: RunInstances

Next Steps

FAQ

Q: Can I use Shadow Executor with CDK?

A: Yes! CDK synthesizes to CloudFormation templates. Run cdk synth to generate cdk.out/*.template.json and pass it to Shadow Executor.

Q: What about multi-environment repositories?

A: Create environment-specific policies:

# .shadow-exec.prod.policy.yaml
# .shadow-exec.dev.policy.yaml

Then use different policies based on branch:

- name: Run Shadow Executor (prod)
if: github.base_ref == 'main'
run: npx shadow-exec-pr-check --policy .shadow-exec.prod.policy.yaml

- name: Run Shadow Executor (dev)
if: github.base_ref == 'develop'
run: npx shadow-exec-pr-check --policy .shadow-exec.dev.policy.yaml

Q: Can I skip Shadow Executor checks?

A: Yes, but with explicit approval. Add [skip shadow-exec] to PR title and require manual approval:

- name: Check for skip label
run: |
if [[ "${{ github.event.pull_request.title }}" == *"[skip shadow-exec]"* ]]; then
echo "Shadow Executor check skipped by PR author"
exit 0
fi

Q: How do I test policies locally?

A: Run Shadow Executor locally before pushing:

terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json
npx shadow-exec-pr-check --policy .shadow-exec.policy.yaml --terraform-plan tfplan.json