Enforce infrastructure policies with OPA/Conftest, Checkov, and SCPs
Policy as Code validates infrastructure changes against security, compliance, and organizational rules before deployment. This prevents misconfigurations from reaching production.
graph LR
A[terraform plan] --> B[terraform show -json]
B --> C[conftest test]
C --> D{Pass?}
D -->|Yes| E[terraform apply]
D -->|No| F[Fail Pipeline]
style F fill:#ff6b6b
style E fill:#90EE90
| Tool | Language | Use Case |
|---|---|---|
| OPA/Conftest | Rego | Custom policies, Terraform |
| Checkov | Python/YAML | Security scanning, compliance |
| tfsec | Go | Terraform security |
| SCPs | JSON | AWS Organizations guardrails |
Custom policies to enforce tagging, encryption, and access controls.
π Location: policies/conftest/
# policies/terraform/required_tags.rego
package terraform.required_tags
required_tags := ["Environment", "Owner", "Project", "CostCenter"]
deny[msg] {
resource := input.resource_changes[_]
resource.change.actions[_] == "create"
# Check for required tags
missing := required_tags - object.keys(resource.change.after.tags)
count(missing) > 0
msg := sprintf(
"%s '%s' is missing required tags: %v",
[resource.type, resource.name, missing]
)
}
# policies/terraform/s3_encryption.rego
package terraform.s3_security
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_s3_bucket"
resource.change.actions[_] == "create"
# Check for encryption
not resource.change.after.server_side_encryption_configuration
msg := sprintf("S3 bucket '%s' must have encryption enabled", [resource.name])
}
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_s3_bucket"
resource.change.actions[_] == "create"
# Check for public access block
not resource.change.after.block_public_acls
msg := sprintf("S3 bucket '%s' must block public access", [resource.name])
}
# Generate plan JSON
terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json
# Run Conftest
conftest test tfplan.json -p policies/terraform/
Pre-built security checks with custom policies.
π Location: policies/checkov/
# Scan Terraform files
checkov -d terraform/ --framework terraform
# With custom policies
checkov -d terraform/ --external-checks-dir policies/checkov/
# Output for CI
checkov -d terraform/ -o junitxml > checkov-results.xml
# policies/checkov/custom_rds_encryption.yaml
metadata:
id: "CUSTOM_RDS_001"
name: "Ensure RDS instances are encrypted"
category: "ENCRYPTION"
definition:
cond_type: "attribute"
resource_types:
- "aws_db_instance"
attribute: "storage_encrypted"
operator: "is_true"
# GitHub Actions example
- name: Terraform Plan
run: |
terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json
- name: Run Conftest
run: conftest test tfplan.json -p policies/ --no-fail
- name: Run Checkov
uses: bridgecrewio/checkov-action@master
with:
directory: terraform/
framework: terraform
soft_fail: false