Azure infrastructure specialist for Platform as a Service Stack v3.0.0+. Expert in deterministic naming (MD5), RBAC-first security (uuidv5), feature flag orchestration, and Azure AD authentication. Always consults Microsoft Docs and Terraform provider MCP before ANY implementation to ensure latest best practices and avoid anti-patterns.
Azure infrastructure specialist for Platform as a Service Stack v3.0.0+. Expert in deterministic naming (MD5), RBAC-first security (uuidv5), feature flag orchestration, and Azure AD authentication. Always consults Microsoft Docs and Terraform provider MCP before ANY implementation to ensure latest best practices and avoid anti-patterns.
Azure Platform Stack Specialist
Overview
This skill provides expert guidance for Azure infrastructure operations in the Platform as a Service Stack v3.0.0+ environment. It focuses on deterministic resource provisioning, RBAC-first security, feature flag management, and ensuring compliance with platform-specific patterns (MD5 naming, uuidv5 role assignments, 180s RBAC propagation).
When to Use This Skill
Implementing new Azure resources for Platform Stack (Storage Account, SQL Server, Key Vault, Container Apps, etc.)
Debugging RBAC propagation delays or "permission denied" errors
# MI is pre-attached to Container Apps Environment
# ACR login_server is passed through automatically
module "container_apps" {
source = "./modules/workloads/container-apps"
count = var.enable_container_apps ? 1 : 0
# ... other config ...
managed_identity_id = var.enable_managed_identity ? module.managed_identity[0].id : null
container_registry_url = var.enable_container_registry ? module.container_registry[0].login_server : null
}
Outputs (individual, no resource IDs):
The root outputs.tf exposes only safe values: container_apps_environment_name, container_apps_environment_default_domain, container_apps_environment_static_ip, container_registry_name, container_registry_login_server. Resource IDs were removed because they expose the subscription ID.
**Key points:**
- External module: pinned at `ref=1.0.3` from `tfmodules-as-a-service-stack`
- Naming: `cr{name}{region}{md5}` (no hyphens — Azure ACR doesn't allow them)
- RBAC: AcrPush + AcrPull auto-assigned to Managed Identity via uuidv5
- Container Apps: MI pre-attached to Environment + ACR `login_server` passed through (zero-config pull)
- SKU validation: only `Basic`, `Standard`, or `Premium` accepted
### Multi-Subscription Provider Architecture
```terraform
# providers.tf
provider "azurerm" {
alias = "stefanininam"
subscription_id = var.stefanininam_subscription_id
tenant_id = var.stefanininam_tenant_id
client_id = var.stefanininam_client_id
client_secret = var.stefanininam_client_secret
features {
resource_group {
prevent_deletion_if_contains_resources = true
}
key_vault {
purge_soft_delete_on_destroy = false
recover_soft_deleted_key_vaults = true
}
}
}
provider "azurerm" {
alias = "devops"
subscription_id = var.devops_subscription_id
tenant_id = var.devops_tenant_id
client_id = var.devops_client_id
client_secret = var.devops_client_secret
features {}
}
provider "azurerm" {
alias = "sophie"
subscription_id = var.sophie_subscription_id
tenant_id = var.sophie_tenant_id
client_id = var.sophie_client_id
client_secret = var.sophie_client_secret
features {}
}
Standard pattern:<tenant>-<resource>-<environment>
Tenant prefixes:
na - North America (primary)
sophie - Sophie tenant
woopi - WoopiAI platform
dex - Data Exchange
emea - Europe/Middle East/Africa
latam - Latin America
Resource examples:
# AKS clusters
"na-aks-prod"
"sophie-aks-dev"
"woopi-aks-prod"
# Resource groups
"na-rg-network"
"sophie-rg-data"
"woopi-rg-storage"
# Storage accounts (lowercase, no hyphens due to Azure limits)
"stapplicationsautomation"
"stsophiedataprod"
"stwoopidatadev"
# Key Vaults
"na-kv-secrets-prod"
"sophie-kv-dev"
"woopi-kv-prod"
# AKS node pools
"system" (system pool)
"user" (user workloads)
"gpu" (GPU workloads)
Provider Version Management
See terraform-platform-instructions.md — Provider versions. Use ~> 4.64.0 for azurerm, ~> 1.14 for Terraform. Always pin with ~> constraint and check latest via MCP before generating code.
Security Standards
Production requirements:
Private endpoints for all PaaS services (Storage, Key Vault, SQL, ACR)
Managed Identity instead of Service Principal
Azure AD RBAC enabled on AKS
Network security groups (NSGs) on all subnets
Private DNS zones for private endpoints
Customer-Managed Keys (CMK) for encryption at rest
Azure Policy for governance
Development/QA:
Can use public endpoints with IP restrictions
Still use Managed Identity where possible
Network security still required
2. Creating AKS Cluster
MCP workflow:
Search: "AKS private cluster terraform best practices"
Get samples: "AKS private cluster" with language="terraform"
Get provider: azurerm kubernetes_cluster resource details
# Verify environment variables
echo $env:ARM_SUBSCRIPTION_ID
echo $env:ARM_TENANT_ID
echo $env:ARM_CLIENT_ID
# ARM_CLIENT_SECRET should be set but not echoed
# Verify service principal exists
az ad sp show --id $env:ARM_CLIENT_ID
# Test authentication
az login --service-principal `
--username $env:ARM_CLIENT_ID `
--password $env:ARM_CLIENT_SECRET `
--tenant $env:ARM_TENANT_ID
# Check subscription access
az account show
az account list --all
# Check RBAC role assignments
az role assignment list --assignee $env:ARM_CLIENT_ID
Common fixes:
Verify service principal has Contributor role on subscription
Check service principal hasn't expired
Validate client secret is current
Ensure correct tenant ID and subscription ID
Networking Issues
Symptoms:
"Error: timeout while waiting for state"
"Error: unable to connect to backend"
Private endpoint connection failures
MCP workflow:
Search: "Azure private endpoint troubleshooting"
Review NSG and route table configurations
Debugging steps:
# Check NSG rules
az network nsg show --resource-group <rg> --name <nsg-name>
# Check route table
az network route-table show --resource-group <rg> --name <rt-name>
# Check private DNS zone
az network private-dns zone show --resource-group <rg> --name <zone-name>
# Test DNS resolution
nslookup <resource>.privatelink.blob.core.windows.net
# Check service endpoint
az network vnet subnet show --resource-group <rg> --vnet-name <vnet> --name <subnet>
State Lock Issues
Symptoms:
"Error: Error locking state"
"Error: state blob is already locked"
MCP workflow:
Search: "Terraform Azure blob storage state lock"
Resolution:
# Check blob lease status
az storage blob show `
--account-name storagepaas `
--container-name tfstate `
--name <tenant>-<environment>.tfstate `
--query properties.lease
# Break lease if stuck (CAUTION: verify no one is running terraform)
az storage blob lease break `
--blob-name <tenant>-<environment>.tfstate `
--container-name tfstate `
--account-name storagepaas
Resource Already Exists
Symptoms:
"Error: A resource with the ID already exists"
MCP workflow:
Check if resource exists in Azure Portal
Decide: import or remove
Option 1: Import existing resource:
# Get resource ID from Azure
az resource show --resource-group <rg> --name <name> --resource-type <type>
# Import into Terraform state
terraform import azurerm_resource_group.main /subscriptions/<sub-id>/resourceGroups/<rg-name>
# Verify
terraform plan # Should show no changes