The most surprising thing about provisioning Cosmos DB with Terraform is how much of the "magic" is actually just declarative API calls, and how little state Terraform truly needs to manage for Cosmos itself.

Let’s see it in action. Imagine you want a new Cosmos DB account, a database within it, and a container to hold your documents. Here’s a simple main.tf:

provider "azurerm" {
  features {}
}

resource "azurerm_resource_group" "example" {
  name     = "cosmos-provisioning-rg"
  location = "East US"
}

resource "azurerm_cosmosdb_account" "example" {
  name                = "my-cosmos-account-tf"
  resource_group_name = azurerm_resource_group.example.name
  location            = azurerm_resource_group.example.location
  offer_type         = "Standard"
  kind               = "GlobalDocumentDB"

  consistency_policy {
    consistency_level = "Session"
  }

  geo_location {
    failover_priority = 0
    location          = azurerm_resource_group.example.location
    prefix            = "mycosmos"
  }
}

resource "azurerm_cosmosdb_database" "example" {
  name                = "myDatabase"
  resource_group_name = azurerm_resource_group.example.name
  account_name        = azurerm_cosmosdb_account.example.name
}

resource "azurerm_cosmosdb_container" "example" {
  name                  = "myContainer"
  database_name         = azurerm_cosmosdb_database.example.name
  resource_group_name   = azurerm_resource_group.example.name
  account_name          = azurerm_cosmosdb_account.example.name
  partition_key_path    = "/id"
  throughput            = 400 # RU/s
}

When you run terraform apply, Terraform talks to the Azure Resource Manager (ARM) API. ARM then orchestrates the creation of the Cosmos DB account, database, and container. Terraform’s role is primarily to translate your desired state (these resources, with these properties) into API calls. It keeps track of the identifiers of the resources it created (like the account name, database name, container name) in its state file, so it knows how to reference them for future changes or destruction.

The problem this solves is the manual, repetitive, and error-prone process of setting up Cosmos DB instances, especially for development, testing, or production environments. Instead of clicking through the Azure portal or writing complex scripts, you define your infrastructure as code.

Internally, when you define azurerm_cosmosdb_account, Terraform is essentially saying "Ensure an Azure Cosmos DB account named 'my-cosmos-account-tf' exists in resource group 'cosmos-provisioning-rg' with these settings." Azure’s API validates these settings, creates the account, and returns a unique resource ID. Terraform stores this ID. For azurerm_cosmosdb_database and azurerm_cosmosdb_container, Terraform uses the output of the azurerm_cosmosdb_account resource (its name and resource group) to correctly target the creation of nested resources. The partition_key_path on the container is crucial; it defines how your data will be distributed across physical partitions, directly impacting performance and scalability. The throughput is provisioned in Request Units per second (RU/s), which is Cosmos DB’s unit of measure for throughput.

A common point of confusion is how throughput is managed. For Standard offer types, you can provision throughput at the database level (shared across all containers in that database) or at the container level (dedicated throughput for that specific container). The azurerm_cosmosdb_container resource directly sets the throughput for a single container, overriding any database-level settings if they were also defined. If you omit the throughput argument here, and a database-level throughput is set via azurerm_cosmosdb_database, that database-level throughput will apply.

Don’t forget that Cosmos DB accounts have a default consistency policy, but it’s best practice to explicitly define it in your Terraform configuration, as shown with consistency_policy { consistency_level = "Session" }. This ensures predictable read behavior across your distributed environment and avoids unexpected data staleness.

The next thing you’ll likely want to configure is setting up private endpoints for secure network access to your Cosmos DB account.

Want structured learning?

Take the full Cosmos-db course →