Tuesday, 19 August 2025

Setting Up Azure Managed Redis with Terraform Using AzApi

 The new Azure Managed Redis can be deployed with balanced compute and memory with high availability, and useful modules such as RedisJson and RedisSearch. This is really a useful and good pricing options to use Redis as a managed service in Azure. Note that it is not the enterprise redis offering in Azure and managed redis for Azure has more flexible pricing options. However, terraform support for this is yet to be added and will be available in another month or so as per the pull request here. Let's see how to get a Azure Managed Redis deployed with terraform for no using AzAPI.

The expectaion is to have a deployed Azure Managed Redis as shown below.

Here is the step by step details. The full terraform example is in GitHub.

First let's define few local variables. SPN below should have required permissoin to create new resources in the subscription.

locals {
  subscription_id    = "subscription_id"
  tenant_id          = "tenant_id"
  spn_app_id         = "spn_app_id"
  spn_pwd            = "spn_pwd" # replace with your actual service principal password

  redis_port = 10000
}


Then we need the providers setup as shown below.

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "=4.40.0"
    }
    azuread = {
      source  = "hashicorp/azuread"
      version = "=3.5.0"
    }
    azapi = {
      source  = "azure/azapi"
      version = "2.6.0"
    }
  }
}

provider "azurerm" {
  features {}
  subscription_id = local.subscription_id
}

To setup Azure managed redis with required modules we can use azapi as shwon below.

# Azure Managed Redis
resource "azapi_resource" "managed_redis" {
  type      = "Microsoft.Cache/redisEnterprise@2025-04-01"
  name      = "ch-demo-dev01-redis"
  location  = azurerm_resource_group.rg.location
  parent_id = azurerm_resource_group.rg.id

  body = {
    properties = {
      highAvailability  = "Enabled"
      minimumTlsVersion = "1.2"
    }
    sku = {
      name = "Balanced_B0"
    }
  }

  schema_validation_enabled = true
}

resource "azapi_resource" "managed_redis_database" {
  type      = "Microsoft.Cache/redisEnterprise/databases@2025-04-01"
  name      = "default"
  parent_id = azapi_resource.managed_redis.id

  body = {
    properties = {
      clientProtocol   = "Encrypted"
      evictionPolicy   = "NoEviction"
      clusteringPolicy = "EnterpriseCluster"
      deferUpgrade     = "NotDeferred"
      modules = [
        {
          name = "RedisBloom"
        },
        {
          name = "RediSearch"
        },
        {
          name = "RedisTimeSeries"
        },
        {
          name = "RedisJSON"
        }
      ]
      persistence = {
        aofEnabled = true
        rdbEnabled = false
      }
      accessKeysAuthentication = "Enabled"
      port = local.redis_port
    }
  }

  depends_on = [
    azapi_resource.managed_redis
  ]

  schema_validation_enabled = true
}

Then we can get the host name and port for output as below.

output "redis_hostname" {
  value = azapi_resource.managed_redis.output.properties.hostName
}

output "redis_port" {
  value = local.redis_port
}

However, getting the password/accesskey is bit challanging with as azapi does not have a direct way to get it as of now.

Therefore, we have to use a workaround as shown below.

Using the always running null resource below, we can extract the accesskey/password for redis and save it in a json file as a temp file.

resource "null_resource" "redis_key" {

  triggers = {
    always_run = timestamp()
  }

  depends_on = [
    azapi_resource.managed_redis,
    azapi_resource.managed_redis_database
  ]

  provisioner "local-exec" {
    command     = <<-SHELL
      az login --service-principal -u ${local.spn_app_id} -p ${local.spn_pwd} --tenant ${local.tenant_id}
      az account set --subscription ${local.subscription_id}
      az extension add --name redisenterprise --upgrade --yes
      $key = az redisenterprise database list-keys --cluster-name ${azapi_resource.managed_redis.name} --resource-group ${azurerm_resource_group.rg.name} --query primaryKey --output tsv
      $json = @{ primary_key = $key } | ConvertTo-Json -Compress
      $json | Out-File -FilePath redis_key.json -Encoding utf8
    SHELL
    interpreter = ["PowerShell"]
  }
}

Then we can have a powershell script setup in the terraform folder with name read-redis-key.ps1.
The script implments a mechanism to read the saved password from the temp file.

$ErrorActionPreference = "Stop"

$path = "redis_key.json"

try {
    if (Test-Path $path) {
        $json = Get-Content -Raw -Path $path | ConvertFrom-Json
        $jsonOut = $json | ConvertTo-Json -Compress

        # Print JSON to stdout FIRST so Terraform can read it
        Write-Output $jsonOut

        # Now that Terraform has read it, delete the file
        Remove-Item -Path $path -Force
    } else {
        @{ primary_key = "" } | ConvertTo-Json -Compress
    }
} catch {
    @{ primary_key = "" } | ConvertTo-Json -Compress
}

Then, we can use below terraform data source to run the above powershell script, to get the password and output it. If you are on linux you may replace powershell with bash script to get the same thing done.

data "external" "redis_key" {
  program = ["PowerShell", "./read-redis-key.ps1"]

  depends_on = [ null_resource.redis_key ]
}

output "redis_primary_access_key" {
  value     = data.external.redis_key.result.primary_key
  sensitive = true
}


No comments:

Popular Posts