ARM Templates for Cosmos DB
Complete the full lesson to earn 25 points — 50 with Pro
Work through each section, then tap “Mark as Complete” on the last one.
✦ Skip the page breaks, the wait, and see fewer ads — read each lesson on a single page with Pro
Mastering Infrastructure as Code: ARM Templates for Azure Cosmos DB
Introduction: Why Infrastructure as Code Matters for Cosmos DB
When managing cloud resources, the manual approach—clicking through the Azure Portal to configure databases, containers, and throughput—is a recipe for inconsistency. As your application grows from a single development environment to staging and production, the risk of "configuration drift" increases significantly. You might accidentally set a different indexing policy in production than in development, or forget to enable a specific consistency level that your application logic depends on. This is where Infrastructure as Code (IaC) becomes essential.
Azure Resource Manager (ARM) templates are the native way to define your Azure infrastructure as JSON files. By treating your Cosmos DB configuration as code, you gain the ability to version control your infrastructure, peer-review changes through pull requests, and deploy environments that are identical across every stage of your development lifecycle. For Cosmos DB specifically, this is critical because parameters like partition keys, Time-to-Live (TTL) settings, and throughput (RU/s) are foundational to the performance and cost of your application. Mastering ARM templates ensures that your database layer is as predictable and reliable as the application code it supports.
Understanding the Anatomy of an ARM Template
An ARM template is essentially a blueprint. When you submit this blueprint to Azure, the Resource Manager service parses the JSON, validates your request, and orchestrates the creation or modification of the resources. Every ARM template follows a standard schema that includes specific sections designed to handle different aspects of the deployment.
The Core Sections of an ARM Template
- $schema: This tells Azure which version of the template language to use. You should always use the latest schema to ensure full support for newer resource properties.
- contentVersion: An internal version number that you can use to track your own template changes.
- parameters: These are inputs that allow you to make your templates reusable. Instead of hardcoding a database name, you define it as a parameter so you can pass "prod-db" or "dev-db" during deployment.
- variables: These are values you calculate or concatenate within the template. Use variables to avoid repeating complex strings like resource IDs or naming conventions.
- resources: This is the heart of the template. Here, you define the actual Azure resources, such as the Cosmos DB account, the SQL database, and the containers.
- outputs: These return information after the deployment is finished, such as the primary connection string or the endpoint URI, which your application might need to connect to the database.
Callout: Declarative vs. Imperative ARM templates are declarative. This means you describe what you want the final state to look like, rather than providing a list of instructions on how to get there. If you ask for a Cosmos DB account with 400 RU/s, Azure looks at the current state and makes only the necessary changes to reach that 400 RU/s target. This is fundamentally different from imperative scripts (like Azure CLI or PowerShell) which often require complex logic to check existing states before taking action.
Setting Up Your First Cosmos DB Template
To deploy a Cosmos DB account, we must define the Microsoft.DocumentDB/databaseAccounts resource. This resource type is the top-level container for all Cosmos DB operations. Below is a foundational template that sets up a standard SQL API account with basic configurations.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"accountName": {
"type": "string",
"metadata": { "description": "The name of the Cosmos DB account" }
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]"
}
},
"resources": [
{
"type": "Microsoft.DocumentDB/databaseAccounts",
"apiVersion": "2021-04-15",
"name": "[parameters('accountName')]",
"location": "[parameters('location')]",
"kind": "GlobalDocumentDB",
"properties": {
"databaseAccountOfferType": "Standard",
"locations": [
{
"locationName": "[parameters('location')]",
"failoverPriority": 0
}
],
"consistencyPolicy": {
"defaultConsistencyLevel": "Session"
}
}
}
]
}
Breaking Down the Resource Properties
- kind: For the SQL API, we use
GlobalDocumentDB. If you were deploying a MongoDB or Cassandra API, this value would change accordingly. - databaseAccountOfferType: This is always
Standardfor general-purpose Cosmos DB accounts. - consistencyPolicy: This is a mandatory setting.
Sessionconsistency is the most common default, balancing performance and data integrity for most web applications. - locations: This defines the geo-replication strategy. Even if you only deploy to one region, you must specify it here with a
failoverPriorityof zero.
Managing Databases and Containers
A Cosmos DB account is just the shell. To make the database useful, you need to define the sqlResources/databases and sqlResources/containers within the template. These are "child resources" of the account. You can define them within the resources array of the account or as separate resources linked by their parent property.
Defining a Container with Throughput
Defining a container requires specifying the partition key, which is one of the most critical decisions in Cosmos DB design. Changing this later is difficult, so defining it correctly in your ARM template from day one is vital.
{
"type": "Microsoft.DocumentDB/databaseAccounts/sqlResources/containers",
"apiVersion": "2021-04-15",
"name": "[concat(parameters('accountName'), '/myDatabase/myContainer')]",
"properties": {
"resource": {
"id": "myContainer",
"partitionKey": {
"paths": ["/userId"],
"kind": "Hash"
},
"indexingPolicy": {
"indexingMode": "consistent",
"includedPaths": [{ "path": "/*" }]
}
},
"options": {
"throughput": 400
}
}
}
Note: The
nameproperty for a child resource must follow the patternparentAccountName/databaseName/containerName. Using theconcatfunction as shown above ensures that the template dynamically builds this path based on your parameters.
Advanced Configuration: Autoscale vs. Manual Throughput
One of the most common requirements in production is managing costs. Cosmos DB allows you to choose between manual throughput (a fixed RU/s) and Autoscale (a range that scales based on demand). Using ARM templates, you can easily toggle these settings.
Implementing Autoscale
To configure Autoscale, you modify the options block within your container definition. Instead of a simple throughput integer, you provide an autoscaleSettings object.
"options": {
"autoscaleSettings": {
"maxThroughput": 1000
}
}
When you deploy this, Cosmos DB will automatically scale your throughput between 100 RU/s and 1000 RU/s based on your traffic patterns. This is generally recommended for production workloads where traffic is unpredictable, as it prevents you from paying for idle capacity while ensuring your application doesn't hit request rate limits during spikes.
Best Practices for ARM Template Development
Managing infrastructure as code is a discipline. To keep your templates maintainable and scalable, follow these industry-standard practices:
- Modularization: Don't put everything in one massive file. Use "Linked Templates" to break your infrastructure into smaller, logical chunks (e.g., one template for the network, one for the database, one for the compute).
- Use Parameters Files: Never hardcode values directly into the main template. Create a
parameters.jsonfile for each environment (e.g.,dev.parameters.json,prod.parameters.json). This keeps your template logic clean and environment-specific data separate. - Standardize Naming: Use a consistent naming convention for your resources. For example, use prefixes like
cosmos-,rg-, orst-to identify resource types at a glance in the Azure Portal. - Version Control: Always store your ARM templates in a Git repository. Every change to your infrastructure should be documented in a commit message, allowing you to roll back if a deployment causes an issue.
- Use Azure Policy: Use Azure Policy to enforce standards. For example, you can create a policy that prevents the creation of Cosmos DB accounts that do not have encryption-at-rest enabled.
Callout: The Power of
copyLoops You might find yourself needing to create multiple containers at once. Instead of copying and pasting the JSON block for each one, use thecopyproperty in your template. This allows you to iterate over an array of container names, drastically reducing the size of your template and the likelihood of human error.
Common Pitfalls and How to Avoid Them
Even experienced engineers run into issues with ARM templates. Understanding these common traps will save you significant debugging time.
1. The "Resource Dependencies" Trap
Azure does not always know which resource to create first. If your container depends on a database, and the database depends on the account, you must use the dependsOn property. Without this, Azure might attempt to create the container before the account exists, leading to a deployment failure.
"dependsOn": [
"[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('accountName'))]"
]
2. Ignoring Throughput Limits
If you try to set a throughput that is too high for your subscription quota, the deployment will fail. Always check your subscription limits before deploying large-scale infrastructure. Additionally, remember that changing throughput via ARM template can trigger a re-indexing operation or a partition re-balancing, which can take time.
3. Over-complicating Logic
ARM templates support expressions like if, concat, and reference. While powerful, they can make templates unreadable if overused. If your template logic is becoming too complex, consider using Bicep. Bicep is a domain-specific language that compiles down to ARM templates but provides a much cleaner, more readable syntax.
Comparison: ARM Templates vs. Bicep
While this lesson focuses on ARM templates, it is important to understand the landscape. Bicep is the recommended evolution of ARM templates.
| Feature | ARM Templates (JSON) | Bicep |
|---|---|---|
| Syntax | Verbose JSON | Concise, clean syntax |
| Readability | Challenging for complex logic | Highly readable |
| Tooling | Basic support | Excellent (IntelliSense, validation) |
| Modularity | Requires complex linking | Native modules |
| Lifecycle | Compiled to ARM JSON | Compiles to ARM JSON |
Tip: If you are starting a new project, prioritize learning Bicep. It offers all the power of ARM templates without the syntactic overhead of JSON. However, knowing standard ARM templates remains essential, as Bicep is ultimately an abstraction on top of the ARM platform.
Step-by-Step: Deploying Your Template
Once your template is ready, you need to deploy it to Azure. You can do this via the Azure CLI, PowerShell, or the Azure Portal. Using the CLI is the most common approach for automated pipelines.
- Create a Resource Group: If you haven't already, create a container for your resources.
az group create --name MyCosmosRG --location eastus - Validate the Template: Before deploying, check for syntax errors.
az deployment group validate --resource-group MyCosmosRG --template-file template.json --parameters parameters.json - Execute the Deployment: Run the deployment command.
az deployment group create --resource-group MyCosmosRG --template-file template.json --parameters parameters.json - Verify: Log into the Azure Portal or use
az cosmosdb showto confirm the account and containers were created with the expected settings.
Dealing with Existing Resources (Importing)
A common challenge is "I already have a Cosmos DB account; how do I start using ARM templates?" You don't have to delete your database to start using IaC. You can use the "Export Template" feature in the Azure Portal to generate a template based on your existing resource.
- Navigate to your Cosmos DB account in the Azure Portal.
- Select Export template from the left-hand menu.
- Review the generated JSON. Note that the exported template often includes many auto-generated properties that you don't need; clean it up by removing unnecessary metadata and hardcoded IDs.
- Save this as your base template and move it into your version control system.
Warning: Be cautious when using the "Export template" feature. The generated JSON is often "noisy" and contains environment-specific details that should be moved into parameters. Always sanitize the exported file before using it as a foundation for your infrastructure code.
Security and Compliance in Templates
When deploying to production, security is non-negotiable. Your ARM templates should reflect your security posture.
- Network Rules: Use the
virtualNetworkRulesproperty to restrict access to your Cosmos DB account to specific subnets only. - Public Access: Explicitly set
publicNetworkAccesstoDisabledif your database should only be accessible from within your virtual network. - Encryption: Ensure
keyVaultKeyUriis configured if you are using customer-managed keys (CMK) for encryption at rest. - Identity: Use Managed Identities whenever possible to connect your application to Cosmos DB, rather than storing primary keys in application configuration files.
Handling Database Keys and Secrets
A frequent mistake is putting the Cosmos DB primary keys in an ARM template. Never do this. The primary keys are sensitive secrets. Instead, use an ARM template to deploy the Cosmos DB account, and then use a separate process (like a Key Vault deployment or a managed identity) to handle the connection strings.
If you need to retrieve the connection string after deployment, use the listKeys function in an output block, but be very careful where you store that output.
"outputs": {
"primaryKey": {
"type": "string",
"value": "[listKeys(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('accountName')), '2021-04-15').primaryMasterKey]"
}
}
Only use this approach if you are outputting the value directly to a secure location, such as an Azure Key Vault secret, during the deployment process.
Integrating into CI/CD Pipelines
To truly benefit from ARM templates, they must be integrated into a CI/CD pipeline (such as Azure DevOps or GitHub Actions).
- Continuous Integration: Whenever a developer pushes a change to the
templates/folder, the pipeline should run avalidatetask. This ensures the template is syntactically correct and adheres to your company's rules. - Continuous Deployment: Upon merging to the
mainbranch, the pipeline should execute the deployment to the staging environment. After automated tests pass, the pipeline can proceed to deploy to production. - Dry Runs: Use the "What-If" operation (
az deployment group what-if) in your pipelines. This command tells you exactly what changes will be made to your infrastructure before they actually happen. It is the best way to prevent accidental deletions or unintended reconfigurations.
Summary: Key Takeaways for Maintaining Cosmos DB
Maintaining a Cosmos DB solution via ARM templates is a professional standard that separates hobbyist setups from enterprise-grade architectures. By following the principles outlined here, you ensure your database remains consistent, scalable, and secure.
- Infrastructure as Code is Mandatory: Manual configuration leads to drift. Use ARM templates (or Bicep) to define your database structure, ensuring every environment is a mirror of the next.
- Parameters are Your Best Friend: Decouple your infrastructure logic from environment-specific values. Use separate parameter files for development, testing, and production to maintain a clean codebase.
- Understand Dependencies: Always use the
dependsOnproperty to guide Azure’s deployment sequence. This prevents failures related to resource creation order, especially when dealing with child resources like containers. - Prioritize Security: Use ARM templates to enforce network restrictions and encryption policies from the start. Never hardcode sensitive credentials like primary keys in your templates.
- Leverage Automation: Integrate your templates into CI/CD pipelines and always use "What-If" operations before applying changes to production. This creates a safety net that protects your data and uptime.
- Start Small, Then Scale: If you are new to ARM templates, begin by exporting an existing resource, cleaning it up, and deploying it to a sandbox environment. Gradually add complexity like Autoscale settings and modularized templates as you gain confidence.
- Embrace Evolution: While ARM templates are powerful, keep an eye on Bicep. It provides the same deployment power as JSON-based ARM templates but with a significantly lower barrier to entry and better developer experience.
By adopting these practices, you transform your infrastructure management from a reactive, manual burden into a proactive, automated asset. This allows you to focus on building features and optimizing your application, confident that your database foundation is robust, documented, and ready for whatever scale your users require.
Reach the last section to complete this lesson and earn points — you're on section 1 of 11.
- Introduction to Cosmos DB Data Modeling
- Introduction to Cosmos DB Data Modeling Quiz5q
- Multiple Entity Types in Same Container
- Multiple Entity Types in Same Container Quiz5q
- Storing Related Entities in Same Document
- Storing Related Entities in Same Document Quiz5q
- Denormalizing Data Across Documents
- Denormalizing Data Across Documents Quiz5q
- Referencing Between Documents
- Referencing Between Documents Quiz5q
- Partition Keys and Document IDs
- Partition Keys and Document IDs Quiz5q
- Time to Live (TTL) Configuration
- Time to Live (TTL) Configuration Quiz5q
- Document Versioning Strategies
- Document Versioning Strategies Quiz5q
- Schema Versioning Patterns
- Schema Versioning Patterns Quiz5q
- Choosing Partition Strategies
- Choosing Partition Strategies Quiz5q
- Partition Key Selection Best Practices
- Partition Key Selection Best Practices Quiz5q
- Transactions and Partition Keys
- Transactions and Partition Keys Quiz5q
- Cross-Partition Query Costs
- Cross-Partition Query Costs Quiz5q
- Data Distribution Analysis
- Data Distribution Analysis Quiz5q
- Throughput Distribution Planning
- Throughput Distribution Planning Quiz5q
- Synthetic Partition Keys
- Synthetic Partition Keys Quiz5q
- Hierarchical Partition Keys
- Hierarchical Partition Keys Quiz5q
- Throughput and Storage Requirements
- Throughput and Storage Requirements Quiz5q
- Serverless vs Provisioned Throughput
- Serverless vs Provisioned Throughput Quiz5q
- Database-Level Provisioned Throughput
- Database-Level Provisioned Throughput Quiz5q
- Granular Scale Units
- Granular Scale Units Quiz5q
- Global Distribution Costs
- Global Distribution Costs Quiz5q
- Configuring Throughput in Portal
- Configuring Throughput in Portal Quiz5q
- Gateway vs Direct Connectivity Mode
- Gateway vs Direct Connectivity Mode Quiz5q
- Creating Database Connections
- Creating Database Connections Quiz5q
- Azure Cosmos DB Emulator
- Azure Cosmos DB Emulator Quiz5q
- Connection Error Handling
- Connection Error Handling Quiz5q
- Singleton Pattern for Clients
- Singleton Pattern for Clients Quiz5q
- Global Distribution Regions
- Global Distribution Regions Quiz5q
- Threading and Parallelism
- Threading and Parallelism Quiz5q
- Arrays and Nested Objects Queries
- Arrays and Nested Objects Queries Quiz5q
- Correlated Subqueries
- Correlated Subqueries Quiz5q
- Array and Type-Checking Functions
- Array and Type-Checking Functions Quiz5q
- Mathematical and String Functions
- Mathematical and String Functions Quiz5q
- Date Functions in Queries
- Date Functions in Queries Quiz5q
- Point Operations vs Query Operations
- Point Operations vs Query Operations Quiz5q
- CRUD Point Operations
- CRUD Point Operations Quiz5q
- Patch Operations for Updates
- Patch Operations for Updates Quiz5q
- Transactional Batch Operations
- Transactional Batch Operations Quiz5q
- Bulk Operations with SDK
- Bulk Operations with SDK Quiz5q
- Optimistic Concurrency with ETags
- Optimistic Concurrency with ETags Quiz5q
- Query Pagination and Continuation
- Query Pagination and Continuation Quiz5q
- Cosmos DB Mirroring for Fabric
- Cosmos DB Mirroring for Fabric Quiz5q
- Mirroring vs Spark Connector
- Mirroring vs Spark Connector Quiz5q
- Enabling Analytical Store
- Enabling Analytical Store Quiz5q
- Synapse Spark and SQL Queries
- Synapse Spark and SQL Queries Quiz5q
- Change Data Capture in Analytical Store
- Change Data Capture in Analytical Store Quiz5q
- Azure Functions and Event Hubs Integration
- Azure Functions and Event Hubs Integration Quiz5q
- Denormalization with Change Feed
- Denormalization with Change Feed Quiz5q
- Referential Integrity with Change Feed
- Referential Integrity with Change Feed Quiz5q
- Azure AI Search Integration
- Azure AI Search Integration Quiz5q
- Azure Functions Change Feed Trigger
- Azure Functions Change Feed Trigger Quiz5q
- Consuming Change Feed with SDK
- Consuming Change Feed with SDK Quiz5q
- Change Feed Estimator
- Change Feed Estimator Quiz5q
- Denormalization via Change Feed
- Denormalization via Change Feed Quiz5q
- Aggregation Persistence with Change Feed
- Aggregation Persistence with Change Feed Quiz5q
- Read-Heavy vs Write-Heavy Indexing
- Read-Heavy vs Write-Heavy Indexing Quiz5q
- Index Type Selection
- Index Type Selection Quiz5q
- Custom Indexing Policies
- Custom Indexing Policies Quiz5q
- Composite Index Implementation
- Composite Index Implementation Quiz5q
- Index Performance Optimization
- Index Performance Optimization Quiz5q
- Response Status Codes and Metrics
- Response Status Codes and Metrics Quiz5q
- Normalized RU Consumption Monitoring
- Normalized RU Consumption Monitoring Quiz5q
- Server-Side Latency Metrics
- Server-Side Latency Metrics Quiz5q
- Data Replication Monitoring
- Data Replication Monitoring Quiz5q
- Azure Monitor Alerts Configuration
- Azure Monitor Alerts Configuration Quiz5q
- Resource Logs Implementation
- Resource Logs Implementation Quiz5q
- Partition Throughput Monitoring
- Partition Throughput Monitoring Quiz5q
- Encryption Key Management
- Encryption Key Management Quiz5q
- Network-Level Access Control
- Network-Level Access Control Quiz5q
- Data Encryption Configuration
- Data Encryption Configuration Quiz5q
- Azure RBAC for Control Plane
- Azure RBAC for Control Plane Quiz5q
- Microsoft Entra ID for Data Plane
- Microsoft Entra ID for Data Plane Quiz5q
- CORS Settings Configuration
- CORS Settings Configuration Quiz5q
- Customer-Managed Keys
- Customer-Managed Keys Quiz5q
- Always Encrypted Implementation
- Always Encrypted Implementation Quiz5q
- Data Movement Strategy Selection
- Data Movement Strategy Selection Quiz5q
- SDK Bulk Operations for Data Movement
- SDK Bulk Operations for Data Movement Quiz5q
- Azure Data Factory Pipelines
- Azure Data Factory Pipelines Quiz5q
- Kafka Connector Integration
- Kafka Connector Integration Quiz5q
- Azure Stream Analytics Integration
- Azure Stream Analytics Integration Quiz5q
- Cosmos DB Spark Connector
- Cosmos DB Spark Connector Quiz5q
Enjoying the courses?
Everything stays free. Pro shows fewer ads, doubles the points you earn on every lesson and quiz so you progress twice as fast, unlocks half of every practice exam — plus full case studies — with the Learn & Exam study modes, and lets you read each lesson on one page.
- ✓ Fewer advertisements
- ✓ 2× points per lesson & quiz
- ✓ 50% of every exam unlocked
- ✓ Learn & Exam modes
- ✓ Distraction-free lessons