The "just give them admin and let them choose" approach to cloud dev access is a budget killer. I've seen $200/month workloads launched on $2k/month instances because a dev picked the first familiar SKU.
Force a menu. Here's how we did it with Terraform for AWS EC2, adaptable to Azure/GCP.
Define a local map of approved types per environment. No `t2.micro` in prod, no `r5.24xlarge` in dev.
```hcl
locals {
approved_instance_types = {
development = ["t3.small", "t3.medium", "m5.large"]
staging = ["t3.medium", "m5.large", "m5.xlarge"]
production = ["m5.large", "m5.xlarge", "c5.2xlarge", "r5.xlarge"]
}
environment = terraform.workspace
}
```
Then, validate at plan time using a variable.
```hcl
variable "instance_type" {
description = "Instance type must be from the approved list for the environment."
type = string
}
validation {
condition = contains(local.approved_instance_types[local.environment], var.instance_type)
error_message = "Instance type ${var.instance_type} not approved for ${local.environment}. Choose from: ${join(", ", local.appropped_instance_types[local.environment])}."
}
```
Key points:
* The list is maintained by platform/FinOps team.
* Validation fails fast, during `terraform plan`.
* Combine with policy-as-code (OPA, Sentinel) for extra enforcement.
* Update the map quarterly based on cost analysis and new gen types.
Result: cut dev/test environment waste by ~40% in one quarter because random oversized picks were eliminated.
—cp
—cp
This is a solid approach, especially the use of the validation block to stop a plan dead in its tracks if the rule isn't met. I've been looking for a pattern like this to apply to NetSuite SuiteCloud projects, where you often need to lock down which roles or permissions are assignable in a sandbox to prevent accidental over-provisioning. The principle translates.
I'm curious about the maintenance side, though. Who owns the `approved_instance_types` map in your setup, and how often is it reviewed? In a fast-moving environment, I could see a dev needing a genuinely different instance type for a one-off data processing job and getting blocked by this, leading to a workaround like a separate, ungoverned project. Do you have a process for temporary exceptions, or is the map updated frequently enough to accommodate new use cases?
> "The list is maintained by platfo"
Right, so now instead of a dev picking the wrong instance, you've got a platform team that's going to forget to update the list when a new generation comes out. Then everyone's stuck on t3.small while t4g is half the price and twice the performance. Classic.
I've seen this pattern play out. The validation block works great until someone needs a g4dn for a quick ML spike and the ticket to add it to the approved list takes three days. What happens then? They spin up a separate project with full admin because "it's just for testing." Now you've got ungoverned resources anyway, just with extra steps.
The real question nobody wants to answer is: are you solving for cost, or for control? A $2k/month instance running 24/7 is a billing problem, not a choice problem. If you gave devs a budget alert and a chargeback dashboard, they'd probably pick the right size themselves. But that would require trusting them, which is apparently harder than writing Terraform validators.
cg