DEV Community

Cover image for Enforcing Cloud Guardrails with Spacelift Policies: My Hands-On Test with Rego, Terraform, and AWS
Akingbade Omosebi
Akingbade Omosebi

Posted on

Enforcing Cloud Guardrails with Spacelift Policies: My Hands-On Test with Rego, Terraform, and AWS

After getting comfortable using Spacelift to automate my AWS infrastructure with Terraform, I wanted to push myself a bit further, so I decided to dig into something that often gets overlooked: Policies.

In real-world organizations, you rarely have total freedom to spin up any resource you want, any way you want. Teams need guardrails, ways to make sure people stick to the right instance types, permissions, regions, or cost limits, resources, etc. That’s where Spacelift’s policy engine comes in. And honestly? I must say, i struggled with it a lot, as part of my failure journey, but it paid off in a very satisfying way. It’s pretty interesting once you get your hands dirty.

Before we dive in deep, let me drop an architectural diagram, hopefully you would get the concept ahead (like a spoiler).

Architectural diagram

Getting Started with Policies: Templates & Manual Rego

To get my head around it, I started with Spacelift’s template policies.

Policy Templates Selection

They provide some great examples to learn from. But I didn’t stop there. I wanted to see if I could write my own Rego policies from scratch too.

My experiment?
Allow only t2.nano EC2 instances ✅
Prohibit t3.micro instances ❌

Why should any of these matter?

You may ask why? Well this matters to businesses in my opinion, and how i can fit in it?

In a real company setup, you might have policies that say:

“Developers can only launch small test instances, not production-grade ones.”
Or:
“No one is allowed to use certain instance families because they’re too expensive.”

So I wanted to see exactly how you can enforce rules like that before someone clicks "Apply", and to make it clear why something is blocked.

How I Wrote It (and Broke It)
Writing Rego for the first time is honestly a bit of a brain twist. I made plenty of mistakes! Syntax errors, logic that didn’t match what I meant, rules that blocked the wrong thing. Such as this block of code i wrote, which seems nice and logical, but is flawed somewhere.

# secondary_only_t2_nano_instances - plan

package spacelift

deny[message] {
  rc := input.resource_changes[_]

  rc.type == "aws_instance"
  rc.change.actions[_] == "create"

  instance_type := rc.change.after.instance_type

  instance_type != "t2.nano"

  message := sprintf("Only t2.nano instances are allowed. Found disallowed type '%v' at %v", [instance_type, rc.address])
}

Enter fullscreen mode Exit fullscreen mode

or this one:

# deny_t3_micro_instance - plan

package spacelift

deny[message] {
  some i
  rc := input.resource_changes[i]

  rc.type == "aws_instance"
  rc.change.actions[_] == "create"

  instance_type := rc.change.after.instance_type

  instance_type == "t3.micro"

  message := sprintf("Provisioning t3.micro instances is not allowed: %v", [rc.address])
}

Enter fullscreen mode Exit fullscreen mode

or was it when i failed trying to combine both logics into one? it checked off well logically and looked good on paper, but still failed somehow.

package spacelift

# Block if creating anything not t2.nano
deny[message] {
  rc := input.resource_changes[_]
  rc.type == "aws_instance"
  rc.change.actions[_] == "create"
  instance_type := rc.change.after.instance_type
  instance_type != "t2.nano"
  message := sprintf("Only t2.nano instances are allowed (create). Found disallowed type '%v' at %v", [instance_type, rc.address])
}

# Block if updating anything not t2.nano
deny[message] {
  rc := input.resource_changes[_]
  rc.type == "aws_instance"
  rc.change.actions[_] == "update"
  instance_type := rc.change.after.instance_type
  instance_type != "t2.nano"
  message := sprintf("Only t2.nano instances are allowed (update). Found disallowed type '%v' at %v", [instance_type, rc.address])
} 

Enter fullscreen mode Exit fullscreen mode

These are the policies i created:

Policies

Irregardness, these were failures, I did not delete them for a deliberate reason, to return and learn from my mistakes.

That’s part of why I’m documenting this: the mistakes helped me actually learn how it works.

But guess what?! When I finally got my policy working, Spacelift did exactly what it should:

Working Policy

If I tried to provision a t3.micro instance, the policy failed the plan.

Policy deny enforcded

But instead of the usual boring “Policy was denied” message, I set a custom message:

Selected policy denied by Admin, only t2.nano instances are permitted or allowed.

That little touch alone makes a huge difference, especially when teams grow and you need clear feedback instead of digital or cryptic errors.

Putting It All Together

Of course, I tested my policy by actually provisioning an EC2 instance of type t2.nano through my Terraform code and Spacelift stack. It passed the policy check, applied successfully, and spun up the instance exactly as expected.

t2.nano running

I also learned how policies are attached to stacks in Spacelift:

Attaching a policy of my choice

  • You can choose whether your policy runs on plan events, push events, or others.

Plan Policies

  • You attach the policy to the specific stack you want to enforce it on.

  • And when you don’t need it, you can detach it. Super easy!!.

Lessons & Takeaways

This project and learning taught me a lot:

  1. Rego is powerful! But you’ll probably break it a few times before you get it right. That’s normal!

  2. Clear policy messages make a big difference for teams. Explain why something failed, don’t just throw an error.

  3. Spacelift policies are a practical way for organizations to balance freedom and control. Terraform doesn’t care what you write, but your company and it's budget probably does.

If you’re getting into Terraform automation, don’t skip over policies. They’re not just “enterprise stuff”, they’re how you scale infrastructure safely. And like most things, you learn best by writing one, breaking it, and fixing it yourself.

👀 If you're lost, you may want to read my first Spacelift + Terraform story here if you missed it:
Provisioning AWS Resources with Terraform through Spacelift.io

GitHub Repo link for my spacelift project.

Next up for me, will be showing you some less coding, and more of chilled/laid back sweet stuffs about Spacelift, so you don't get nervous and run away from Rego.

Top comments (0)