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,

main.tf

# Configure the Azure provider
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 2.85"
    }
  }
  required_version = ">= 0.15.3"
}

provider "azurerm" {
  features {}
}

cosmos.tf

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

  • Source Code

Cassandra_Table_WithUDT.jpg