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
| Resource | Create | Delete | Update |
|---|---|---|---|
| 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:
- Verify Terraform plan path is correct
- Ensure plan was generated:
terraform plan -out=tfplan - Confirm JSON conversion:
terraform show -json tfplan > tfplan.json - Check file exists in workflow:
ls -la tfplan.json
Policy not blocking operations
Symptom: Operations that should be blocked are passing
Solutions:
- Verify policy file syntax:
cat .shadow-exec.policy.yaml - Check service names match (e.g.,
dbnotrdsforaws_db_instance) - Verify tag matching is exact (case-sensitive)
- Review rule ordering (first match wins)
Parse errors
Symptom: "Invalid Terraform plan" or "Invalid CloudFormation template"
Solutions:
- Validate Terraform plan:
terraform show -json tfplan | jq . - Validate CloudFormation:
aws cloudformation validate-template --template-body file://template.json - 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
- Policy Reference - Learn about all policy rule types
- Simulation Fidelity - Understand simulation accuracy
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