Upgrade Notes
- The tested backend moves from LiteLLM 1.98.0 to exactly LiteLLM 1.104.0. The published provider source remains
registry.terraform.io/ncecere/litellm; Terraform >= 1.1.0, OpenTofu >= 1.6.0, and Go >= 1.24.0 for provider development are unchanged, and only optional write-only attributes require Terraform or OpenTofu >= 1.11.0. Published examples continue to constrain the provider to>= 2.0.1, < 3.0.0. No provider attribute is removed or retyped, and existing HCL, state, IDs, and imports remain valid, except for the new plan-time rejections listed in the next item. - Provider changes that can require action on an existing configuration:
litellm_key: assigning or changingproject_idon an existing key is now a plan-time error, because LiteLLM 1.104.0 rejects it; removeproject_idto detach, or replace the key withterraform taintand apply (this issues a new key secret).soft_budgetchanges now reach LiteLLM, which writes them to the key's budget row; whenbudget_idpoints at a shared budget, a plan-time warning says so.litellm_agent: removing a Terraform-managed secretlitellm_paramskey (for exampleapi_key) is now a plan-time error, because LiteLLM 1.104.0 keeps a stored secret when an update omits it. The check cannot see the proxy version at plan time, so it also applies to older proxies. Set it to"", or replace the agent withterraform taintand apply. The agent data sources report secret values as LiteLLM'sREDACTED_BY_LITELMplaceholder.litellm_model: pricing keys inadditional_model_infooradditional_model_info_jsonnow produce a plan-time warning (see below).litellm_prompt: refresh now removes a prompt from state when LiteLLM's database-only version history confirms it is gone, instead of failing.litellm_jwt_key_mapping: from LiteLLM 1.102.0, deleting the mapped key also deletes its mappings; the next refresh then removes the mapping from state.
- LiteLLM 1.102 through 1.104 introduced backend behavior changes that affect Terraform-managed configuration independently of this provider release. Review them before upgrading the proxy:
- Every organization endpoint, including reads, requires a LiteLLM Enterprise license. Unlicensed proxies return HTTP 403 for
litellm_organization,litellm_organization_member, and the organization data sources. - An organization
max_budgetof0now means zero allowance instead of unlimited (LiteLLM 1.103.0), and a projectmax_budgetof0blocks all spend. Omit the attribute for an unlimited budget. - stdio MCP servers are disabled unless the proxy process sets
LITELLM_ENABLE_MCP_STDIO=true; creating or switching alitellm_mcp_serverto stdio otherwise fails. - Team-admin (non proxy-admin) keys can no longer update teams or projects unless
general_settings.team_admin_editable_team_fieldsallows it. Managelitellm_teamandlitellm_projectwith a proxy-admin or org-admin key. - Pricing in
model_infois ignored from LiteLLM 1.102.0.litellm_modelnow warns at plan time about pricing keys inadditional_model_infoandadditional_model_info_json; creating or changing such a key fails at apply on LiteLLM 1.102.0 and later. Prices already stored inmodel_infokeep applying after the LiteLLM upgrade, but move them to the dedicated cost attributes oradditional_litellm_params(see thelitellm_model"Custom pricing" documentation) before changing them. Dedicated cost attributes are unaffected. - Deleting a model now removes its name from unified access groups when no other deployment serves it, and renaming a model rewrites the name in access groups and in key, team, organization, project, and user
modelsallowlists. Addlifecycle { create_before_destroy = true }to models referenced by access groups so replacements keep the name served; thelitellm_modeldocumentation describes the effects. litellm_unified_access_group.assigned_team_idsis now computed by LiteLLM: it includes every team whose ownaccess_group_idslists the group. Manage a team link from one side only; configuring the group's list without a self-linked team causes a perpetual diff. Unknown team IDs are rejected with HTTP 400.- When
config.yamldeclares a setting, runtime writes to that setting are refused, so manage router fallbacks either in the file or throughlitellm_fallback, not both.
- Every organization endpoint, including reads, requires a LiteLLM Enterprise license. Unlicensed proxies return HTTP 403 for
Security
- Deleted or regenerated keys are detected again on LiteLLM 1.104.0, which answers
/key/infofor them with an archived row instead of 404:litellm_keyandlitellm_key_blockno longer report a deleted key as present (orblocked = true), and unified access group membership checks no longer accept a deleted key's stale group list. litellm_jwt_key_mappingcan map a key bykey_hash, so neither the raw key nor Terraform >= 1.11 write-only support is needed for that path.- Absence is only inferred from exact LiteLLM signals: a prompt is treated as gone only on LiteLLM's own "No versions found" 404, and a key only on an explicit
status = "deleted". Agent, MCP, and credential secrets are still never read back into state from responses or echoed in diagnostics; new diagnostics name keys, never values.
Added
litellm_jwt_key_mappingkey_hash: Map a key by its SHA-256 management identifier (for examplelitellm_key.example.id) instead of the write-only rawkey_wo, using LiteLLM 1.104.0'stokenfield. The raw key never appears in configuration, and Terraform 1.11 write-only support is not required for this path. Changing the hash replaces the mapping; it conflicts withkey_wo/key_wo_version.tpd_limit(tokens per day): Add optionaltpd_limittolitellm_key,litellm_team,litellm_budget, andlitellm_project, and computedtpd_limitto thelitellm_key,litellm_team,litellm_budget, andlitellm_projectdata sources and thelitellm_teams,litellm_budgets, andlitellm_projectslists. LiteLLM 1.104.0 stores it with the entity's other rate limits and enforces it only for batch submissions, at key and team scope (a budget's limit reaches keys throughbudget_id); a project's value is stored but not enforced; removing it sends an explicitnullthat clears the limit. Existing state reads the new attribute as null, so no migration is needed.litellm_jwt_key_mappingissuer scope: Add optionaljwt_issuerto the resource and computedjwt_issuerto both data sources. LiteLLM 1.104.0 scopes mappings by issuer plus claim pair; changing a configured issuer replaces the mapping with the samekey_wosafeguards as a claim change, an omitted issuer preserves the existing scope, and the global scope is null.
Changed
- Release workflow: Download Go modules before the offline API contract check in the release and contract-update workflows. The check resolves imports with
GOPROXY=off, so a cold module cache made the tag build fail before any artifact was produced. - Development and release API contract: Re-pin the reproducible LiteLLM API contract from v1.98.0 to v1.104.0 (
79645770fedc7ec2627e6468d31062f20f82aecc). The exporter verifies v1.104.0's 35 lazy feature routers, excludes undocumented Starlette transport routes from OpenAPI comparison, and requires the now-publicPATCH /v2/organization/{organization_id}route in generated OpenAPI. All provider HTTP operations resolve unchanged; the 105 new upstream operations are classified in the reviewed inventory. - Documentation and diagnostics: Schema descriptions, diagnostics, and resource/data-source documentation no longer describe LiteLLM behavior as "v1.98" when LiteLLM 1.104.0 behaves the same; text that differs between the two versions now names both.
litellm_team_member_adddiagnostic summaries drop the version (for example "Unrepairable Membership-Only State" instead of "Unrepairable v1.98 Membership-Only State"); automation that matched the old summary text should match the new text. litellm_organization_memberdocumentation: LiteLLM's organization member-add endpoint (1.98.0 and 1.104.0) returns HTTP 500 wheneveruser_emailis sent anduser_iddoes not name an existing user, so the documented email-only example could never succeed. The example now creates the user first, and the limitation note states the exact condition. Provider behavior is unchanged.- Acceptance harness:
make testaccno longer stops at thekey_write_onlycase with "SMOKE_PRIVATE_ROOT: unbound variable" when run outside the upgrade matrix with Terraform 1.11 or newer.
Fixed
- Data sources and prompts on multi-worker LiteLLM 1.104.0: The
litellm_keyandlitellm_teamdata sources failed with "expected an object of strings" because LiteLLM 1.104.0 stores per-model limits insidemetadataas objects; those dedicated structured members are now omitted from themetadatamap, and all string members are unchanged.litellm_promptdestroy no longer fails with "Prompt Delete Not Confirmed", and create no longer fails with "Prompt Already Exists", when another worker's in-memory registry still serves a just-deleted database prompt, and refresh now removes a prompt from state when LiteLLM's database-only version history confirms it is gone (previously every 400/404 retained state). litellm_mcp_serverstdio diagnostic: LiteLLM 1.104.0 rejects creating a stdio server, or switching one to stdio, with HTTP 422 unless the proxy setsLITELLM_ENABLE_MCP_STDIO=true. The provider reported only a generic rejection; it now reportsstdio MCP Servers Disabledwith the remediation. The response body, which echoes request secrets, is still never shown.litellm_userandlitellm_budgetclears: Removingmax_budgetorbudget_durationfrom a user, or any scalar limit from a budget (max_budget,soft_budget,budget_duration,max_parallel_requests,tpm_limit,rpm_limit), sent nothing, so LiteLLM kept the old value: the user kept it silently, and the budget apply failed with "Provider produced inconsistent result". Updates now send the explicitnullthat LiteLLM 1.104.0 persists, which also resets the budget reset time when the duration is cleared. Usertpm_limitandrpm_limitstill cannot be cleared because LiteLLM drops those nulls.litellm_teammetadata after clearing member defaults: Once a team's member-budget defaults were cleared, LiteLLM keeps the server-ownedteam_member_budget_id, and every later metadata, tag, guardrail, or per-model limit change failed with "Team Metadata Composition Failed". LiteLLM 1.101.0 and later merge that ID back into every metadata update themselves, so on a proxy whose team rows carrytpd_limit(LiteLLM 1.103.0 and later) the provider no longer requires member defaults in the same request. On older proxies, including 1.98.0, where such an update would sever the member-budget relation, the provider keeps refusing it before any request. It never sends the ID and still verifies it on read-back.- Organization and project license diagnostics on LiteLLM 1.102.0+: LiteLLM gates every organization endpoint, including reads, behind an Enterprise license and returns HTTP 403.
litellm_organization,litellm_organization_member,litellm_project, and their data sources reported generic read, create, update, or delete failures; they now reportLiteLLM Enterprise License RequiredwithLITELLM_LICENSEguidance. The 403 is still never treated as absence, so state is unchanged. Organization and projectmax_budgetdescriptions now state that 0 means zero allowance, and the team and project documentation describes the new team-admin restrictions. litellm_keyproject, soft budget, and router settings on LiteLLM 1.104.0: LiteLLM 1.104.0 now appliesproject_idandsoft_budgeton/key/update. Assigning or changing a key's project is rejected by LiteLLM, so the provider now fails that plan before any request; removingproject_idsends the explicitnullLiteLLM requires to detach, and read-back records the detach.soft_budgetis sent only when it changes, with a plan-time warning whenbudget_idis set, because LiteLLM writes it to a possibly shared budget row.router_settingsgainsweights,optional_pre_call_checks, and theservice_unavailable_error_retries,not_found_error_retries, anddefault_retriesretry-policy fields; previously any key with these set outside Terraform failed every read.litellm_modelcredential detach and multi-worker reads on LiteLLM 1.104.0: Removinglitellm_credential_namesent an empty string, which LiteLLM 1.104.0 rejects with HTTP 400 before any write; the provider now retries that detach once with the explicitnullLiteLLM 1.104.0 requires. LiteLLM 1.98.0, which ignoresnullhere, still receives the empty string first. Post-update verification now waits, within its existing bound, for workers that still show a cleared value or briefly answer HTTP 400 while reloading the updated model, and model reads (resource refresh and thelitellm_modeldata source) tolerate HTTP 400 for about 25 seconds while workers load a newly written model; retry waits honor cancellation. A persistent 400 is still an error and never removes the model from state.litellm_agentsecret parameters on LiteLLM 1.104.0: LiteLLM 1.104.0 returns every secretlitellm_paramsvalue (for exampleapi_key) asREDACTED_BY_LITELMto all callers, proxy admins included, and keeps a stored secret when a PATCH omits it. Creating or updating an agent with a secret parameter failed with "Agent Create Not Confirmed". The provider now recognizes the marker for values of any type, keeps the configured secret, verifies every other returned value, and rejects removing a Terraform-managed secret key at plan time (set it to"", or replace the agent withterraform taintand apply;-replacealone is not enough), because LiteLLM would silently keep it.litellm_mcp_serverOAuth scopes on LiteLLM 1.104.0: LiteLLM 1.104.0 returnscredentials.scopes(a list) andcredentials.upstream_token_headerto full proxy admins. The provider requiredcredentialsto be a string map, so every create, read, import, and destroy of a server withoauth_scopesfailed, and thelitellm_mcp_serverdata source rejected any projection other than a loneupstream_resource. Both now accept LiteLLM's documented projection, the resource verifiesoauth_scopesafter create and scope changes when LiteLLM returns them, and a configuredcredentials.upstream_token_headeris verified likeupstream_resource.- Deleted keys on LiteLLM 1.104.0: LiteLLM 1.104.0 answers
/key/infofor a deleted or regenerated key with HTTP 200 and the archived row (status = "deleted") instead of 404.litellm_keyandlitellm_key_blocktherefore never noticed a key deleted outside Terraform (a block on a deleted key stayed in state, possibly still reportingblocked = true), thelitellm_keydata source read deleted keys, and unified access group membership verification could accept a deleted key's stale access groups. Every/key/infoconsumer now treats the archived row as absence, accepts LiteLLM 1.98's status-less rows, and fails closed on an unrecognized status. litellm_modelcustom pricing inmodel_info: LiteLLM 1.102.0 and later silently drop pricing sent inmodel_info, so a price inadditional_model_infooradditional_model_info_jsonwas not saved, spend fell back to the catalog price, and apply then failed with "Provider produced inconsistent result" and a tainted model. Both attributes now warn at plan time about LiteLLM's pricing fields, tiered*_above_<N>_tokensrates, and the generatedkey/pricing_overridesfields. The warning is not an error, so existing configurations, and LiteLLM 1.101 and earlier where these keys still work, are unaffected. Set prices with the dedicated cost attributes oradditional_litellm_params; the model documentation describes the one-time migration for models whose prices were stored inmodel_infoon LiteLLM 1.101 or earlier. A contract test derives the pricing field set from the pinned OpenAPI so a future LiteLLM pin cannot drift silently.litellm_jwt_key_mappingon LiteLLM 1.104.0: Every create, read, import, and data-source read failed with "Invalid API Response" because LiteLLM 1.104.0 returns a newjwt_issuerfield that the strict decoder rejected. Responses with and without the field are now accepted, and issuer identity is verified on every read-back. Deleting a mapping's virtual key now also deletes the mapping upstream; the resource documentation describes how to re-create both together.litellm_agent/litellm_agentsdata sources: Agents with secretlitellm_params(for exampleapi_key) no longer fail the read with "Invalid API Response". LiteLLM 1.104.0 masks those values for every caller, proxy admins included, so the data sources now report LiteLLM'sREDACTED_BY_LITELMplaceholder, as the guardrail data source already does.litellm_mcp_serversdata source on LiteLLM 1.104.0: The list failed with "malformed MCP server list" whenever a server had OAuth scopes, because LiteLLM 1.104.0 now returns proxy admins the redacted credential projection (scopesand non-secret admin-config strings) in the list too. That projection is accepted and still not exposed; any other credential member continues to fail the read without echoing its content.