Terraform: Azure Container Apps with Azure Managed Grafana using the AzAPI provider
Code snippet for the Monitoring Azure Container Apps With Azure Managed Grafana article.
main.tf
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">=3.0.0"
}
azapi = {
source = "azure/azapi"
version = ">=0.5.0"
}
}
}
provider "azurerm" {
features {
resource_group {
prevent_deletion_if_contains_resources = false
}
key_vault {
purge_soft_delete_on_destroy = false
}
}
}
resource "random_pet" "aca" {
length = 2
separator = ""
}
resource "random_integer" "aca" {
min = 000
max = 999
}
resource "random_string" "aca" {
length = 5
lower = true
upper = false
numeric = true
special = false
keepers = {
# Generate a new random_string on every run to avoid a conflict with the previous revision
none = timestamp()
}
}
locals {
resource_name = format("%s", random_pet.aca.id)
resource_name_unique = format("%s%s", random_pet.aca.id, random_integer.aca.result)
location = "eastus"
}
resource "azurerm_resource_group" "aca" {
name = "rg-${local.resource_name}"
location = local.location
}
resource "azurerm_log_analytics_workspace" "aca" {
name = "law-${local.resource_name_unique}"
resource_group_name = azurerm_resource_group.aca.name
location = azurerm_resource_group.aca.location
sku = "PerGB2018"
retention_in_days = 30
}
# https://docs.microsoft.com//azure/templates/microsoft.app/2022-03-01/managedenvironments?tabs=bicep&pivots=deployment-language-terraform
resource "azapi_resource" "env" {
type = "Microsoft.App/managedEnvironments@2022-03-01"
name = "env-${local.resource_name}"
parent_id = azurerm_resource_group.aca.id
location = azurerm_resource_group.aca.location
body = jsonencode({
properties = {
appLogsConfiguration = {
destination = "log-analytics"
logAnalyticsConfiguration = {
customerId = azurerm_log_analytics_workspace.aca.workspace_id
sharedKey = azurerm_log_analytics_workspace.aca.primary_shared_key
}
}
}
})
}
resource "azapi_resource" "helloworld" {
type = "Microsoft.App/containerApps@2022-03-01"
name = "helloworld"
parent_id = azurerm_resource_group.aca.id
location = azurerm_resource_group.aca.location
body = jsonencode({
properties = {
managedEnvironmentId = azapi_resource.env.id
configuration = {
ingress = {
allowInsecure = false
external = true
targetPort = 80
traffic = [
{
label = "dev"
latestRevision = true
weight = 100
}
]
}
}
template = {
containers = [
{
name = "helloworld"
image = "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest"
resources = {
cpu = 0.5
memory = "1.0Gi"
}
}
]
revisionSuffix = random_string.aca.result
scale = {
minReplicas = 0
maxReplicas = 30
rules = [
{
name = "http-rule"
http = {
metadata = {
concurrentRequests = "100"
}
}
}
]
}
}
}
})
# this tells azapi to pull out properties and stuff into the output attribute for the object
response_export_values = ["properties.configuration.ingress.fqdn"]
}
data "azurerm_subscription" "aca" {}
data "azurerm_client_config" "aca" {}
resource "azapi_resource" "amg" {
type = "Microsoft.Dashboard/grafana@2022-08-01"
name = "amg-${local.resource_name}"
parent_id = azurerm_resource_group.aca.id
location = azurerm_resource_group.aca.location
identity {
type = "SystemAssigned"
}
body = jsonencode({
properties = {
apiKey = "Enabled"
autoGeneratedDomainNameLabelScope = "TenantReuse"
deterministicOutboundIP = "Enabled"
publicNetworkAccess = "Enabled"
zoneRedundancy = "Disabled"
}
sku = {
name = "Standard"
}
})
# this tells azapi to pull out properties and stuff into the output attribute for the object
response_export_values = ["identity.principalId"]
}
resource "azurerm_role_assignment" "amg_reader" {
scope = data.azurerm_subscription.aca.id
role_definition_name = "Monitoring Reader"
principal_id = jsondecode(azapi_resource.amg.output).identity.principalId
}
resource "azurerm_role_assignment" "amg_admin" {
scope = azapi_resource.amg.id
role_definition_name = "Grafana Admin"
principal_id = data.azurerm_client_config.aca.object_id
}
output "helloworld_ingress_url" {
value = format("%s%s", "https://", jsondecode(azapi_resource.helloworld.output).properties.configuration.ingress.fqdn)
}
# optional to automate the dashboard import process
resource "null_resource" "aca" {
provisioner "local-exec" {
command = <<-EOT
az config set extension.use_dynamic_install=yes_without_prompt
az grafana dashboard import -g ${azurerm_resource_group.aca.name} -n ${azapi_resource.amg.name} --definition 16592
EOT
}
}
Resource References
- random_pet
- random_integer
- random_string
- azurerm_resource_group
- azurerm_log_analytics_workspace
- azurerm_subscription
- azurerm_role_assignment
- local-exec
- azapi_resource (Microsoft.App/managedEnvironments)
- azapi_resource (Microsoft.App/containerApps)
- azapi_resource (Microsoft.Dashboard/grafana)
- null_resource