When managing Azure OpenAI resources with Terraform, you’re likely to encounter a common issue: deployment failures due to model deprecation. These failures can be cryptic and disruptive, especially when you’re simply trying to run a terraform apply to spin up or update your infrastructure.

Here’s a typical error message you might see:

Error: creating Deployment (Subscription: “f716a567-edba-413c-baf4–52b493c5b8f9” Resource Group Name: “rg-foobar-dev-westus3” Account Name: “oai-foobar-dev-westus3” Deployment Name: “gpt-35-turbo”): performing CreateOrUpdate: unexpected status 400 (400 Bad Request) with error: ServiceModelDeprecated: The model ‘Format:OpenAI,Name:gpt-35-turbo,Version:0125’ has been deprecated since 11/14/2025 00:00:00.

This issue stems from the way model versions are hardcoded into Terraform configurations.

The Problem with Hardcoding Model Versions

A standard deployment using the azurerm_cognitive_deployment resource might look like this:

resource "azurerm_cognitive_deployment" "deployment_gpt_35_turbo" {
  name                 = "gpt-35-turbo"
  cognitive_account_id = azurerm_cognitive_account.openai.id

  sku {
    name     = "Standard"
    capacity = 10
  }

  model {
    format  = "OpenAI"
    name    = "gpt-35-turbo"
    version = "0125"
  }
}

While this works initially, the model version “0125” may be deprecated in the future. When that happens, any future terraform apply attempts will fail until you manually update the version.

Solution: Query Azure for the Latest Model Versions

To make deployments more resilient, you can dynamically fetch the latest model versions using the AzAPI Terraform provider. This lets you retrieve model metadata directly from the Azure OpenAI API, avoiding hardcoded version pitfalls.

Here’s how you can fetch the available models:

data "azapi_resource_action" "location_models" {
  type        = "Microsoft.CognitiveServices/locations@2024-10-01"
  resource_id = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/providers/Microsoft.CognitiveServices/locations/${data.azurerm_cognitive_account.openai.location}"
  # e.g. var.location = "westus3"

  action = "models"
  method = "GET"

  response_export_values = ["value"] # 'value' is the array of Model objects
}

This queries the Azure API for a list of available models and versions at your location. The result is a complex array of model definitions that we’ll distill into a more usable format.

Building a Version Map with Locals

Once you have the model list, the next step is to create a local map of the latest model versions using a Terraform localsblock:

locals {
  models_raw = data.azapi_resource_action.location_models.output["value"]

  models_latest = {
    for name, versions in {
      for m in local.models_raw :
      m.model.name => m.model.version...
    } :
    name => sort(versions)[
      length(versions) - 1
    ]
  }
}

This logic extracts the latest version number for each model. The output is a simple, practical map such as:

{
  "AI21-Jamba-Instruct" = "1"
  "Codestral-2501" = "2"
  "Cohere-command-r" = "1"
  "Cohere-command-r-08-2024" = "1"
  "Cohere-command-r-plus" = "1"
  "Cohere-command-r-plus-08-2024" = "1"
  "Cohere-embed-v3-english" = "1"
  "Cohere-embed-v3-multilingual" = "1"
  "DeepSeek-R1" = "1"
  "DeepSeek-R1-0528" = "1"
  "DeepSeek-V3-0324" = "1"
  "DeepSeek-V3.1" = "1"
  "FLUX-1.1-pro" = "1"
  "FLUX.1-Kontext-pro" = "1"
  "Llama-3.2-11B-Vision-Instruct" = "2"
  "Llama-3.2-90B-Vision-Instruct" = "3"
  "Llama-3.3-70B-Instruct" = "5"
  "Llama-4-Maverick-17B-128E-Instruct-FP8" = "1"
  "Llama-4-Scout-17B-16E-Instruct" = "1"
  "MAI-DS-R1" = "1"
  "Meta-Llama-3-70B-Instruct" = "9"
  "Meta-Llama-3-8B-Instruct" = "9"
  "Meta-Llama-3.1-405B-Instruct" = "1"
  "Meta-Llama-3.1-70B-Instruct" = "4"
  "Meta-Llama-3.1-8B-Instruct" = "5"
  "Ministral-3B" = "1"
  "Mistral-Large-2411" = "2"
  "Mistral-Nemo" = "1"
  "Mistral-large-2407" = "1"
  "Mistral-small" = "1"
  "Phi-3-medium-128k-instruct" = "7"
  "Phi-3-medium-4k-instruct" = "6"
  "Phi-3-mini-128k-instruct" = "13"
  "Phi-3-mini-4k-instruct" = "15"
  "Phi-3-small-128k-instruct" = "5"
  "Phi-3-small-8k-instruct" = "5"
  "Phi-3.5-MoE-instruct" = "5"
  "Phi-3.5-mini-instruct" = "6"
  "Phi-3.5-vision-instruct" = "2"
  "Phi-4" = "7"
  "Phi-4-mini-instruct" = "1"
  "Phi-4-mini-reasoning" = "1"
  "Phi-4-multimodal-instruct" = "1"
  "Phi-4-reasoning" = "1"
  "ada" = "1"
  "babbage" = "1"
  "cohere-command-a" = "1"
  "curie" = "1"
  "davinci" = "1"
  "embed-v-4-0" = "1"
  "gpt-35-turbo" = "1106"
  "gpt-4" = "turbo-2024-04-09"
  "gpt-4-32k" = "0613"
  "gpt-4.1" = "2025-04-14"
  "gpt-4.1-mini" = "2025-04-14"
  "gpt-4.1-nano" = "2025-04-14"
  "gpt-4o" = "2024-11-20"
  "gpt-4o-audio-preview" = "2024-12-17"
  "gpt-4o-mini" = "2024-07-18"
  "gpt-5-mini" = "2025-08-07"
  "gpt-5-nano" = "2025-08-07"
  "gpt-oss-120b" = "1"
  "grok-3" = "1"
  "grok-3-mini" = "1"
  "grok-4-fast-non-reasoning" = "1"
  "grok-4-fast-reasoning" = "1"
  "jais-30b-chat" = "3"
  "mistral-document-ai-2505" = "1"
  "mistral-medium-2505" = "1"
  "mistral-small-2503" = "1"
  "o1" = "2024-12-17"
  "o3-mini" = "2025-01-31"
  "o4-mini" = "2025-04-16"
  "text-embedding-3-large" = "1"
  "text-embedding-3-small" = "1"
  "text-embedding-ada-002" = "2"
  "tts" = "001"
  "tts-hd" = "001"
  "whisper" = "001"
}

You can then reference this map in your deployment configuration.

Final Terraform Deployment Using the Dynamic Model Version

Here’s the final version of your deployment resource that leverages the dynamically fetched version:

locals {
  gpt35_turbo = "gpt-35-turbo"
}

resource "azurerm_cognitive_deployment" "deployment_gpt_35_turbo" {
  name                 = "gpt-35-turbo"
  cognitive_account_id = azurerm_cognitive_account.openai.id

  sku {
    name     = "Standard"
    capacity = 10
  }

  model {
    format  = "OpenAI"
    name    = local.gpt35_turbo
    version = local.aoai_models_map[local.gpt35_turbo]
  }
}

This approach ensures that each time you run terraform apply, you’ll be deploying with the latest available version of the model — no more version mismatches or silent deprecations causing your pipeline to fail.

A Word of Caution

While this solution helps keep your deployments current, it doesn’t protect against full model deprecation. If a model is removed entirely from the API, even the latest version won’t save your deployment from breaking. In those cases, you’ll need to update the model name and possibly revise downstream consumers of that model (e.g., prompt formatting or token limits).

Conclusion

Hardcoding model versions in Terraform is brittle in a fast-moving ecosystem like Azure OpenAI. By using the AzAPI provider to dynamically query and map the latest model versions, you can make your infrastructure more robust and less prone to breakage from silent deprecations.

This approach future-proofs your deployments — at least until the models themselves vanish entirely.

Alt