rity standards must be translated into policy definitions. Use Open Policy Agent (OPA) Rego, Checkov, or a custom TypeScript rule engine. Policies should cover:
- IAM least privilege (no wildcard actions, explicit resource constraints)
- Network isolation (no public subnets for databases, security group egress restrictions)
- Encryption at rest and in transit (KMS enforcement, TLS 1.2+)
- Logging and monitoring (CloudTrail, VPC Flow Logs, audit trail retention)
- Secret management (no plaintext credentials, rotation policies)
Step 2: Instrument IaC Scanning
Integrate static analysis into the infrastructure-as-code workflow. Terraform, Pulumi, or AWS CDK plans must be parsed before apply. The scanner evaluates the planned state against baseline policies. Failures block deployment; warnings route to review queues.
Step 3: Implement Policy Validation Engine
Build a TypeScript-based validator that consumes IaC plan output and executes rule evaluations. This approach keeps security logic in the same language as application code, enabling shared testing, mocking, and CI integration.
import { readFileSync } from 'fs';
import { join } from 'path';
interface PolicyRule {
id: string;
description: string;
severity: 'critical' | 'high' | 'medium';
evaluate: (resource: any) => boolean;
}
interface ValidationReport {
passed: number;
failed: number;
violations: Array<{ ruleId: string; resource: string; severity: string; message: string }>;
}
const policies: PolicyRule[] = [
{
id: 'IAM-001',
description: 'IAM roles must not use wildcard actions',
severity: 'critical',
evaluate: (res) => {
const actions = res.Properties?.AssumeRolePolicyDocument?.Statement?.flatMap((s: any) => s.Action) || [];
return !actions.some((a: string) => a.includes('*'));
}
},
{
id: 'NET-002',
description: 'Security groups must not allow 0.0.0.0/0 on port 22',
severity: 'high',
evaluate: (res) => {
const ingress = res.Properties?.SecurityGroupIngress || [];
return !ingress.some((i: any) => i.CidrIp === '0.0.0.0/0' && i.FromPort === 22);
}
},
{
id: 'ENC-003',
description: 'S3 buckets must enforce encryption',
severity: 'high',
evaluate: (res) => {
return res.Properties?.BucketEncryption !== undefined;
}
}
];
export function validatePlan(planPath: string): ValidationReport {
const plan = JSON.parse(readFileSync(planPath, 'utf-8'));
const resources = plan.resource_changes || [];
const report: ValidationReport = { passed: 0, failed: 0, violations: [] };
for (const change of resources) {
const resource = change.change?.after || change.change?.before;
if (!resource) continue;
for (const policy of policies) {
const isValid = policy.evaluate(resource);
if (!isValid) {
report.failed++;
report.violations.push({
ruleId: policy.id,
resource: change.address,
severity: policy.severity,
message: policy.description
});
} else {
report.passed++;
}
}
}
return report;
}
// CLI entrypoint
if (require.main === module) {
const planFile = process.argv[2] || 'tfplan.json';
const report = validatePlan(planFile);
console.log(JSON.stringify(report, null, 2));
process.exit(report.failed > 0 ? 1 : 0);
}
Step 4: Integrate into CI/CD Pipeline
The validator runs after terraform plan or pulumi preview. CI configuration enforces gate behavior:
critical violations block merge
high violations require security team approval
medium violations log to dashboard for backlog tracking
- Exit codes map to pipeline status (0 = pass, 1 = fail)
Step 5: Add Runtime Drift Detection
Static validation only covers planned state. Deploy a lightweight agent or scheduled job that queries cloud APIs (AWS Config, Azure Policy, GCP Security Command Center) and compares actual state against baseline policies. Drift triggers automated remediation tickets or self-healing scripts.
Architecture Rationale:
- Language-native validation reduces context switching for TypeScript/Node.js teams
- Plan-time interception prevents resource creation before policy evaluation
- Exit code mapping enables seamless CI/CD integration without custom orchestration
- Runtime drift detection closes the gap between desired and actual state
- Policy version control enables audit trails and rollback capabilities
Pitfall Guide
-
Treating Checklists as Static Artifacts
Checklists stored in wikis or spreadsheets are never executed. They become outdated the moment infrastructure changes. Best practice: version control policies alongside IaC. Treat security rules as code with unit tests, code review, and automated deployment.
-
Ignoring Runtime Drift
Static validation only covers the deployment window. Manual console changes, third-party integrations, and emergency patches introduce drift. Best practice: schedule periodic state reconciliation using cloud-native config services or custom polling agents. Alert on unauthorized modifications.
-
Over-Trust in Provider Defaults
Cloud providers ship with permissive defaults for ease of use. Security groups allow all traffic, IAM roles inherit broad permissions, storage buckets lack encryption. Best practice: enforce deny-by-default policies. Explicitly grant only required permissions. Validate against CIS benchmarks before provisioning.
-
Secret Sprawl in IaC and Logs
Developers embed API keys, database passwords, and tokens in Terraform variables, environment files, or CI logs. Best practice: use dedicated secret managers (AWS Secrets Manager, HashiCorp Vault, Azure Key Vault). Reference secrets via dynamic lookups. Scan repositories with pre-commit hooks for entropy and known secret patterns.
-
IAM Over-Provisioning and Role Chaining
Granting AdministratorAccess or wildcard actions simplifies development but creates lateral movement paths. Role chaining allows compromised services to escalate privileges. Best practice: implement just-in-time access, session tagging, and permission boundaries. Use AWS IAM Access Analyzer or equivalent to identify unused permissions and remove them.
-
Multi-Cloud Policy Fragmentation
Maintaining separate checklists for AWS, Azure, and GCP creates inconsistency and operational overhead. Best practice: abstract policies to a cloud-agnostic layer (OPA/Rego, Checkov, or custom TS engine). Map cloud-specific resources to generic policy targets. Maintain a single source of truth for security rules.
-
No Feedback Loop to Developers
Security violations reported in isolated dashboards are ignored. Developers lack context and urgency. Best practice: inline violations in pull requests. Provide remediation snippets. Track security debt alongside application bugs. Integrate with Slack/Teams for real-time alerts on critical breaches.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Single Cloud (AWS/Azure/GCP) | Cloud-native policy service + IaC scanner | Native integration, low setup overhead, provider-supported compliance mappings | Low: $0–$500/mo for tooling |
| Multi-Cloud Environment | OPA/Rego or TypeScript policy engine | Cloud-agnostic rules, unified governance, avoids vendor lock-in | Medium: $1k–$3k/mo for engineering time |
| Regulated Industry (HIPAA, PCI, SOC2) | Policy-as-code + continuous compliance dashboard | Audit-ready evidence, automated reporting, drift prevention | High: $3k–$8k/mo for tooling + compliance ops |
| Startup / Rapid Scaling | Lightweight Checkov + GitHub Actions gate | Fast deployment, low maintenance, scales with team size | Low: $0–$200/mo, minimal engineering overhead |
Configuration Template
CI/CD Pipeline Integration (GitHub Actions)
name: Cloud Security Validation
on:
pull_request:
paths: ['infrastructure/**']
push:
branches: [main]
jobs:
security-gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- name: Generate Terraform Plan
run: terraform plan -out=tfplan -json > tfplan.json
- name: Run Policy Validation
run: |
npm ci
npx ts-node src/validate.ts tfplan.json
env:
NODE_ENV: ci
- name: Upload Security Report
if: always()
uses: actions/upload-artifact@v4
with:
name: security-report
path: security-report.json
Terraform + OPA Integration (Checkov Alternative)
# main.tf
resource "aws_s3_bucket" "secure_bucket" {
bucket = "my-secure-bucket"
server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
}
}
}
}
# .checkov.yaml
skip-check:
- CKV_AWS_18
framework:
- terraform
quiet: true
output: cli
Quick Start Guide
-
Initialize Repository Structure
Create infrastructure/, security/policies/, and ci/ directories. Add package.json with TypeScript dependencies (typescript, ts-node, @types/node).
-
Install Policy Validator
Run npm init -y && npm install typescript ts-node @types/node. Copy the TypeScript validator script into src/validate.ts. Compile with npx tsc --init and set target: "ES2020".
-
Generate Terraform Plan
Navigate to infrastructure/. Run terraform init && terraform plan -out=tfplan -json > ../tfplan.json. Ensure no resources are created during plan generation.
-
Execute Validation
Run npx ts-node src/validate.ts tfplan.json. Review JSON output. Fix critical/high violations. Commit changes. Push to trigger CI gate.
-
Verify Pipeline Integration
Open a pull request modifying infrastructure files. Confirm CI runs the validator, blocks merge on critical violations, and uploads the security report artifact. Adjust severity thresholds as needed.