Skip to content

IaC Change Review

Analyze Infrastructure as Code changes from Terraform, Pulumi, or CDK to produce a Risk Summary and Operator-Friendly Change Summary.

Workflow Definition

name: iac-review
description: |
Analyze Infrastructure as Code changes (Terraform, Pulumi, CDK) to produce
a risk assessment and operator-friendly change summary. Identifies dangerous
changes, security implications, and provides clear explanations of what's changing.
version: "1.0"
inputs:
- name: working_dir
type: string
required: false
default: "."
description: Path to IaC project directory
- name: tool
type: string
required: false
default: "terraform"
description: IaC tool (terraform, pulumi, cdk)
- name: plan_output
type: string
required: false
description: Pre-generated plan output (skips running the plan command)
- name: environment
type: string
required: false
default: "unknown"
description: Target environment (dev, staging, production)
- name: strict_mode
type: boolean
required: false
default: false
description: Fail on any HIGH or CRITICAL risk findings
steps:
# Step 1: Generate the plan (if not provided)
- id: generate_plan
name: Generate IaC Plan
condition: "{{not .inputs.plan_output}}"
shell.run:
command: |
cd {{.inputs.working_dir}}
{{if eq .inputs.tool "terraform"}}
terraform plan -no-color -detailed-exitcode 2>&1 || true
{{else if eq .inputs.tool "pulumi"}}
pulumi preview --diff --non-interactive 2>&1 || true
{{else if eq .inputs.tool "cdk"}}
cdk diff 2>&1 || true
{{else}}
echo "Unsupported tool: {{.inputs.tool}}" && exit 1
{{end}}
timeout: 300
# Step 2: Risk Analysis - identify dangerous changes
- id: risk_analysis
name: Risk Analysis
type: llm
model: balanced
system: |
You are an infrastructure security expert analyzing IaC plan output.
Identify and flag the following risk categories:
**CRITICAL Risks (block deployment):**
- Destruction of production databases or persistent storage
- Removal of backup configurations
- Security group changes exposing sensitive ports (22, 3389, databases) to 0.0.0.0/0
- IAM policy changes granting admin/* or overly permissive access
- Disabling encryption on storage or transit
- Removing audit logging or monitoring
- Changes to authentication/authorization systems
**HIGH Risks (requires approval):**
- Any resource destruction in production
- Scaling down (reducing instance counts, memory, CPU)
- Network topology changes (VPC, subnets, routing)
- Certificate or secret rotation
- DNS record modifications
- Load balancer configuration changes
- Database configuration changes (even non-destructive)
**MEDIUM Risks (review recommended):**
- New IAM roles or policies
- Security group modifications (not critical)
- New resources being created
- Tag changes on critical resources
- Environment variable changes
**LOW Risks (informational):**
- Documentation/description updates
- Tag additions
- Non-critical resource updates
**Anomaly Detection:**
- Flag unexpected changes (resources being modified that shouldn't be)
- Identify drift (resources changing that weren't in the commit)
- Note unusually large change counts
- Highlight resources changing for the first time
For each finding, provide:
1. Risk level (CRITICAL/HIGH/MEDIUM/LOW)
2. Resource affected (type and name/ID)
3. What's changing
4. Why it's risky
5. Recommended action
prompt: |
Analyze this {{.inputs.tool}} plan for risks:
**Environment:** {{.inputs.environment}}
**Plan Output:**
```
{{if .inputs.plan_output}}{{.inputs.plan_output}}{{else}}{{.steps.generate_plan.stdout}}{{end}}
```
Provide a structured risk assessment. Group findings by severity.
Include a risk score (0-100) and deployment recommendation.
Format as:
## Risk Score: X/100
## Deployment Recommendation
[PROCEED / PROCEED_WITH_CAUTION / REQUIRES_APPROVAL / BLOCK]
## Critical Risks
...
## High Risks
...
## Medium Risks
...
## Low Risks / Informational
...
## Anomalies Detected
...
timeout: 60
retry:
max_attempts: 2
backoff_base: 2
backoff_multiplier: 2.0
# Step 3: Operator-Friendly Change Summary
- id: change_summary
name: Change Summary for Operators
type: llm
model: fast
system: |
You are a technical writer creating infrastructure change summaries for
network operators and on-call engineers who need to understand changes quickly.
Write in plain English. Avoid IaC jargon. Focus on operational impact:
**What operators care about:**
- What endpoints/services are affected?
- Will there be downtime or degraded performance?
- What ports/protocols are changing?
- Are there IP address changes?
- What monitoring/alerting might fire?
- What rollback steps exist?
- Are there dependencies that need coordination?
**Format guidelines:**
- Use bullet points for easy scanning
- Group by service/application affected
- Include before/after for key changes
- Highlight anything requiring manual intervention
- Note if changes are reversible vs destructive
**Avoid:**
- Terraform/Pulumi/CDK resource type names
- ARNs or resource IDs (use friendly names)
- Implementation details (use outcomes)
prompt: |
Create an operator-friendly summary of these infrastructure changes:
**Environment:** {{.inputs.environment}}
**Raw Plan:**
```
{{if .inputs.plan_output}}{{.inputs.plan_output}}{{else}}{{.steps.generate_plan.stdout}}{{end}}
```
Write a clear summary that a network operator can understand at 3 AM during an incident.
Format as:
## Summary
[One sentence: what's changing and why it matters]
## Services Affected
- [Service name]: [what's changing]
## Network Changes
- [Any connectivity, routing, DNS changes]
## Potential Impact
- [Downtime expectations]
- [Performance implications]
- [Monitoring alerts to expect]
## Action Required
- [Any manual steps needed]
- [Coordination required]
## Rollback
- [Is this reversible?]
- [Rollback steps if needed]
timeout: 45
retry:
max_attempts: 2
backoff_base: 2
backoff_multiplier: 2.0
# Step 4: Consolidate into final report
- id: final_report
name: Generate Final Report
type: llm
model: fast
system: |
You are combining a risk analysis and change summary into a single
deployment review document. Keep both sections intact but add:
1. An executive summary at the top (2-3 sentences)
2. A clear GO/NO-GO recommendation
3. Required approvals based on risk level
prompt: |
Combine these analyses into a final IaC change review:
**Risk Analysis:**
{{.steps.risk_analysis.response}}
**Operator Summary:**
{{.steps.change_summary.response}}
Create a final report with:
1. Executive Summary (2-3 sentences)
2. GO/NO-GO Recommendation
3. Required Approvals (based on {{.inputs.environment}} and risk level)
4. Risk Analysis (from above)
5. Operator Summary (from above)
For production + CRITICAL/HIGH risks, recommend approval from:
- Platform team lead
- Security team (for security-related changes)
- Database team (for data changes)
timeout: 30
outputs:
- name: report
type: string
value: "{{.steps.final_report.response}}"
description: Complete IaC change review report
- name: risk_score
type: string
value: "{{.steps.risk_analysis.response}}"
description: Detailed risk analysis
- name: operator_summary
type: string
value: "{{.steps.change_summary.response}}"
description: Operator-friendly change summary
- name: recommendation
type: string
value: "{{.steps.final_report.response}}"
description: GO/NO-GO recommendation with required approvals

Why Use an LLM for This?

IaC plan outputs are verbose and technical. Important changes hide in walls of text:

  • A security group rule change is just one line among hundreds
  • Resource destruction looks the same as resource creation at a glance
  • The operational impact of changes isn’t obvious from resource diffs

This workflow uses LLM reasoning to:

  1. Identify risks - Flag dangerous changes that humans might miss
  2. Assess context - Understand that deleting a database in production is worse than in dev
  3. Translate for operators - Convert technical diffs into “what does this mean at 3 AM?”

Features

  • Multi-tool support: Terraform, Pulumi, CDK
  • Risk scoring: 0-100 score with GO/NO-GO recommendation
  • Anomaly detection: Flags unexpected changes and drift
  • Operator summaries: Plain English explanations for on-call engineers
  • Environment awareness: Different thresholds for dev vs production
  • Approval routing: Recommends who needs to approve based on risk

Usage

Basic Usage - Run Plan Inline

Terminal window
# Terraform project
conductor run examples/iac-review \
--input working_dir=./infrastructure \
--input tool=terraform \
--input environment=production
# Pulumi project
conductor run examples/iac-review \
--input working_dir=./infra \
--input tool=pulumi \
--input environment=staging
# CDK project
conductor run examples/iac-review \
--input working_dir=./cdk \
--input tool=cdk \
--input environment=dev

Provide Pre-Generated Plan

If you’ve already run the plan, pass it directly:

Terminal window
# Terraform
terraform plan -no-color > plan.txt
conductor run examples/iac-review \
--input plan_output="$(cat plan.txt)" \
--input tool=terraform \
--input environment=production
# Pulumi
pulumi preview --diff > preview.txt
conductor run examples/iac-review \
--input plan_output="$(cat preview.txt)" \
--input tool=pulumi \
--input environment=production

CI/CD Integration

GitHub Actions

name: IaC Review
on:
pull_request:
paths:
- 'terraform/**'
- 'infrastructure/**'
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
- name: Terraform Init
run: terraform init
working-directory: ./terraform
- name: Generate Plan
id: plan
run: terraform plan -no-color -out=tfplan 2>&1 | tee plan.txt
working-directory: ./terraform
continue-on-error: true
- name: IaC Review
id: review
run: |
conductor run examples/iac-review \
--input plan_output="$(cat terraform/plan.txt)" \
--input tool=terraform \
--input environment=${{ github.base_ref == 'main' && 'production' || 'staging' }} \
--output-json > review.json
- name: Comment on PR
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const review = JSON.parse(fs.readFileSync('review.json', 'utf8'));
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: review.report
});
- name: Block on Critical
run: |
if grep -q "BLOCK" review.json; then
echo "::error::Critical risks detected. Deployment blocked."
exit 1
fi

GitLab CI

iac-review:
stage: validate
script:
- terraform init
- terraform plan -no-color > plan.txt
- |
conductor run examples/iac-review \
--input plan_output="$(cat plan.txt)" \
--input tool=terraform \
--input environment=${CI_ENVIRONMENT_NAME:-staging} \
--output-json > review.json
- cat review.json | jq -r '.report'
artifacts:
reports:
dotenv: review.env
paths:
- review.json
rules:
- changes:
- "terraform/**/*"

Jenkins Pipeline

pipeline {
agent any
stages {
stage('IaC Review') {
steps {
dir('terraform') {
sh 'terraform init'
sh 'terraform plan -no-color > plan.txt'
}
sh '''
conductor run examples/iac-review \
--input plan_output="$(cat terraform/plan.txt)" \
--input tool=terraform \
--input environment=${ENVIRONMENT} \
--output-json > review.json
'''
script {
def review = readJSON file: 'review.json'
if (review.report.contains('BLOCK')) {
error('Critical risks detected. Deployment blocked.')
}
}
}
}
}
}

Example Output

## Executive Summary
This change modifies the production RDS instance configuration and updates
security group rules. Risk score: 72/100. Requires platform team approval
before proceeding.
## Recommendation: PROCEED_WITH_CAUTION
### Required Approvals
- Platform team lead (database configuration change)
- Security team (security group modification)
---
## Risk Score: 72/100
## Critical Risks
None detected.
## High Risks
### 1. RDS Instance Modification
- **Resource:** aws_db_instance.production
- **Change:** Modifying instance class from db.r5.large to db.r5.xlarge
- **Risk:** Database restart required, potential 5-10 minute downtime
- **Recommendation:** Schedule during maintenance window
### 2. Security Group Update
- **Resource:** aws_security_group.api_servers
- **Change:** Adding ingress rule for port 443 from 10.0.0.0/8
- **Risk:** Expands network access to internal CIDR
- **Recommendation:** Verify source CIDR is expected
## Medium Risks
### 1. New IAM Role
- **Resource:** aws_iam_role.lambda_execution
- **Change:** Creating new role with S3 read access
- **Risk:** New permissions being introduced
- **Recommendation:** Review policy document
---
## Services Affected
- **API Service**: Security group rules changing, may affect connectivity
- **Database**: Configuration update, expect brief restart
## Network Changes
- New ingress rule allowing 10.0.0.0/8 to reach API servers on port 443
- No DNS changes
- No routing changes
## Potential Impact
- 5-10 minute database restart during RDS modification
- No expected API downtime (security group changes are immediate)
- CloudWatch may alert on RDS metric gaps during restart
## Action Required
- Schedule change during maintenance window (recommended: 2-4 AM)
- Notify on-call that RDS restart is expected
- No manual intervention needed
## Rollback
- RDS instance class change is reversible (another restart required)
- Security group rule can be removed immediately
- IAM role can be deleted if unused

Configuration Options

InputTypeDefaultDescription
working_dirstring.Path to IaC project directory
toolstringterraformIaC tool: terraform, pulumi, cdk
plan_outputstring-Pre-generated plan (skips running plan command)
environmentstringunknownTarget environment: dev, staging, production
strict_modebooleanfalseExit non-zero on HIGH or CRITICAL risks

Customization

Adjust Risk Thresholds

Modify the risk_analysis step’s system prompt to adjust what’s considered CRITICAL vs HIGH:

# In workflow.yaml, adjust the system prompt:
system: |
# Add your organization's specific risk criteria
**CRITICAL Risks:**
- Changes to PCI-scoped resources
- Modifications to SOC2 audit logging
- Your custom criteria...

Add Team-Specific Routing

Extend the final_report step to route to your teams:

prompt: |
...
For database changes, require DBA approval.
For network changes, require NetOps approval.
For IAM changes, require Security approval.

Integrate with Approval Systems

Use the JSON output to drive approval workflows:

Terminal window
# Get recommendation
recommendation=$(conductor run examples/iac-review ... --output-json | jq -r '.recommendation')
case $recommendation in
*BLOCK*)
echo "Deployment blocked"
exit 1
;;
*REQUIRES_APPROVAL*)
# Create approval request in your system
create_approval_request "$recommendation"
;;
*PROCEED*)
echo "Safe to proceed"
;;
esac