SPB Git

Repository Public

service-accounts.md 12.0 KB 285 lines markdown

1# Manage service accounts with Terraform23> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.45An OpenAI service account is a nonhuman identity owned by a project. Terraform can create the account without a default role, define a least-privilege permission bundle, and assign that bundle through a group. Create and manage service-account API keys outside Terraform through the Administration API.67This guide follows a typical service-account onboarding workflow:891. Create a service account without a default project role or API key.102. Assign a custom project role through a group, granting only the permissions the workload needs.113. Create a scoped API key and store it in your secrets manager.1213## Before you begin1415Complete the [Terraform provider setup](https://developers.openai.com/api/docs/guides/terraform), export an Admin API key as `OPENAI_ADMIN_KEY`, and export the existing project's ID as `PROJECT_ID`.1617Use a test organization when evaluating service-account creation, import, replacement, and deletion.1819## Create a service account without a default role2021Create the service account with Terraform:2223```terraform24resource "openai_project_service_account" "application" {25  project_id = "proj_123"26  name       = "example-application-development-service-account"27}2829output "service_account_id" {30  value = openai_project_service_account.application.service_account_id31}32```3334Replace `proj_123` with the ID of the existing project that will own the service account.3536The provider creates the service-account identity without generating an API key or assigning a default project role. Terraform stores the service-account ID and other nonsensitive metadata in state. At this stage, the service account has no project permissions.3738## Assign least-privilege permissions3940Define a custom project role with only the permissions the workload requires. Create a group, add the service account to it, and assign the role to the group. This example allows group members to create responses:4142```terraform43resource "openai_project_role" "application" {44  project_id  = openai_project_service_account.application.project_id45  role_name   = "Application response writer"46  description = "Allows the application to create responses"47  permissions = ["api.responses.write"]48}4950resource "openai_group" "application_access" {51  name = "example-application-development-access"52}5354resource "openai_group_user" "application" {55  group_id = openai_group.application_access.group_id56  user_id  = openai_project_service_account.application.id57}5859resource "openai_project_group_role" "application_access" {60  project_id = openai_project_service_account.application.project_id61  group_id   = openai_group.application_access.group_id62  role_id    = openai_project_role.application.role_id63}64```6566The `openai_project_role` resource defines the least-privilege permission bundle, `openai_group_user` adds the service account to the group, and `openai_project_group_role` assigns the role to that group. Every service account added to the group inherits the same project role. Replace `api.responses.write` with the smallest set of permissions approved for your workload. See [Projects and access](https://developers.openai.com/api/docs/guides/terraform/projects-and-access) for more information about group-based project access.6768Review and apply the configuration:6970```bash71terraform plan72terraform apply73```7475Don't assign the built-in `member` or `owner` role when a custom project role76  provides the permissions your workload needs. Keep access limited to the77  approved permission bundle.7879## Create a scoped API key8081After applying the Terraform configuration, create an API key through the [Create project service account API key](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/projects/subresources/service_accounts/subresources/api_keys/methods/create) endpoint. The API returns the key's full value only once, so protect the response file before making the request:8283```bash84SERVICE_ACCOUNT_ID="$(terraform output -raw service_account_id)"85umask 0778687curl -X POST \88  "https://api.openai.com/v1/organization/projects/$PROJECT_ID/service_accounts/$SERVICE_ACCOUNT_ID/api_keys" \89  -H "Authorization: Bearer $OPENAI_ADMIN_KEY" \90  -H "Content-Type: application/json" \91  -d '{92    "name": "Production App",93    "scopes": ["api.responses.write"]94  }' \95  --output service-account-api-key.json96```9798Choose the narrowest scopes the workload needs. API-key scopes can further restrict the service account's permissions, but they can't grant permissions outside its assigned project role.99100Pass the `value` from `service-account-api-key.json` to your approved secrets-manager workflow without printing it. After your secrets manager stores and verifies the secret, remove the response file:101102```bash103rm service-account-api-key.json104```105106Treat `service-account-api-key.json` as a secret for as long as it exists. Don't commit it, write the key to Terraform configuration, expose it through a Terraform output, or pass it as a Terraform variable.107108The [API reference](https://developers.openai.com/api/reference/resources/admin/subresources/organization/subresources/projects/subresources/service_accounts/subresources/api_keys/methods/create) includes the response shape and language-specific examples. Workloads that support [workload identity federation](https://developers.openai.com/api/docs/guides/workload-identity-federation) can use the same service account and least-privilege role without creating an API key.109110## Import an existing service account111112You don't need to import a service account that Terraform created. To adopt a service account created outside Terraform, declare it with the same project ID and name:113114```terraform115resource "openai_project_service_account" "application" {116  project_id = "proj_123"117  name       = "example-application-development-service-account"118}119```120121Import the existing identity before running a normal apply:122123```bash124SERVICE_ACCOUNT_ID="<existing-service-account-id>"125126terraform import \127  openai_project_service_account.application \128  "$PROJECT_ID/$SERVICE_ACCOUNT_ID"129130terraform plan131```132133The first plan after import should propose no changes to the service account. If it proposes replacement, make the configured name and project match the existing account before applying.134135Import doesn't recover or store an API key, change the service account's existing project role, or import its group membership. Declare and import the existing `openai_project_role`, `openai_group`, `openai_group_user`, and `openai_project_group_role` resources if Terraform should manage them. The workload continues to read any existing secret from your secrets manager.136137Import the service account before applying the resource declaration. If you138  apply first, Terraform creates a different service account instead of adopting139  the existing identity.140141## Recover or rotate credentials142143The full API-key value is available only in the API-key create response. Later project API-key retrieval returns a redacted value, so you can't recover a lost key.144145Replace a lost or rotating credential without interrupting the workload:1461471. Declare the replacement as a new `openai_project_service_account` resource, using a different Terraform resource name from the old account.1482. Apply the configuration to create the replacement service account.1493. Add the replacement to the existing group with `openai_group_user` so it inherits the least-privilege project role.1504. Create an API key for the replacement through the Administration API and store the key with your approved secrets-manager workflow.1515. Deploy the replacement key and verify the workload with the replacement account.1526. Remove the old `openai_project_service_account` and its `openai_group_user` resource from the Terraform configuration. Keep the role, group, and group role assignment that the replacement service account still uses.1537. Review and apply the plan that deletes the old service account and its group membership, then run `terraform plan` and require a no-op result.154155Deleting an `openai_project_service_account` resource deletes the remote service account. Require explicit review for that change, especially while the old credential is still serving traffic.156157For broader state adoption and removal behavior, see [Import and reconciliation](https://developers.openai.com/api/docs/guides/terraform/import-and-reconcile).158159## Run the complete example160161The focused examples use concrete values to explain service-account creation, role assignment, and API-key creation. The complete configuration replaces project-specific values and permissions with variables so you can reuse it across environments.162163Save the following configuration as `main.tf`:164165```terraform166terraform {167  required_version = ">= 1.0"168169  required_providers {170    openai = {171      source  = "openai/openai"172      version = ">= 1.0.0"173    }174  }175}176177provider "openai" {}178179variable "project_id" {180  type        = string181  description = "ID of the existing OpenAI project."182}183184variable "service_account_name" {185  type        = string186  description = "Name of the application service account."187}188189variable "project_role_permissions" {190  type        = list(string)191  description = "Least-privilege project permissions for the application."192193  validation {194    condition     = length(var.project_role_permissions) > 0195    error_message = "Provide at least one approved project permission."196  }197}198199resource "openai_project_service_account" "application" {200  project_id = var.project_id201  name       = var.service_account_name202}203204resource "openai_project_role" "application" {205  project_id  = var.project_id206  role_name   = "Application API access"207  description = "Least-privilege permissions approved for the application"208  permissions = var.project_role_permissions209}210211resource "openai_group" "application_access" {212  name = "${var.service_account_name}-access"213}214215resource "openai_group_user" "application" {216  group_id = openai_group.application_access.group_id217  user_id  = openai_project_service_account.application.id218}219220resource "openai_project_group_role" "application_access" {221  project_id = var.project_id222  group_id   = openai_group.application_access.group_id223  role_id    = openai_project_role.application.role_id224}225226output "project_id" {227  value = var.project_id228}229230output "service_account_id" {231  value = openai_project_service_account.application.service_account_id232}233234output "group_id" {235  value = openai_group.application_access.group_id236}237238output "project_role_id" {239  value = openai_project_role.application.role_id240}241```242243Create `terraform.tfvars` with an existing project ID, a unique service-account name, and the smallest set of approved project permissions:244245```terraform246project_id           = "proj_123"247service_account_name = "example-application-development-service-account"248249project_role_permissions = [250  "api.responses.write",251]252```253254Initialize Terraform, then review and apply a saved plan:255256```bash257terraform init258terraform fmt259terraform validate260terraform plan -out=tfplan261terraform show tfplan262terraform apply tfplan263```264265The first plan should contain five resources to add: the service account, its custom project role, the group, the group membership, and the group role assignment. Run `terraform plan` again to confirm that the configuration produces no further changes.266267Create the service-account API key outside Terraform:268269```bash270PROJECT_ID="$(terraform output -raw project_id)"271SERVICE_ACCOUNT_ID="$(terraform output -raw service_account_id)"272umask 077273274curl -X POST \275  "https://api.openai.com/v1/organization/projects/$PROJECT_ID/service_accounts/$SERVICE_ACCOUNT_ID/api_keys" \276  -H "Authorization: Bearer $OPENAI_ADMIN_KEY" \277  -H "Content-Type: application/json" \278  -d '{279    "name": "Production App",280    "scopes": ["api.responses.write"]281  }' \282  --output service-account-api-key.json283```284285Move the returned API-key value into your approved secrets manager, then delete `service-account-api-key.json`. Don't store the key in Terraform configuration, state, or outputs.
Files main
Menu

Theme