Cloud engineer reviewing infrastructure configuration and compliance posture across multiple monitors

Policy engine for security compliance

September 24, 2026 · 13 min read · By Nadia Kowalski

Key Takeaways:

  • Policy as code treats compliance rules as versioned text files, so they get the same review, testing, and rollback as application code.
  • Open Policy Agent, HashiCorp Sentinel, AWS Config Rules, and Azure Policy cover different layers: portable decision-making, Terraform plan-time gates, and cloud-native guardrails.
  • The highest-value integration point is the CI/CD pipeline, where a policy gate blocks non-compliant infrastructure before it is provisioned.
  • Most audit findings come from missing evidence that a control operated, not from a missing control; policy engines generate that evidence automatically.
  • Enforcement mode (blocking vs. advisory) should be staged, because a hard deny on day one breaks pipelines that have not yet been remediated.

What Policy as Code Actually Changes

Manual review cannot keep pace with cloud infrastructure change. Compliance rules written as code are stored as text files in version control. HashiCorp defines this as “writing code in high-level language to manage and automate policies,” allowing teams to apply version control, automated testing, and automated deployment to governance rules just like application code (HashiCorp Sentinel documentation). A rule entered manually in a console lacks diff history, review steps, or test suites. A rule stored as a file can be proposed in a pull request, reviewed by a security engineer, tested against sample inputs, and rolled back if it causes issues in production.

CI/CD Integration Patterns

Three enforcement points are important. Plan-time gates run inside the pipeline before infrastructure is created. Admission control runs when a resource is submitted, blocking non-compliant objects from creation. Continuous evaluation runs after deployment, detecting drift and generating the audit trail. A program that only runs continuous evaluation identifies problems but does not prevent them.

Open Policy Agent: Portable Decision-Making

Open Policy Agent (OPA) is an open source, general-purpose policy engine that unifies policy enforcement across the stack and is a graduated CNCF project, according to the OPA documentation. Its defining feature is separating decision-making from enforcement: software queries OPA with structured input, OPA evaluates it against policies and data, and returns a decision. The application then determines how to act on that decision.

Open Policy Agent: Portable Decision-Making
Open Policy Agent: Portable Decision-Making, architecture diagram

Policies are written in Rego, a declarative language for rules over hierarchical data structures. The OPA documentation lists decisions a policy can express: which roles a user needs to access a resource, which network ranges egress traffic may reach, which container registries are allowed as image sources, and which system calls a binary inside a container may make. Decisions are not limited to allow/deny; a policy can return arbitrary structured data.

Portability explains why OPA is used in many environments. The same engine evaluates Kubernetes admission requests, API gateway calls, and CI/CD pipeline checks, so a rule is written once and applied in three places. The trade-off is that Rego has a learning curve, and teams treating it as a general-purpose programming language face challenges. Another limitation is that OPA only makes decisions. It does not remediate or store evidence by default. Those tasks belong to the integrating system, so an OPA deployment without a logging layer produces decisions that leave no audit trail. For compliance, the log is the required deliverable.

HashiCorp Sentinel: Guardrails at Plan Time

HashiCorp Sentinel is a policy-as-code framework embedded in HashiCorp products, with documented integrations for Terraform, Vault, and Consul. While OPA is a general engine integrating with many systems, Sentinel is designed specifically for the HashiCorp toolchain, with its tightest integration in Terraform. Sentinel evaluates a Terraform plan before infrastructure is applied, so a non-compliant resource is blocked before it exists rather than flagged after it is running.

Sentinel uses its own language rather than Rego; the documentation notes every Sentinel-enabled application shares the same policy language, so a rule written for Terraform has the same syntax as one written for Vault. Sentinel includes a local CLI for developing and testing policies, intended for a developer’s daily workflow and CI. Policies are validated with a test framework designed for automation, so a pipeline can verify a policy still behaves as expected before deployment.

The limitation is scope. Sentinel is not a cross-platform engine; it governs HashiCorp systems. An organization running Terraform for provisioning but AWS Config for runtime detection uses both, and the two policy languages do not share syntax.

AWS Config Rules and Azure Policy: Cloud-Native Enforcement

AWS Config Rules define the ideal configuration of AWS resources and continuously check whether deployed resources match it. The AWS Config documentation divides rules into managed rules, which are predefined and customizable, and custom rules. Custom rules come in two forms: Lambda rules written in Java or Python, and custom policy rules written in Guard, AWS’s policy-as-code language, which avoids writing and deploying a function.

Two AWS Config features matter for compliance programs. Conformance packs bundle Config rules and remediation actions into a single deployable entity, authored as a YAML template and deployable in one account and Region or across an organization in AWS Organizations, according to the conformance packs documentation. That packaging turns individual rules into a framework-aligned control set deployed and versioned as a unit. Proactive evaluation is the second: it lets a rule assess resources before provisioning, moving AWS Config from detection toward prevention.

Cost management is important with Config. The documentation warns that deleting rules creates configuration items for the resource compliance type, which can increase recorder costs, and recommends excluding that resource type from recording before deleting rules. It also recommends scoping custom Lambda rules to specific resource types, because an unscoped rule invokes the function for every resource in the account.

Azure Policy uses a different structure. A policy definition is JSON containing a display name, description, mode, parameters, and a policy rule built from an if condition and a then effect, according to the Azure Policy definition structure documentation. The effect determines what happens on a match; the effects documentation lists options including deny, audit, append, modify, deployIfNotExists, and auditIfNotExists.

Evaluation order determines behavior. Azure Policy checks disabled first, then append and modify, then deny, then audit, then manual, then auditIfNotExists, and last denyAction. Evaluating deny before audit prevents double logging of an undesired resource. Layering multiple assignments produces a cumulative most-restrictive result, so overlapping deny policies block a resource even if another assignment would allow it.

Engine Policy language Primary enforcement point Documented integrations
Open Policy Agent Rego Decoupled decision API queried by the integrating system Microservices, Kubernetes, CI/CD pipelines, API gateways
HashiCorp Sentinel Sentinel language Terraform plan evaluation before apply Terraform, Vault, Consul
AWS Config Rules Guard (custom policy rules) or Java/Python (Lambda rules) Continuous resource evaluation; proactive evaluation before provisioning Managed rules, custom rules, conformance packs, AWS Organizations
Azure Policy JSON policy definitions Resource create/update request evaluation Azure Resource Manager, Azure Kubernetes Service, Azure Arc-enabled Kubernetes, Key Vault

Sources: OPA documentation, Sentinel documentation, AWS Config documentation, Azure Policy documentation.

Policy Examples for Common Compliance Requirements

Compliance frameworks translate into a small number of recurring technical rules. The value of a policy engine is that each rule is written once and enforced wherever it applies.

Encryption at rest is the most common requirement, and the control GDPR, SOC 2, and ISO 27001 auditors all ask about. In Azure Policy, this is a definition whose if condition checks a storage account’s encryption field and whose then effect is deny for new resources and audit for existing ones. In AWS Config, it is a managed rule evaluating whether each bucket has default encryption enabled. Both produce the same outcome in different clouds: the control is defined once conceptually and implemented per platform.

Restricting resource deployment to approved regions is the second recurring rule, driven by data residency obligations. The Azure Policy documentation includes an example: an “Allowed locations” definition with a parameter listing permitted locations and a deny effect blocking deployment elsewhere. The same intent in Terraform is a Sentinel policy inspecting the plan and rejecting any resource whose region attribute falls outside an allowlist.

Tagging enforcement is the third, and it makes every other control auditable. A resource without an owner tag cannot be routed to the right team when it fails a check. Azure Policy’s append and modify effects can add or correct tags during resource creation; the documentation notes append and modify are evaluated before deny, so a tag can be added before the resource is assessed against other rules. AWS Config evaluates tag compliance through managed rules, though the documentation warns that resources which do not support tagging or do not return tag data in their describe API will not have tag data captured, breaking tag-based compliance evaluation for that resource type.

Network exposure rules complete the set. The OPA documentation uses a network security example directly: servers reachable from the internet must not expose the insecure http protocol, and no server may expose telnet. That rule is expressible in Rego against a JSON representation of the infrastructure, and the same logic applies to a security group rule in AWS or a network security group in Azure.

Cloud engineer reviewing infrastructure configuration and compliance status across monitoring screens
Configuration review moves from a manual checklist to a continuous evaluation loop once policies run as code.

CI/CD Integration Patterns

The CI/CD pipeline is the point where policy as code stops non-compliant resources before they exist. Three patterns cover most implementations.

The first is the plan-time gate. Sentinel evaluates Terraform plans, so a policy decision happens between plan and apply. A pipeline step runs the policy check, and a failure halts the run before any infrastructure is created. This prevents the problem rather than reporting it, which is why Sentinel’s documentation describes policy as providing “guardrails for other automated systems.”

The second is the configuration-file gate. OPA can be invoked against arbitrary structured input, including Kubernetes manifests, Terraform JSON plans, and CI configuration files. A pipeline step submits the file to OPA and fails the build if the decision is a denial. The advantage over plan-time gating in a specific tool is portability: the same policy set evaluates manifests for Kubernetes, Helm charts, and Terraform in one pipeline, so a team does not maintain three rule sets.

The third is the admission controller. OPA integrates with Kubernetes admission control, so the decision happens when an object is submitted to the cluster rather than in the pipeline. This catches changes that bypass the pipeline entirely, including manual kubectl apply commands and drift introduced by other automation. Azure Policy offers an equivalent through its Kubernetes resource provider mode, which the documentation describes as supporting audit, deny, and disabled effects for Azure Kubernetes Service and Azure Arc-enabled Kubernetes clusters.

These patterns work together. A pipeline gate stops the change before it reaches the cluster; an admission controller stops it at the cluster boundary. A program with only the pipeline gate misses any change that does not flow through that pipeline. A program with only the admission controller misses policy violations that never reach Kubernetes at all, such as a misconfigured database or storage account. Testing policies before deploying them is the step teams often skip: Sentinel includes a test framework designed for automation, and the documentation states its CLI can verify policies before deploying them to a system.

Enforcement Modes and Rollout Sequencing

The most common implementation mistake is deploying policies in blocking mode from day one. A deny effect on an existing estate will block deployments that have not yet been remediated, usually leading to emergency exemptions that weaken the program. The effective approach is a staged progression through three modes.

  • Audit only. Deploy every rule with a non-blocking effect and collect findings for two to four weeks. The output is a prioritized list of what would break, ranked by how many resources each rule affects.
  • Warn and notify. Keep the rule non-blocking but route findings to the owning team with a remediation deadline. This is where the tag-based ownership model helps, because a finding without an owner is a finding nobody fixes.
  • Enforce on new resources. Switch the effect to deny for resource creation while leaving existing resources under audit. New resources are compliant by construction, and the existing estate is remediated on its own schedule.

Azure Policy supports this progression clearly because effects are parameterized with allowed values, so a single definition can be assigned in audit mode in one scope and deny mode in another. The documentation notes audit, deny, and either modify or append are often interchangeable, making the mode switch a parameter change rather than a rewrite. AWS Config separates detection from remediation, so the equivalent control is the conformance pack’s remediation actions, which can be left unconfigured during the audit phase and enabled once the estate is clean. Enforcing too early has a real cost: a deny policy that blocks a legitimate deployment during an incident forces engineers to find a workaround under time pressure, and the workaround is usually an exemption that never gets removed.

Common Pitfalls and Audit Preparation

Policy engines generate decisions, and auditors ask for evidence that controls operated. Those are different artifacts, and the gap between them causes most program failures. A Config rule evaluating resources for twelve months is a control; the compliance history showing those evaluations is the evidence. Both need to exist and be retained for the audit period.

Recurring pitfalls are consistent across engines. Policies that are too broad produce so many findings that teams stop reading them, which is effectively the same as having no policy. Policies that are too narrow miss important cases. Rules written without tests change behavior unexpectedly when a parameter is updated. Rules deployed without an owner model produce findings nobody is accountable for closing.

An effective audit preparation sequence takes about twelve weeks. Weeks one through three inventory the policy set and map each rule to the framework controls it satisfies. Weeks three through six verify every rule is actively evaluating and its output is routed to an immutable store. Weeks six through nine run a dry-run evidence review with someone outside the team, pulling the compliance history for a sample of controls. Weeks nine through twelve close gaps and document the mapping so the auditor receives a control matrix rather than a folder of exports.

The mapping exercise is where multi-framework programs save the most effort. One encryption rule can satisfy the encryption expectations of GDPR, SOC 2, and ISO 27001 at once, and one access-control rule can cover the access requirements those frameworks share. This matches our guide to enterprise compliance standards, where a shared control core across five frameworks means the same technical control produces evidence for multiple auditors. Policy as code makes that mapping mechanical rather than manual, because each rule already carries an identifier that can be tied to a control reference.

The connection to continuous monitoring is direct. Our analysis of cloud security posture management covered how misconfiguration, not novel malware, is the entry point for most cloud breaches. Policy engines prevent misconfiguration: CSPM detects drift after it happens, while a policy gate stops the misconfiguration from being created. The limitation is that policy as code governs what the engine can see. A resource type the engine does not support, a legacy system outside the cloud provider, or a manual change made directly in a console may fall outside coverage. The policy set should be reviewed against the resource inventory regularly, because coverage gaps do not announce themselves and auditors find them by asking about a system the team forgot to include.

More in-depth coverage from this blog on closely related topics:

Sources and References

Sources cited while researching and writing this article:

Nadia Kowalski

Has read every privacy policy you've ever skipped. Fluent in GDPR, CCPA, SOC 2, and several other acronyms that make people's eyes glaze over. Processes regulatory updates faster than most organizations can schedule a meeting about them. Her idea of light reading is a 200-page compliance framework, and she remembers all of it.