Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 172 additions & 0 deletions modules/loki_blob/azure/1.0/facets.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
intent: loki_blob
flavor: azure
version: '1.0'
description: |
Creates Azure Blob Storage infrastructure for Loki log aggregation: a storage
account, blob container, user-assigned managed identity with Storage Blob Data
Contributor role, federated identity credential for AKS workload identity, and
an annotated Kubernetes service account for secretless pod authentication.
intentDetails:
type: Logging & Observability
description: Azure Blob-backed Loki storage with AKS workload identity federation
displayName: Loki Blob Storage
iconUrl: https://raw.githubusercontent.com/Facets-cloud/facets-modules-redesign/main/icons/monitoring.svg
clouds:
- azure
inputs:
cloud_account:
type: '@facets/azure_cloud_account'
displayName: Azure Cloud Account
description: Azure subscription and credential context for provisioning storage and identity resources.
optional: false
providers:
- azurerm
aks_cluster:
type: '@facets/azure_aks'
displayName: AKS Cluster
description: AKS cluster supplying the OIDC issuer URL for federated credentials and the Kubernetes provider for service account creation.
optional: false
default:
resource_type: kubernetes_cluster
resource_name: default
providers:
- kubernetes
- kubernetes-alpha
- helm
spec:
title: Loki Azure Blob Storage
description: Configure Azure Blob Storage infrastructure for Loki log aggregation
type: object
x-ui-order:
- storage_config
- workload_identity
- loki_config
- tags
properties:
storage_config:
type: object
title: Storage Configuration
x-ui-toggle: false
x-ui-order:
- container_name
- account_tier
- replication_type
- enable_versioning
- retention_days
properties:
container_name:
type: string
title: Blob Container Name
description: Name of the Azure Blob container used for Loki chunk and index storage.
default: loki-data
pattern: '^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$'
x-ui-placeholder: loki-data
account_tier:
type: string
title: Storage Account Tier
description: Performance tier for the Azure Storage Account.
enum:
- Standard
- Premium
default: Standard
replication_type:
type: string
title: Replication Type
description: Data replication strategy for the storage account.
enum:
- LRS
- GRS
- RAGRS
- ZRS
- GZRS
- RAGZRS
default: LRS
enable_versioning:
type: boolean
title: Enable Blob Versioning
description: Retain previous blob versions for point-in-time recovery.
default: false
retention_days:
type: integer
title: Retention Days
description: Number of days to retain Loki data blobs before automatic deletion. Set to 0 to disable expiry.
minimum: 0
maximum: 3650
default: 0
required:
- container_name
- account_tier
- replication_type
workload_identity:
type: object
title: Workload Identity
x-ui-toggle: false
x-ui-order:
- service_account_namespace
- service_account_name
properties:
service_account_namespace:
type: string
title: Kubernetes Namespace
description: Namespace in which the Loki Kubernetes service account will be created.
default: monitoring
pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'
x-ui-placeholder: monitoring
service_account_name:
type: string
title: Kubernetes Service Account Name
description: Name of the Kubernetes service account used by Loki pods.
default: loki
pattern: '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$'
x-ui-placeholder: loki
required:
- service_account_namespace
- service_account_name
loki_config:
type: object
title: Loki Configuration
x-ui-toggle: true
x-ui-overrides-only: true
properties:
query_timeout:
type: integer
title: Query Timeout (seconds)
description: Maximum time in seconds Loki will wait for a query to complete.
minimum: 10
maximum: 600
default: 60
required: []
tags:
type: object
title: Azure Tags
description: Additional tags applied to all Azure resources created by this module.
x-ui-yaml-editor: true
required:
- storage_config
- workload_identity
outputs:
default:
type: '@facets/loki_blob_storage'
title: Loki Azure Blob Storage Output
description: Storage account, container, and workload identity details for configuring the Loki Helm chart.
sample:
kind: loki_blob
flavor: azure
version: '1.0'
disabled: true
spec:
storage_config:
container_name: loki-data
account_tier: Standard
replication_type: LRS
enable_versioning: false
retention_days: 0
workload_identity:
service_account_namespace: monitoring
service_account_name: loki
iac:
validated_files:
- variables.tf
- locals.tf
- main.tf
- outputs.tf
53 changes: 53 additions & 0 deletions modules/loki_blob/azure/1.0/locals.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
locals {
# ── Spec shortcuts ─────────────────────────────────────────────────────────
storage_config = var.instance.spec.storage_config
workload_identity = var.instance.spec.workload_identity

# ── Storage configuration ──────────────────────────────────────────────────
container_name = local.storage_config.container_name
account_tier = local.storage_config.account_tier
replication_type = local.storage_config.replication_type
enable_versioning = local.storage_config.enable_versioning
retention_days = local.storage_config.retention_days
has_retention = local.retention_days > 0

# ── Workload identity / Kubernetes service account ─────────────────────────
k8s_sa_namespace = local.workload_identity.service_account_namespace
k8s_sa_name = local.workload_identity.service_account_name

# ── Loki query timeout (from optional nested object) ───────────────────────
query_timeout = var.instance.spec.loki_config != null ? var.instance.spec.loki_config.query_timeout : 60

# ── AKS cluster attributes (via lookup for safe optional access) ───────────
aks_attributes = lookup(var.inputs.aks_cluster, "attributes", {})
resource_group_name = lookup(local.aks_attributes, "resource_group_name", "")
location = lookup(local.aks_attributes, "cluster_location", "")
oidc_issuer_url = lookup(local.aks_attributes, "oidc_issuer_url", "")

# ── Storage account name: 3-24 chars, globally unique, lowercase alphanumeric only ──
# Azure storage account names cannot contain hyphens or underscores.
storage_account_name_raw = lower(
replace(replace("${var.environment.unique_name}${var.instance_name}", "-", ""), "_", "")
)
storage_account_name = substr(local.storage_account_name_raw, 0, 24)

# ── Managed identity resource name ────────────────────────────────────────
identity_name = "loki-id-${var.environment.unique_name}-${var.instance_name}"

# ── Federated credential name (must be unique per managed identity) ────────
federated_credential_name = "${var.instance_name}-loki-fed-cred"

# ── Tags ───────────────────────────────────────────────────────────────────
all_tags = merge(
var.environment.cloud_tags,
var.instance.spec.tags,
{
resource_type = "loki_blob"
resource_name = var.instance_name
flavor = "azure"
}
)

# ── Azure Blob endpoint suffix (used in Loki Helm chart config) ───────────
endpoint_suffix = "core.windows.net"
}
107 changes: 107 additions & 0 deletions modules/loki_blob/azure/1.0/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# ── Azure Storage Account for Loki log data ───────────────────────────────────
resource "azurerm_storage_account" "main" {
name = local.storage_account_name
resource_group_name = local.resource_group_name
location = local.location
account_tier = local.account_tier
account_replication_type = local.replication_type
min_tls_version = "TLS1_2"

blob_properties {
versioning_enabled = local.enable_versioning
}

tags = local.all_tags

lifecycle {
prevent_destroy = true
}
}

# ── Blob container for Loki chunks and indices ────────────────────────────────
resource "azurerm_storage_container" "loki" {
name = local.container_name
storage_account_id = azurerm_storage_account.main.id
container_access_type = "private"
}

# ── Lifecycle management policy (only created when retention_days > 0) ────────
resource "azurerm_storage_management_policy" "loki_retention" {
count = local.has_retention ? 1 : 0

storage_account_id = azurerm_storage_account.main.id

rule {
name = "loki-data-expiry"
enabled = true

filters {
blob_types = ["blockBlob"]
}

actions {
base_blob {
delete_after_days_since_modification_greater_than = local.retention_days
}
}
}

depends_on = [azurerm_storage_account.main]
}

# ── User-Assigned Managed Identity for Loki workload identity ─────────────────
resource "azurerm_user_assigned_identity" "main" {
name = local.identity_name
resource_group_name = local.resource_group_name
location = local.location
tags = local.all_tags
}

# ── Storage Blob Data Contributor: grants the managed identity read/write/delete
# on all blobs in the storage account (Azure RBAC equivalent of the S3 IAM policy) ──
resource "azurerm_role_assignment" "storage_blob_contributor" {
scope = azurerm_storage_account.main.id
role_definition_name = "Storage Blob Data Contributor"
principal_id = azurerm_user_assigned_identity.main.principal_id

depends_on = [azurerm_user_assigned_identity.main]
}

# ── Federated Identity Credential: trusts the AKS OIDC issuer to let the
# specified K8s service account impersonate the managed identity ─────────────
resource "azurerm_federated_identity_credential" "main" {
name = local.federated_credential_name
resource_group_name = local.resource_group_name
audience = ["api://AzureADTokenExchange"]
issuer = local.oidc_issuer_url
parent_id = azurerm_user_assigned_identity.main.id
subject = "system:serviceaccount:${local.k8s_sa_namespace}:${local.k8s_sa_name}"

depends_on = [azurerm_user_assigned_identity.main]
}

# ── Kubernetes Service Account annotated for Azure Workload Identity ───────────
# Loki pods must reference this service account to receive Azure AD tokens.
resource "kubernetes_service_account" "loki" {
metadata {
name = local.k8s_sa_name
namespace = local.k8s_sa_namespace

annotations = {
"azure.workload.identity/client-id" = azurerm_user_assigned_identity.main.client_id
}

labels = {
"azure.workload.identity/use" = "true"
resource_type = "loki_blob"
resource_name = var.instance_name
}
}

automount_service_account_token = false

depends_on = [
azurerm_user_assigned_identity.main,
azurerm_federated_identity_credential.main,
]
}
23 changes: 23 additions & 0 deletions modules/loki_blob/azure/1.0/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
locals {
output_attributes = {
# Storage
storage_account_name = azurerm_storage_account.main.name
storage_account_id = azurerm_storage_account.main.id
container_name = azurerm_storage_container.loki.name
endpoint_suffix = local.endpoint_suffix

# Managed identity (for Helm chart annotations and Loki storage config)
managed_identity_id = azurerm_user_assigned_identity.main.id
managed_identity_client_id = azurerm_user_assigned_identity.main.client_id
managed_identity_principal_id = azurerm_user_assigned_identity.main.principal_id

# Kubernetes service account (for Loki Helm chart serviceAccount config)
service_account_name = local.k8s_sa_name
service_account_namespace = local.k8s_sa_namespace

# Loki configuration hints
query_timeout = local.query_timeout
}

output_interfaces = {}
}
Loading