Last reviewed: May 2026
Build the AWS services on the DP-420 exam with plain Terraform — one block at a time, each tied back to an exam domain. The same code works on OpenTofu.
By the end of this lab you'll have provisioned, with plain Terraform, a Cosmos DB account configured for the DP-420 exam patterns — Core (SQL) API, autoscale throughput with a max cap, a database + container with a thoughtful partition key, a custom indexing policy that excludes write-heavy paths, point-in-time restore enabled, and Continuous backup. This is the DP-420 reference baseline.
Drop the snippets into a single main.tf, run terraform init, then terraform apply step-by-step.
>= 1.5 or OpenTofu >= 1.6.az login).DP-420 cost questions are about Cosmos DB capacity modes:
max_throughput = 1000).This lab uses autoscale with max 1000 RU/s — minimum bill ~$24/month even at zero traffic (autoscale bills at 10% of max RU/s minimum). Switch autoscale_settings to serverless capability for $0 idle. Destroy promptly if you don't switch.
Standard Azure opener. Cosmos DB account names must be globally unique (DNS-published as <name>.documents.azure.com) — random_id dodges collisions.
terraform {
required_version = ">= 1.5"
required_providers {
azurerm = { source = "hashicorp/azurerm", version = "~> 4.0" }
random = { source = "hashicorp/random", version = "~> 3.6" }
}
}
provider "azurerm" {
features {}
}
resource "random_id" "suffix" {
byte_length = 3
}
data "azurerm_client_config" "current" {}
locals {
tags = {
Project = "certlabpro-dp-420"
ManagedBy = "terraform"
}
}
resource "azurerm_resource_group" "main" {
name = "certlabpro-dp-420-rg"
location = "eastus"
tags = local.tags
}DP-420 Implement, manage, and monitor a Cosmos DB solution tests backup modes head-on: Periodic (default; 2 backups every 4 hours retained 8 days) vs Continuous (point-in-time restore to any second within 30 or 7 days). Continuous is the prod-grade answer; we use the 7-day tier for the lab to keep cost low.
local_authentication_disabled = true forces Entra ID auth — the DP-420 production answer. Cosmos's RBAC data-plane model is one of the most-tested exam topics.
resource "azurerm_cosmosdb_account" "main" {
name = "cosmos-dp420-${random_id.suffix.hex}"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
offer_type = "Standard"
kind = "GlobalDocumentDB" # Core (SQL) API — DP-420 default
consistency_policy {
consistency_level = "BoundedStaleness"
max_interval_in_seconds = 10
max_staleness_prefix = 100
}
geo_location {
location = azurerm_resource_group.main.location
failover_priority = 0
}
backup {
type = "Continuous"
tier = "Continuous7Days" # cheapest continuous-backup tier
}
local_authentication_disabled = true # Entra auth only
tags = local.tags
}The container is where DP-420 spends the most exam time. We pick /userId as the partition key for the example — high cardinality (good distribution), the typical access pattern is get my user's records, so we read within partition (fastest, cheapest RU/s).
The indexing policy is the next big DP-420 cost lever. By default Cosmos indexes every property — fast queries, expensive writes. We exclude /largeBlob/? (a path that holds heavy unstructured data we never query) and /_etag/? (auto-managed, no value to index). Custom indexing policies are explicitly tested as the cost-optimization answer for write-heavy workloads.
Autoscale max 1000 RU/s — bills 10% of max minimum at idle (100 RU/s = ~$24/month). For a real prod database you'd model the expected RU/s per request, multiply by sustained QPS, multiply by 1.5x safety, set max to that number.
resource "azurerm_cosmosdb_sql_database" "app" {
name = "appdb"
resource_group_name = azurerm_resource_group.main.name
account_name = azurerm_cosmosdb_account.main.name
autoscale_settings {
max_throughput = 1000
}
}
resource "azurerm_cosmosdb_sql_container" "user_events" {
name = "user-events"
resource_group_name = azurerm_resource_group.main.name
account_name = azurerm_cosmosdb_account.main.name
database_name = azurerm_cosmosdb_sql_database.app.name
partition_key_paths = ["/userId"]
indexing_policy {
indexing_mode = "consistent"
included_path {
path = "/*"
}
excluded_path {
path = "/largeBlob/?"
}
excluded_path {
path = "/_etag/?"
}
}
default_ttl = -1 # explicit opt-in (-1 means no auto-expiry); set to seconds for TTL
}With local_authentication_disabled = true from Step 2, the only way to read or write the container is via Entra ID auth + Cosmos's data-plane RBAC. The built-in Cosmos DB Built-in Data Contributor role (GUID 00000000-0000-0000-0000-000000000002) grants full read/write on the account scope.
DP-420 tests the control-plane vs data-plane role distinction: control-plane RBAC (Azure RBAC) manages the account itself (create container, change throughput); data-plane RBAC (Cosmos-specific) manages documents inside containers. They're separate role assignments; one doesn't imply the other.
resource "azurerm_cosmosdb_sql_role_assignment" "me_data_contributor" {
resource_group_name = azurerm_resource_group.main.name
account_name = azurerm_cosmosdb_account.main.name
role_definition_id = "${azurerm_cosmosdb_account.main.id}/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002"
principal_id = data.azurerm_client_config.current.object_id
scope = azurerm_cosmosdb_account.main.id
}terraform destroy tears down everything. Cosmos DB accounts have a 7-day soft-delete window — the name stays reserved during the window. The autoscale throughput stops billing immediately on destroy.
DP-420 covers many Cosmos DB features this lab can't fit — Synapse Link for analytical queries, change feed processor, Cosmos DB for MongoDB / Cassandra / Gremlin / Table (we use only Core SQL), multi-region writes (multi-master), private endpoints, customer-managed keys, hierarchical partition keys (preview at time of writing), and the Cosmos DB serverless capacity mode in depth.
We stick to the partition key + indexing + autoscale + RBAC + Continuous backup primitives because they're the most-tested DP-420 attributes. Multi-region writes layer on top of this base via additional geo_location blocks; private endpoints layer on via azurerm_private_endpoint; Synapse Link is a single attribute toggle. Get the base right; bolt on per-pattern.
For service-by-service coverage, see the Browse and Editorial sections of this cert page.