DEV Community

Ahab
Ahab

Posted on • Originally published at indieseek.co

OpenAI Terraform provider: import API resources without destructive drift.

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" {}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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


Originally published on IndieSeek.

Top comments (0)