Enforcing AWS AMI images with OPA

In the part 2, we provided examples of how you can enforce AWS tags to ensure compliance and governance within your organization. In this post, we will extend those concepts by demonstrating how you can enforce specific AMI IDs using OPA policies, helping you maintain control over the AMI images used in your infrastructure.
It is critical for all organizations to establish a robust AMI building process to ensure that operating systems are consistently patched for security vulnerabilities. By maintaining control over the AMI lifecycle, organizations can enforce compliance, reduce exposure to risks, and ensure that their infrastructure remains secure and up-to-date. This process not only helps in mitigating potential threats but also aligns with best practices for infrastructure management and governance. This process is typically managed by the Security team, who are responsible for building and maintaining approved AMIs. The “customers” of these AMIs are other teams within the organization, such as Development, Operations, or QA, who rely on these pre-approved images to ensure their workloads meet security and compliance standards.
And again :) (I won’t stop repeating this) OPA policies enable organizations to enforce guardrails directly within their infrastructure-as-code workflows. By adopting the shift-left paradigm, teams can integrate these policies early in the development lifecycle, ensuring compliance and security from the outset. This approach moves the responsibility closer to the source code, allowing developers to validate their configurations against organizational policies before deployment.
Example OPA Policy for Enforcing AMI IDs
Below is an example of an OPA policy that can be used with Terraform to enforce the usage of pre-approved AMI IDs. This policy leverages external data to provide the list of allowed AMIs, ensuring flexibility and ease of management.
OPA Policy: allowed_amis.rego
package terraform.ami
import input as tfplan
import data.allowed_ami_ids
resource_to_check = [
"aws_instance"
]
array_contains(arr, elem) {
arr[_] == elem
}
evaluated_resources[r] {
r := tfplan.resource_changes[_]
array_contains(resource_to_check, r.type)
}
deny[reason] {
resource := evaluated_resources[_]
action := resource.change.actions[count(resource.change.actions) - 1]
array_contains(["create", "update"], action)
ami := resource.change.after.ami
not array_contains(allowed_ami_ids, ami)
reason := sprintf(
"%s: is using not allowed AMI - %s. Allowed AMI ids are %v. Make sure to pick the right one for your platform!",
[resource.address, ami, allowed_ami_ids]
)
}
External Data: allowed_amis.json
{
"allowed_amis": [
"ami-12345678",
"ami-87654321",
"ami-abcdef12"
]
}
Integrating with Terraform Plan
To use this policy with Terraform, you can evaluate the plan file against the OPA policy using the opa eval
command:
In previous posts, we demonstrated how to use conftest
as a CLI tool to validate policies against Terraform configurations. However, in this example, we will showcase how the same validation can be performed directly using the opa
CLI. This approach provides flexibility for teams who prefer working directly with OPA or need to integrate it into custom workflows.
terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > tfplan.json
opa eval --data allowed_amis.json --input tfplan.json --bundle allowed_amis.rego --format pretty
This setup ensures that any Terraform configuration using unapproved AMI IDs will be flagged, helping maintain compliance and governance across your infrastructure.
The allowed_amis.json
file can be dynamically generated to reflect the specific requirements of each environment. For instance, if your organization has an AMI building process that publishes newly created and approved AMI IDs to a company configuration management database (CMDB), you can retrieve these IDs based on environment, team, or other criteria. This approach ensures that the policy remains flexible and tailored to the unique needs of different parts of your infrastructure, while maintaining compliance and governance standards.
Conclusion
While enforcing AMI compliance for new deployments is crucial, addressing existing EC2 instances running outdated AMIs is equally important. A potential solution involves implementing a reporting mechanism using an AWS Lambda function. This function can periodically scan your EC2 instances, identify those using old AMIs, and send notifications via Amazon SNS or email. If certain thresholds are met (e.g., multiple notifications ignored), the Lambda function can terminate or at least stop the non-compliant instances. Additionally, integrating this data into Grafana dashboards can provide visibility into AMI compliance trends, helping teams proactively manage and remediate issues.