ArticlesJune 2, 2026

Governing OpenRouter at Scale with Terraform: Workspaces, Guardrails, and Spend Limits as Code

Created: June 2, 2026 Updated: June 2, 2026

Introduction

OpenRouter has moved from a useful integration point into shared AI infrastructure. When one team uses it, the dashboard can be enough. When multiple product teams depend on it, dashboard-only administration becomes operational risk.

The problems are familiar to any platform team:

  • API keys are created by hand and become hard to trace.
  • Spend controls live outside the review process.
  • Guardrails are easy to forget or apply inconsistently.
  • Workspace conventions drift across teams and environments.
  • Existing state is difficult to inspect from the same workflow that provisions new access.

The cloudopsworks/openrouter Terraform/OpenTofu provider exists to close that gap. It treats OpenRouter as a governed management plane: workspaces, guardrails, spend-capped API keys, and inventory data sources can all move through infrastructure-as-code review instead of one-off dashboard changes.

This article is the practical follow-up to the provider's earlier release story. If you want the engineering history first, read Terraform Provider OpenRouter: From First Commit to Verified v0.2.10.

Why key-only IaC is not enough

Managing an API key as code is useful, but it is not the same as governing the platform.

A key-only workflow answers one question: "Can this service authenticate?"

A governed workflow answers better questions:

  • Which team or workspace owns the access?
  • What spend limit applies?
  • Does the key expire?
  • Which providers or models are allowed through the guardrail?
  • Can we inspect existing workspaces, keys, guardrails, organization members, and provider metadata from code?
  • Can the same pattern be reproduced across development, staging, and production?

That is the reason the provider focuses on OpenRouter's management surface instead of stopping at API-key CRUD.

The target workflow

The operating pattern is straightforward:

  1. Create or resolve a workspace for the team.
  2. Attach a guardrail to that workspace.
  3. Issue a scoped API key with spend and reset controls.
  4. Use data sources to inspect current OpenRouter state.
  5. Review all changes through the normal pull-request and release path.

The result is not just a key. It is a small governance contract that can be reviewed, versioned, reproduced, and audited.

Install the provider

Start with the provider declaration and a management key. The provider accepts the key directly or through OPENROUTER_API_KEY.

terraform {
  required_providers {
    openrouter = {
      source  = "cloudopsworks/openrouter"
      version = "~> 0.2"
    }
  }
}

variable "openrouter_management_key" {
  type      = string
  sensitive = true
}

provider "openrouter" {
  api_key = var.openrouter_management_key
}

For CI/CD, prefer injecting OPENROUTER_API_KEY from your secret store instead of committing credentials into variable files.

Create a governed workspace

A workspace is the first boundary. It gives a team or workload an explicit home instead of mixing every key into one default space.

resource "openrouter_workspace" "platform_ai" {
  name        = "Platform AI"
  slug        = "platform-ai"
  description = "Shared OpenRouter workspace for platform services"

  default_text_model       = "openai/gpt-4o"
  default_provider_sort    = "price"
  io_logging_sampling_rate = 0

  is_data_discount_logging_enabled    = false
  is_observability_broadcast_enabled  = true
  is_observability_io_logging_enabled = true
}

The exact defaults should match your organization's privacy, observability, and cost posture. The important point is that those choices now sit in a reviewed Terraform file.

Add a guardrail

Guardrails are where the workflow moves from provisioning into governance. In this example, the production guardrail is tied to the workspace, sets a monthly USD limit, restricts providers, and enforces zero-data-retention behavior.

resource "openrouter_guardrail" "platform_production" {
  name         = "platform-production"
  workspace_id = openrouter_workspace.platform_ai.id
  description  = "Production policy for platform AI services"

  limit_usd      = 500
  reset_interval = "monthly"

  allowed_providers = ["openai", "anthropic"]
  enforce_zdr       = true
}

A team can start with broad defaults and tighten the guardrail as its model-routing policy matures. The benefit of keeping it in Terraform is that every change leaves a trail.

Issue a scoped, spend-capped key

With the workspace in place, issue an API key for the workload. This example gives a backend service a monthly spend cap and includes BYOK usage in the limit calculation.

resource "openrouter_api_key" "backend_prod" {
  name         = "platform-backend-prod"
  workspace_id = openrouter_workspace.platform_ai.id

  limit                 = 200
  limit_reset           = "monthly"
  include_byok_in_limit = true

  expires_at = "2026-12-31T23:59:59Z"
}

One operational caveat matters: the raw OpenRouter key secret is only returned when the key is created. The provider marks it sensitive, but Terraform state can still contain sensitive values. Use a remote state backend with encryption and restricted access, and move the generated key into your runtime secret manager as part of the platform workflow.

Inspect existing state with data sources

Governance also depends on visibility. The provider includes data sources that let you inspect workspaces, keys, guardrails, organization membership, and provider metadata.

data "openrouter_workspaces" "all" {}

data "openrouter_api_keys" "platform_ai" {
  workspace_id      = openrouter_workspace.platform_ai.id
  include_disabled = false
}

data "openrouter_guardrails" "platform_ai" {
  workspace_id = openrouter_workspace.platform_ai.id
}

data "openrouter_organization" "current" {}

data "openrouter_providers" "all" {}

These data sources are useful for dashboards, validation checks, policy reports, and drift investigations. They also make it easier to migrate from dashboard-managed state into a code-managed model.

Scale the pattern across teams

For a single team, direct resources are clear enough. For many teams, use a map-driven pattern so every workspace follows the same contract.

variable "teams" {
  type = map(object({
    display_name      = string
    slug              = string
    monthly_limit     = number
    allowed_models    = optional(list(string))
    allowed_providers = optional(list(string), ["openai", "anthropic"])
  }))
}

resource "openrouter_workspace" "team" {
  for_each = var.teams

  name        = each.value.display_name
  slug        = each.value.slug
  description = "OpenRouter workspace for ${each.value.display_name}"
}

resource "openrouter_guardrail" "team" {
  for_each = var.teams

  name         = "${each.value.slug}-production"
  workspace_id = openrouter_workspace.team[each.key].id
  description  = "Production policy for ${each.value.display_name}"

  limit_usd         = each.value.monthly_limit
  reset_interval    = "monthly"
  allowed_models    = try(each.value.allowed_models, null)
  allowed_providers = each.value.allowed_providers
  enforce_zdr       = true
}

resource "openrouter_api_key" "team_backend" {
  for_each = var.teams

  name         = "${each.value.slug}-backend-prod"
  workspace_id = openrouter_workspace.team[each.key].id

  limit                 = each.value.monthly_limit
  limit_reset           = "monthly"
  include_byok_in_limit = true
}

This gives platform teams one place to encode the rule and each product team one small configuration entry to request access.

Store generated keys with the companion module

For teams that already use AWS Secrets Manager, the companion terraform-module-openrouter-mgmt-api-keys module wraps API-key creation and persists the generated secret immediately.

The module is designed for existing workspaces. A common pattern is:

  1. Use the provider to create or manage workspaces and guardrails.
  2. Pass each workspace slug or ID into the module.
  3. Let the module create workload keys and store them in AWS Secrets Manager using your naming convention.
module "openrouter_api_keys" {
  source = "git::https://github.com/cloudopsworks/terraform-module-openrouter-mgmt-api-keys.git?ref=v0.1.1"

  org = {
    organization_name = "acme"
    organization_unit = "platform"
    environment_type  = "production"
    environment_name  = "prod"
  }

  settings = {
    workspaces = {
      for team_key, team in var.teams : team_key => {
        workspace_slug = team.slug
        api_keys = {
          backend = {
            name_prefix           = "backend"
            limit                 = team.monthly_limit
            limit_reset           = "monthly"
            include_byok_in_limit = true
            secret = {
              name_prefix = "${team.slug}-openrouter"
              description = "OpenRouter key for ${team.display_name} backend"
            }
          }
        }
      }
    }
  }
}

Pin the module to a release tag instead of master. That keeps downstream environments reproducible and makes upgrades a deliberate change.

Terraform and OpenTofu

The same provider source address works for Terraform and OpenTofu:

source = "cloudopsworks/openrouter"

In most repositories, the workflow change is operational rather than structural: run terraform init/plan/apply or tofu init/plan/apply according to the toolchain your team has standardized. Keep provider versions pinned, keep lock files under review, and validate changes in the same branch strategy used for the rest of your infrastructure.

Production checklist

Before putting the pattern into production, make the implicit operating rules explicit:

  • Use a management key with the minimum access your process supports.
  • Keep Terraform or OpenTofu state encrypted and access-controlled.
  • Decide whether generated API keys are copied into a dedicated secret manager.
  • Define per-team monthly limits and reset intervals before rollout.
  • Use guardrails for production workspaces, not just API keys.
  • Keep imports and drift checks in the migration plan for existing dashboard-managed keys.
  • Review provider and module upgrades through pull requests.

Where to start

Use the provider when you need direct control over OpenRouter workspaces, guardrails, and API keys:

Use the companion module when your repeatable pattern is "create keys for known workspaces and persist them into AWS Secrets Manager":

And if you want to see how the provider reached its first verified public release, read the earlier article: Terraform Provider OpenRouter: From First Commit to Verified v0.2.10.

Ready to Standardize Your Cloud Operations?

Stop reinventing the wheel. Partner with Cloud Ops Works to build the engineering foundations your team needs to scale reliably.