Terraform - Create Azure Cosmos DB with Cassandra API, Add User Defined Type ( UDT) through Terraform
About
- Create (and destroy) an Azure Cosmos DB Account through Terraform
- Enable Cassandra API
- Options - Add Partition Key, Throughput Settings
- Challenges - Add UDT
Pre-requisites
- IAC / Terraform Basics
- Install Terraform and Azure CLI
- Cassandra / Cassandra query language - basics
- Azure Subscription to execute the plan and test
Contents
- Create an Azure Resource Group
- Add Azure Cosmos DB | Cassandra API
- Challenges adding Cassandra UDT
- Add Docker Provider
- Add Custom C*/UDT Scripts
- Add and execute Custom CQL Scripts against CosmosDB
Create an Azure Resource Group
Log in to the Azure using Azure CLI - az login command and choose the subscription needed.
This step needs an Active Azure Subscription under which the resource group will be created. Create a new working directory and add two files main.tf and cosmos.tf files that looks like below,
# Configure the Azure provider
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 2.85"
}
}
required_version = ">= 0.15.3"
}
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "rg" {
name = "rg-terraform-centralindia-001"
location = "centralindia"
}
- Run terraform init to initialize providers and terraform plan to create and check the plan, the output looks like below,
Terrafrom-Cosmos > terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following
symbols:
+ create
Terraform will perform the following actions:
# azurerm_resource_group.rg will be created
+ resource "azurerm_resource_group" "rg" {
+ id = (known after apply)
+ location = "centralindia"
+ name = "rg-terraform-centralindia-001"
}
Plan: 1 to add, 0 to change, 0 to destroy.
- Run terraform apply command. This step will create the resource group rg-terraform-centralindia-001 under the active subscription
Add Azure Cosmos DB | Cassandra API
- Add Azure Cosmos DB - cosmos-cassandra-001 , a Keyspace - keyspace-sample and a table - events by adding the below snippet in cosmos.tf file created earlier
# Add Azure Cosmos DB , Capabilities - EnableCassandra
resource "azurerm_cosmosdb_account" "db" {
name = "cosmos-cassandra-001"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
offer_type = "Standard"
geo_location {
location = azurerm_resource_group.rg.location
failover_priority = 0
}
consistency_policy {
consistency_level = "BoundedStaleness"
max_interval_in_seconds = 300
max_staleness_prefix = 100000
}
capabilities {
name = "EnableCassandra"
}
}
# Add a sample keyspace
resource "azurerm_cosmosdb_cassandra_keyspace" "keyspace_sample" {
name = "keyspace-sample"
resource_group_name = azurerm_resource_group.rg.name
account_name = azurerm_cosmosdb_account.db.name
autoscale_settings {
max_throughput = 4000
}
}
# Add a sample table
resource "azurerm_cosmosdb_cassandra_table" "table_sample_events" {
name = "events"
cassandra_keyspace_id = azurerm_cosmosdb_cassandra_keyspace.keyspace_sample.id
default_ttl = 3600
schema {
column {
name = "id"
type = "UUID"
}
column {
name = "event_timestamp"
type = "timestamp"
}
partition_key {
name = "correlation_id"
}
cluster_key {
name = "event_timestamp"
order_by = "Desc"
}
}
}
- Run terraform plan and terraform apply to create the cosmos db keyspace and the table
Challenges adding Cassandra UDT
There is no OOB support through Terraform to create the special User-defined Types in Azure Cosmos DB.
UDT is defined as,
User-defined types (UDTs) can attach multiple data fields, each named and typed, to a single column. The fields used to create a UDT may be any valid data type, including collections and other existing UDTs. Once created, UDTs may be used to define a column in a table.
Let's solve this problem through Terraform Docker Provider to run CQL Queries.
Add Docker Provider
Terraform Docker Provider and Local Execution is so powerful that literally any IAC Scripts can be run against the resource that's been created. Let's extend the cosmosdb created above with Docker Provider
so, main.tf becomes
# Configure the Azure provider
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 2.85"
}
docker = {
source = "kreuzwerker/docker"
version = "2.15.0"
}
}
required_version = ">= 0.15.3"
}
provider "azurerm" {
features {}
}
provider "docker" {
host = "unix:///var/run/docker.sock"
}
Run a custom cql script agasint Azure CosmosDB
- Lets first create a CQL Script that will use the table created earlier and add UDT. Added a scripts.cql file below,
# Add an UDT Address and alter the table created
use keyspace_sample;
CREATE TYPE IF NOT EXISTS address (
area TEXT,
street TEXT,
locality TEXT
);
ALTER TABLE table_sample_events ADD (
event_address address,
)
Use Terraform docker-image resource to create a Cassandra image and run it using Terraform local-exec
scripts.cql is volume mounted and passed as an argument to docker run command. Terraform locals variable is used to extract the connection string in the format as needed by the CQLSH Command as seen below,
resource "docker_image" "cassandra" {
name = "cassandra:latest"
keep_locally = true
depends_on = [ azurerm_cosmosdb_cassandra_table.table_sample_events]
provisioner "local-exec" {
command ="docker run --rm -v $(pwd)/scripts.cql:/scripts.cql --env SSL_VALIDATE=false --env SSL_VERSION=TLSv1_2 cassandra:latest cqlsh ${local.host[0]} ${local.host[3]} -u cosmos-cassandra-001 -p ${azurerm_cosmosdb_account.db.primary_master_key} --ssl -f /scripts.cql"
}
}
# split the connection string to pass to custom shell script
# enumerated as host , username , secret and port
locals {
host = [for s in split(";", azurerm_cosmosdb_account.db.connection_strings[4]) : split("=", s)[1] if s != ""]
}
Run terraform plan and terraform apply to extract the locals variable and add the custom UDT through scripts.cql file. As seen below, event_address UDT has been added to the table events