EC2 Terraform OPA Policy Test
EC2 Terraform OPA Policy Test
1. Mock Terraform Plan JSON (ec2-plan.json
)
{
"format_version": "0.1",
"terraform_version": "1.13.1",
"resource_changes": {
"aws_instance.example": {
"type": "aws_instance",
"name": "example",
"change": {
"actions": ["create"],
"before": null,
"after": {
"ami": "ami-12345678",
"instance_type": "t2.micro",
"ebs_optimized": false,
"associate_public_ip_address": true,
"iam_instance_profile": "my-ec2-role",
"vpc_security_group_ids": ["sg-12345678", "sg-87654321"],
"tags": {"Environment": "Dev"},
"ebs_block_device": [
{"device_name": "/dev/sda1", "volume_size": 30, "encrypted": false},
{"device_name": "/dev/sdb", "volume_size": 50, "encrypted": true}
]
}
}
}
}
}
Explanation of Fields
Field |
Purpose / Relevance |
instance_type | Must follow allowed instance types |
ebs_optimized | Must be true for performance |
associate_public_ip_address | Must be false for private instances |
iam_instance_profile | Must be attached for proper permissions |
vpc_security_group_ids | Must include required security groups |
ebs_block_device | Volumes must be encrypted |
tags | Optional; can enforce Owner or Environment |
2. OPA Policy (ec2.rego
)
package terraform.ec2
default deny = []
# 1. Disallow t2.micro instances
deny[msg] {
some resource
input.resource_changes[resource].type == "aws_instance"
input.resource_changes[resource].change.after.instance_type == "t2.micro"
msg := sprintf("Instance %v uses disallowed type t2.micro", [input.resource_changes[resource].name])
}
# 2. Require EBS optimization
deny[msg] {
some resource
input.resource_changes[resource].type == "aws_instance"
not input.resource_changes[resource].change.after.ebs_optimized
msg := sprintf("Instance %v is not EBS optimized", [input.resource_changes[resource].name])
}
# 3. Disallow public IP
deny[msg] {
some resource
input.resource_changes[resource].type == "aws_instance"
input.resource_changes[resource].change.after.associate_public_ip_address
msg := sprintf("Instance %v has a public IP assigned", [input.resource_changes[resource].name])
}
# 4. Require IAM role
deny[msg] {
some resource
input.resource_changes[resource].type == "aws_instance"
not input.resource_changes[resource].change.after.iam_instance_profile
msg := sprintf("Instance %v does not have an IAM role attached", [input.resource_changes[resource].name])
}
# 5. Require Security Groups
deny[msg] {
some resource
input.resource_changes[resource].type == "aws_instance"
sg_ids := input.resource_changes[resource].change.after.vpc_security_group_ids
count(sg_ids) == 0
msg := sprintf("Instance %v does not have any Security Groups attached", [input.resource_changes[resource].name])
}
# 6. EBS volumes must be encrypted
deny[msg] {
some resource
input.resource_changes[resource].type == "aws_instance"
volume := input.resource_changes[resource].change.after.ebs_block_device[_]
not volume.encrypted
msg := sprintf("Instance %v has unencrypted volume %v", [input.resource_changes[resource].name, volume.device_name])
}
# 7. Optional: Require Owner tag
deny[msg] {
some resource
input.resource_changes[resource].type == "aws_instance"
tags := input.resource_changes[resource].change.after.tags
not ("Owner" in tags)
msg := sprintf("Instance %v does not have an 'Owner' tag", [input.resource_changes[resource].name])
}
3. Python Script to Call OPA
import subprocess
import json
plan_file = "ec2-plan.json"
policy_file = "policy/ec2.rego"
cmd = [
"opa", "eval",
"-i", plan_file,
"-d", policy_file,
"data.terraform.ec2.deny",
"--format", "json"
]
result = subprocess.run(cmd, capture_output=True, text=True)
opa_output = json.loads(result.stdout)
violations = opa_output["result"][0]["expressions"][0]["value"]
if violations:
print("❌ Policy violations found:")
for v in violations:
print("-", v)
else:
print("✅ All policies passed.")
4. PowerShell Script to Call OPA
# -----------------------------
# PowerShell OPA Test Script for EC2 with EBS, IAM, SG
# -----------------------------
$PlanFile = "C:\OPA_EC2\ec2-plan.json"
$PolicyFile = "C:\OPA_EC2\policy\ec2.rego"
$OpaCommand = @(
"opa", "eval",
"-i", $PlanFile,
"-d", $PolicyFile,
"data.terraform.ec2.deny",
"--format", "json"
)
try {
$OpaOutputRaw = & $OpaCommand
} catch {
Write-Error "Failed to run OPA. Make sure opa.exe is in your PATH."
exit 1
}
$OpaOutput = $OpaOutputRaw | ConvertFrom-Json
$Violations = $OpaOutput.result[0].expressions[0].value
if ($Violations.Count -gt 0) {
Write-Host "❌ Policy violations found:" -ForegroundColor Red
foreach ($v in $Violations) {
Write-Host "- $v" -ForegroundColor Yellow
}
Write-Host "Aborting Terraform apply due to policy violations..." -ForegroundColor Red
exit 1
} else {
Write-Host "✅ All policies passed." -ForegroundColor Green
}
5. Expected Output for This Mock JSON
❌ Policy violations found:
- Instance aws_instance.example uses disallowed type t2.micro
- Instance aws_instance.example is not EBS optimized
- Instance aws_instance.example has a public IP assigned
- Instance aws_instance.example has unencrypted volume /dev/sda1
- Instance aws_instance.example does not have an 'Owner' tag
No comments:
Post a Comment