OpenAI Terraform provider: import API resources without destructive drift
Quick answer
OpenAI released its official Terraform provider on July 29, 2026. It manages API Platform administration resources such as projects, users, groups, roles, access assignments, service accounts, certificates, project rate limits, spend alerts, model permissions, hosted-tool permissions, and data-retention settings.
Do not begin by translating a live dashboard into Terraform and running apply. First decide which system owns each resource. Read externally owned objects through data sources; import only the objects Terraform will own. Make the configuration match current remote settings, review a saved import plan, apply it, and require the next terraform plan to report no changes. Only then introduce an intentional change.
The provider is not a model-deployment tool and does not configure ChatGPT or signed-in Codex settings. It governs OpenAI API Platform resources through the Administration API. Keep that boundary explicit so a Terraform migration does not get mixed with a Codex model retirement migration or application-level model routing.
Who this is for
This guide is for a small platform team or independent developer who already has OpenAI API projects, identities, limits, or controls in the dashboard and wants reproducible infrastructure without recreating production resources. It is also useful when a second environment needs the same least-privilege policy and cost controls.
The provider requires Terraform 1.0 or later and an OpenAI Admin API key. Declarative import blocks require Terraform 1.5 or later. Evaluate the workflow in a test organization or project before adopting live resources.
Choose ownership before writing resources
Use three ownership lanes. Mixing them is the fastest way to turn configuration drift into an accidental deletion or duplicate identity.
| Lane | Use when | Terraform action | Acceptance condition |
|---|---|---|---|
| Observe | Another system owns the object | Read it with a data source | Plan has no lifecycle action |
| Adopt | Terraform will become the owner | Declare current values and add an import block | First post-import plan is a no-op |
| Create | The object does not exist and Terraform should own it | Declare a new resource | Saved plan contains only approved additions |
For example, keep a SCIM-managed group as a data source rather than importing it and making Terraform responsible for membership. Import a project role only when Terraform will own its permission set. Create a new service account only when you have a credential-delivery and rotation path ready.
A seven-stage adoption workflow
1. Inventory remote truth and owners
Record every project, group, role, assignment, service account, rate-limit record, spend alert, model policy, hosted-tool policy, data-retention setting, and certificate in scope. For each object, capture its stable ID, current settings, business owner, current control plane, and desired Terraform lane.
Do not infer ownership from visibility in the OpenAI dashboard. A group may be synchronized from an identity provider; a rate-limit record is created by OpenAI and only updated by Terraform; a service-account key may live in a separate secrets manager.
2. Pin the provider and separate credentials
Use the official openai/openai provider and commit .terraform.lock.hcl so later runs select the reviewed provider version. Supply the Admin API key through OPENAI_ADMIN_KEY, not in the provider block or a checked-in variable file.
terraform {
required_version = ">= 1.5"
required_providers {
openai = {
source = "openai/openai"
version = "~> 1.0"
}
}
}
provider "openai" {}
The Admin API key is an administrative credential, not an application key. Run Terraform in a protected environment, restrict log and plan access, and give state storage encryption, locking, access control, versioning, and recovery equal attention.
3. Declare the remote object exactly as it exists
Start with one low-impact resource. Use its current name and relationships, then attach the documented import ID. A project uses its project ID; a project service account uses /; assignments use composite IDs.
resource "openai_project" "existing" {
name = "existing-project"
}
import {
to = openai_project.existing
id = "proj_123"
}
Do not apply a resource declaration before importing the existing object. For a service account, that ordering would create a second identity instead of adopting the live one.
4. Require an import-only saved plan
terraform init
terraform fmt -check
terraform validate
terraform plan -out=tfplan
terraform show tfplan
The saved plan should show imports without remote updates. If it proposes a rename, replacement, permission change, or deletion, stop and make the configuration match remote truth. Apply the reviewed plan, then run terraform plan again. The adoption gate is a no-op result, not merely a successful import command.
For automation, terraform plan -detailed-exitcode returns 0 for no changes, 2 when changes exist, and 1 for an error. Treat 2 as evidence requiring review, not as a failed shell command to suppress.
5. Add one intentional guardrail
After the no-op baseline, introduce one bounded change: a least-privilege project role, an explicit model allowlist, one hosted-tool policy, a rate-limit adjustment, or a spend alert. Save the plan and verify the resource ID, project ID, additions, removals, and replacement markers.
Do not confuse a spend alert with a hard cap. The provider's spend-alert resource sends a notification; it does not stop requests. Pair alerts with the OpenAI hard-spend-limit runbook when traffic interruption is an intended safety boundary.
6. Keep service-account keys outside Terraform
The provider creates a service-account identity without a default role or API key. Assign a narrow custom project role, then create the scoped API key through the Administration API and deliver it directly to an approved secrets manager. The full key is available only in the create response.
Never place the key in Terraform configuration, variables, outputs, or state. Importing a service account does not recover its existing key. For rotation, create a replacement identity, grant the same narrow role, issue and verify the new secret, move the workload, revoke the old key, and only then delete the old identity.
7. Test drift and every removal behavior
Make one harmless dashboard change in a test project, run a plan, and prove that the drift is visible. Decide whether the remote change is authoritative or should be reverted, reconcile it, and require another no-op plan.
Then test removal semantics. Removing a block does not mean the same thing for every OpenAI resource.
| Resource removed from configuration | Remote result to expect |
|---|---|
openai_project |
Project is archived and cannot be restored |
openai_project_service_account |
Service account is deleted |
| Role, group, membership, or assignment | Managed object or assignment is deleted |
openai_project_model_permissions |
Model-permission configuration is deleted |
| Project rate limit, hosted-tool permissions, or data retention | Resource leaves Terraform state; remote setting is not reset |
This is the critical rollback boundary. A state-only removal can leave an old policy active; a project removal is irreversible archival. Require an explicit removal matrix in review instead of relying on the generic word destroy.
Eight rollout gates
| Gate | Required evidence |
|---|---|
| Scope | Inventory names the exact organization, projects, and resource types |
| Ownership | Every object is marked observe, adopt, or create |
| Provider | Source and lock file pin the reviewed official provider version |
| Credential | Admin key and workload keys never enter configuration, outputs, logs, or state |
| Import | Saved plan contains imports with no remote mutation |
| Baseline | First post-import plan is a no-op |
| Change | The first real change is bounded to one approved guardrail |
| Recovery | Drift, rotation, state recovery, and each removal behavior are rehearsed |
Keep a compact adoption record:
organization: org_redacted
project: proj_redacted
provider: openai/openai
provider_version: 1.x.y
ownership_lane: adopt
import_id: proj_redacted
import_plan_remote_updates: 0
post_import_plan_exit_code: 0
credential_in_state: false
first_intentional_change: project_model_allowlist
removal_behavior_reviewed: true
verdict: pass
Common mistakes
Treating every visible object as Terraform-owned. Use data sources for resources controlled by SCIM, another IaC stack, or an approved manual process.
Applying before importing. A declaration for an existing service account can create a duplicate identity when import has not happened first.
Assuming state removal resets the platform. Rate limits, hosted-tool permissions, and data-retention resources can leave the remote setting in place after they leave state.
Putting a workload API key into Terraform. The official workflow deliberately creates service-account keys outside Terraform. Preserve that separation.
Using Terraform model permissions as application routing. Project allowlists define what may be used; the application still chooses a model. Compare application choices separately in the GPT-5.6 model guide.
FAQ
Can the OpenAI Terraform provider deploy models?
No. It manages OpenAI API Platform administration resources and project controls. It can restrict which models a project may use, but it does not deploy a model or choose the model for an application request.
Should I import every existing OpenAI resource?
No. Import only resources Terraform will own. Use data sources when another system remains authoritative, and leave unsupported or deliberately manual resources in an approved inventory.
Is a successful import enough to start changing production?
No. The first plan after import should show no changes. That no-op proves the configuration describes remote truth before you introduce an intentional update.
Does removing a resource from configuration roll it back?
Not reliably. The effect is resource-specific: projects are archived, service accounts are deleted, while some project controls only leave Terraform state and remain active remotely. Review the provider documentation and a saved destroy plan for each type.
Sources
- OpenAI API changelog: official Terraform provider
- OpenAI Terraform provider overview
- Manage projects and access
- Manage service accounts
- Rate limits and spend
- Model, tool, and data controls
- Import and reconcile resources
- Official provider repository
Originally published on IndieSeek.
Top comments (0)