OpenAI 服务账户是归属于项目的非人类身份。Terraform 可以创建不带默认角色的账户,定义遵循最小权限原则的权限集合,并通过组分配该权限集合。请在 Terraform 之外通过管理 API 创建和管理服务账户的 API 密钥。
本指南按照典型的服务账户接入流程展开:
- 创建不带默认项目角色或 API 密钥的服务账户。
- 通过组分配自定义项目角色,仅授予工作负载所需的权限。
- 创建限定权限范围的 API 密钥,并将其存储在您的机密管理器中。
开始之前
完成 Terraform 提供程序设置,将管理 API 密钥导出为 OPENAI_ADMIN_KEY,并将现有项目的 ID 导出为 PROJECT_ID。
评估服务账户的创建、导入、替换和删除操作时,请使用测试组织。
创建不带默认角色的服务账户
使用 Terraform 创建服务账户:
resource "openai_project_service_account" "application" {
project_id = "proj_123"
name = "example-application-development-service-account"
}
output "service_account_id" {
value = openai_project_service_account.application.service_account_id
}
将 proj_123 替换为该服务账户将归属的现有项目的 ID。
提供程序会创建服务账户身份,但不会生成 API 密钥或分配默认项目角色。Terraform 会将服务账户 ID 和其他非敏感元数据存储在状态中。此时,服务账户没有任何项目权限。
按最小权限原则分配权限
定义一个仅包含工作负载所需权限的自定义项目角色。创建一个组,将服务账户添加到组中,再将该角色分配给组。以下示例允许组成员创建响应:
resource "openai_project_role" "application" {
project_id = openai_project_service_account.application.project_id
role_name = "Application response writer"
description = "Allows the application to create responses"
permissions = ["api.responses.write"]
}
resource "openai_group" "application_access" {
name = "example-application-development-access"
}
resource "openai_group_user" "application" {
group_id = openai_group.application_access.group_id
user_id = openai_project_service_account.application.id
}
resource "openai_project_group_role" "application_access" {
project_id = openai_project_service_account.application.project_id
group_id = openai_group.application_access.group_id
role_id = openai_project_role.application.role_id
}
openai_project_role 资源定义遵循最小权限原则的权限集合,openai_group_user 将服务账户添加到组中,openai_project_group_role 则将角色分配给该组。添加到组中的每个服务账户都会继承同一项目角色。请将 api.responses.write 替换为您的工作负载获准使用的最小权限集合。有关基于组的项目访问权限的更多信息,请参阅项目与访问权限。
审查并应用配置:
terraform plan
terraform apply
如果自定义项目角色
已提供工作负载所需的权限,就不要分配内置的 member 或 owner 角色。请将访问权限限制在
已获批准的权限集合内。
创建限定权限范围的 API 密钥
应用 Terraform 配置后,通过创建项目服务账户 API 密钥端点创建 API 密钥。API 只会返回一次完整密钥值,因此请在发出请求之前保护好响应文件:
SERVICE_ACCOUNT_ID="$(terraform output -raw service_account_id)"
umask 077
curl -X POST \
"https://api.openai.com/v1/organization/projects/$PROJECT_ID/service_accounts/$SERVICE_ACCOUNT_ID/api_keys" \
-H "Authorization: Bearer $OPENAI_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Production App",
"scopes": ["api.responses.write"]
}' \
--output service-account-api-key.json
选择满足工作负载需求的最小权限范围。API 密钥的权限范围可以进一步限制服务账户的权限,但无法授予超出其已分配项目角色的权限。
将 service-account-api-key.json 中的 value 传递给您获准使用的机密管理器工作流程,不要将其打印出来。机密管理器存储并验证该机密后,删除响应文件:
rm service-account-api-key.json
只要 service-account-api-key.json 仍然存在,就必须将其视为机密。不要提交该文件、将密钥写入 Terraform 配置、通过 Terraform 输出暴露密钥,或将其作为 Terraform 变量传递。
API 参考包含响应结构和各编程语言的示例。支持工作负载身份联合的工作负载可以使用同一服务账户和最小权限角色,无需创建 API 密钥。
导入现有服务账户
您无需导入由 Terraform 创建的服务账户。要将 Terraform 之外创建的服务账户纳入管理,请使用相同的项目 ID 和名称声明该账户:
resource "openai_project_service_account" "application" {
project_id = "proj_123"
name = "example-application-development-service-account"
}
在执行常规应用操作之前,先导入现有身份:
SERVICE_ACCOUNT_ID="<existing-service-account-id>"
terraform import \
openai_project_service_account.application \
"$PROJECT_ID/$SERVICE_ACCOUNT_ID"
terraform plan
导入后的第一个计划应当不包含对服务账户的任何更改。如果计划提出替换账户,请在应用前调整配置中的名称和项目,使其与现有账户一致。
导入操作不会恢复或存储 API 密钥,不会更改服务账户现有的项目角色,也不会导入其组成员关系。如果要由 Terraform 管理现有的 openai_project_role、openai_group、openai_group_user 和 openai_project_group_role 资源,请声明并导入这些资源。工作负载会继续从您的机密管理器读取现有机密。
请先导入服务账户,再应用资源声明。如果您 先应用声明,Terraform 会创建另一个服务账户,而不是将 现有身份纳入管理。
恢复或轮换凭证
完整的 API 密钥值仅在创建 API 密钥的响应中提供。之后检索项目 API 密钥时,返回的值会经过遮蔽处理,因此您无法恢复丢失的密钥。
在不中断工作负载的情况下,替换丢失或需要轮换的凭证:
- 将替代账户声明为新的
openai_project_service_account资源,并使用与旧账户不同的 Terraform 资源名称。 - 应用配置以创建替代服务账户。
- 使用
openai_group_user将替代账户添加到现有组中,使其继承最小权限项目角色。 - 通过管理 API 为替代账户创建 API 密钥,并使用您获准使用的机密管理器工作流程存储密钥。
- 部署替代密钥,并验证工作负载在使用替代账户时是否正常运行。
- 从 Terraform 配置中移除旧的
openai_project_service_account及其openai_group_user资源。保留替代服务账户仍在使用的角色、组和组角色分配。 - 审查并应用删除旧服务账户及其组成员关系的计划,然后运行
terraform plan,并确保结果显示无需任何更改。
删除 openai_project_service_account 资源会删除远程服务账户。必须对这项更改进行明确审查,尤其是在旧凭证仍用于处理流量时。
有关将资源纳入状态管理以及从状态中移除资源的更多行为,请参阅导入与状态协调。
运行完整示例
前面的专项示例使用具体值来说明服务账户创建、角色分配和 API 密钥创建。完整配置将项目特定的值和权限替换为变量,以便您在不同环境中复用。
将以下配置保存为 main.tf:
terraform {
required_version = ">= 1.0"
required_providers {
openai = {
source = "openai/openai"
version = ">= 1.0.0"
}
}
}
provider "openai" {}
variable "project_id" {
type = string
description = "ID of the existing OpenAI project."
}
variable "service_account_name" {
type = string
description = "Name of the application service account."
}
variable "project_role_permissions" {
type = list(string)
description = "Least-privilege project permissions for the application."
validation {
condition = length(var.project_role_permissions) > 0
error_message = "Provide at least one approved project permission."
}
}
resource "openai_project_service_account" "application" {
project_id = var.project_id
name = var.service_account_name
}
resource "openai_project_role" "application" {
project_id = var.project_id
role_name = "Application API access"
description = "Least-privilege permissions approved for the application"
permissions = var.project_role_permissions
}
resource "openai_group" "application_access" {
name = "${var.service_account_name}-access"
}
resource "openai_group_user" "application" {
group_id = openai_group.application_access.group_id
user_id = openai_project_service_account.application.id
}
resource "openai_project_group_role" "application_access" {
project_id = var.project_id
group_id = openai_group.application_access.group_id
role_id = openai_project_role.application.role_id
}
output "project_id" {
value = var.project_id
}
output "service_account_id" {
value = openai_project_service_account.application.service_account_id
}
output "group_id" {
value = openai_group.application_access.group_id
}
output "project_role_id" {
value = openai_project_role.application.role_id
}
创建 terraform.tfvars,填入现有项目的 ID、唯一的服务账户名称,以及已获批准的最小项目权限集合:
project_id = "proj_123"
service_account_name = "example-application-development-service-account"
project_role_permissions = [
"api.responses.write",
]
初始化 Terraform,然后审查并应用已保存的计划:
terraform init
terraform fmt
terraform validate
terraform plan -out=tfplan
terraform show tfplan
terraform apply tfplan
第一个计划应包含五个待添加的资源:服务账户、其自定义项目角色、组、组成员关系和组角色分配。再次运行 terraform plan,确认该配置不会产生进一步更改。
在 Terraform 之外创建服务账户 API 密钥:
PROJECT_ID="$(terraform output -raw project_id)"
SERVICE_ACCOUNT_ID="$(terraform output -raw service_account_id)"
umask 077
curl -X POST \
"https://api.openai.com/v1/organization/projects/$PROJECT_ID/service_accounts/$SERVICE_ACCOUNT_ID/api_keys" \
-H "Authorization: Bearer $OPENAI_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Production App",
"scopes": ["api.responses.write"]
}' \
--output service-account-api-key.json
将返回的 API 密钥值转存到您获准使用的机密管理器中,然后删除 service-account-api-key.json。不要将密钥存储在 Terraform 配置、状态或输出中。