Skip to main content
Glama
main.tf16.1 kB
terraform { required_version = ">= 1.5.0" required_providers { azurerm = { source = "hashicorp/azurerm" version = "~> 3.80" } azuread = { source = "hashicorp/azuread" version = "~> 2.45" } random = { source = "hashicorp/random" version = "~> 3.5" } } backend "azurerm" { resource_group_name = "rg-terraform-state" storage_account_name = "stterraformstate" container_name = "tfstate" key = "azure-ai-mcp-server/prod/terraform.tfstate" } } provider "azurerm" { features { resource_group { prevent_deletion_if_contains_resources = false } key_vault { purge_soft_delete_on_destroy = true recover_soft_deleted_key_vaults = true } } } provider "azuread" {} # Local variables locals { environment = "prod" project = "azure-ai-mcp-server" location = var.location common_tags = { Environment = local.environment Project = local.project ManagedBy = "Terraform" Owner = "pXLabs" CostCenter = "Engineering" } # Resource naming convention resource_prefix = "${local.project}-${local.environment}" } # Data sources data "azurerm_client_config" "current" {} data "azuread_client_config" "current" {} # Resource Group resource "azurerm_resource_group" "main" { name = "rg-${local.resource_prefix}" location = local.location tags = local.common_tags } # Key Vault for secrets management resource "azurerm_key_vault" "main" { name = "kv-${local.resource_prefix}-${random_string.suffix.result}" location = azurerm_resource_group.main.location resource_group_name = azurerm_resource_group.main.name tenant_id = data.azurerm_client_config.current.tenant_id sku_name = "premium" enabled_for_deployment = true enabled_for_disk_encryption = true enabled_for_template_deployment = true enable_rbac_authorization = true purge_protection_enabled = true soft_delete_retention_days = 90 network_acls { default_action = "Deny" bypass = "AzureServices" ip_rules = var.allowed_ip_ranges } tags = local.common_tags } # Random string for unique naming resource "random_string" "suffix" { length = 8 special = false upper = false } # Container Registry resource "azurerm_container_registry" "main" { name = "acr${replace(local.resource_prefix, "-", "")}${random_string.suffix.result}" resource_group_name = azurerm_resource_group.main.name location = azurerm_resource_group.main.location sku = "Premium" admin_enabled = false identity { type = "SystemAssigned" } encryption { enabled = true key_vault_key_id = azurerm_key_vault_key.acr_encryption.id identity_client_id = azurerm_user_assigned_identity.acr_encryption.client_id } network_rule_set { default_action = "Deny" ip_rule { action = "Allow" ip_range = "0.0.0.0/0" # Restrict this in production } } tags = local.common_tags } # User Assigned Identity for ACR encryption resource "azurerm_user_assigned_identity" "acr_encryption" { name = "id-acr-encryption-${local.resource_prefix}" resource_group_name = azurerm_resource_group.main.name location = azurerm_resource_group.main.location tags = local.common_tags } # Key Vault Key for ACR encryption resource "azurerm_key_vault_key" "acr_encryption" { name = "acr-encryption-key" key_vault_id = azurerm_key_vault.main.id key_type = "RSA" key_size = 2048 key_opts = [ "decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey", ] depends_on = [azurerm_role_assignment.kv_admin] } # Role assignments for Key Vault resource "azurerm_role_assignment" "kv_admin" { scope = azurerm_key_vault.main.id role_definition_name = "Key Vault Administrator" principal_id = data.azurerm_client_config.current.object_id } resource "azurerm_role_assignment" "acr_encryption_kv" { scope = azurerm_key_vault.main.id role_definition_name = "Key Vault Crypto Service Encryption User" principal_id = azurerm_user_assigned_identity.acr_encryption.principal_id } # Log Analytics Workspace resource "azurerm_log_analytics_workspace" "main" { name = "law-${local.resource_prefix}" location = azurerm_resource_group.main.location resource_group_name = azurerm_resource_group.main.name sku = "PerGB2018" retention_in_days = 90 daily_quota_gb = 10 tags = local.common_tags } # Application Insights resource "azurerm_application_insights" "main" { name = "ai-${local.resource_prefix}" location = azurerm_resource_group.main.location resource_group_name = azurerm_resource_group.main.name workspace_id = azurerm_log_analytics_workspace.main.id application_type = "Node.JS" tags = local.common_tags } # Azure OpenAI Service resource "azurerm_cognitive_account" "openai" { name = "cog-openai-${local.resource_prefix}" location = var.openai_location resource_group_name = azurerm_resource_group.main.name kind = "OpenAI" sku_name = "S0" identity { type = "SystemAssigned" } network_acls { default_action = "Deny" ip_rules = var.allowed_ip_ranges } tags = local.common_tags } # Azure OpenAI Deployments resource "azurerm_cognitive_deployment" "gpt4" { name = "gpt-4" cognitive_account_id = azurerm_cognitive_account.openai.id model { format = "OpenAI" name = "gpt-4" version = "0613" } scale { type = "Standard" capacity = 10 } } resource "azurerm_cognitive_deployment" "gpt35_turbo" { name = "gpt-35-turbo" cognitive_account_id = azurerm_cognitive_account.openai.id model { format = "OpenAI" name = "gpt-35-turbo" version = "0613" } scale { type = "Standard" capacity = 20 } } resource "azurerm_cognitive_deployment" "text_embedding" { name = "text-embedding-ada-002" cognitive_account_id = azurerm_cognitive_account.openai.id model { format = "OpenAI" name = "text-embedding-ada-002" version = "2" } scale { type = "Standard" capacity = 10 } } # Cognitive Services for Text Analytics, Computer Vision, Face API resource "azurerm_cognitive_account" "text_analytics" { name = "cog-text-${local.resource_prefix}" location = azurerm_resource_group.main.location resource_group_name = azurerm_resource_group.main.name kind = "TextAnalytics" sku_name = "S" identity { type = "SystemAssigned" } network_acls { default_action = "Deny" ip_rules = var.allowed_ip_ranges } tags = local.common_tags } resource "azurerm_cognitive_account" "computer_vision" { name = "cog-vision-${local.resource_prefix}" location = azurerm_resource_group.main.location resource_group_name = azurerm_resource_group.main.name kind = "ComputerVision" sku_name = "S1" identity { type = "SystemAssigned" } network_acls { default_action = "Deny" ip_rules = var.allowed_ip_ranges } tags = local.common_tags } resource "azurerm_cognitive_account" "face" { name = "cog-face-${local.resource_prefix}" location = azurerm_resource_group.main.location resource_group_name = azurerm_resource_group.main.name kind = "Face" sku_name = "S0" identity { type = "SystemAssigned" } network_acls { default_action = "Deny" ip_rules = var.allowed_ip_ranges } tags = local.common_tags } # Storage Account resource "azurerm_storage_account" "main" { name = "st${replace(local.resource_prefix, "-", "")}${random_string.suffix.result}" resource_group_name = azurerm_resource_group.main.name location = azurerm_resource_group.main.location account_tier = "Standard" account_replication_type = "ZRS" account_kind = "StorageV2" identity { type = "SystemAssigned" } blob_properties { versioning_enabled = true change_feed_enabled = true change_feed_retention_in_days = 7 delete_retention_policy { days = 30 } container_delete_retention_policy { days = 30 } } network_rules { default_action = "Deny" ip_rules = var.allowed_ip_ranges bypass = ["AzureServices"] } tags = local.common_tags } # Container Apps Environment resource "azurerm_container_app_environment" "main" { name = "cae-${local.resource_prefix}" location = azurerm_resource_group.main.location resource_group_name = azurerm_resource_group.main.name log_analytics_workspace_id = azurerm_log_analytics_workspace.main.id tags = local.common_tags } # Container App for Blue-Green Deployment resource "azurerm_container_app" "main" { name = "ca-${local.resource_prefix}" container_app_environment_id = azurerm_container_app_environment.main.id resource_group_name = azurerm_resource_group.main.name revision_mode = "Multiple" identity { type = "SystemAssigned" } registry { server = azurerm_container_registry.main.login_server identity = azurerm_container_app.main.identity[0].principal_id } template { min_replicas = 2 max_replicas = 10 container { name = "azure-ai-mcp-server" image = "${azurerm_container_registry.main.login_server}/azure-ai-mcp-server:${var.image_tag}" cpu = 1.0 memory = "2Gi" env { name = "AZURE_OPENAI_ENDPOINT" value = azurerm_cognitive_account.openai.endpoint } env { name = "AZURE_OPENAI_API_KEY" secret_name = "azure-openai-api-key" } env { name = "AZURE_COGNITIVE_SERVICES_ENDPOINT" value = azurerm_cognitive_account.text_analytics.endpoint } env { name = "AZURE_COGNITIVE_SERVICES_KEY" secret_name = "azure-cognitive-services-key" } env { name = "AZURE_STORAGE_CONNECTION_STRING" secret_name = "azure-storage-connection-string" } env { name = "AZURE_APPLICATION_INSIGHTS_CONNECTION_STRING" value = azurerm_application_insights.main.connection_string } env { name = "LOG_LEVEL" value = "info" } liveness_probe { transport = "HTTP" port = 3000 path = "/health" } readiness_probe { transport = "HTTP" port = 3000 path = "/ready" } } http_scale_rule { name = "http-scale" concurrent_requests = 100 } } secret { name = "azure-openai-api-key" value = azurerm_cognitive_account.openai.primary_access_key } secret { name = "azure-cognitive-services-key" value = azurerm_cognitive_account.text_analytics.primary_access_key } secret { name = "azure-storage-connection-string" value = azurerm_storage_account.main.primary_connection_string } ingress { allow_insecure_connections = false external_enabled = true target_port = 3000 transport = "http" traffic_weight { percentage = var.switch_traffic ? 100 : 0 latest_revision = var.deployment_slot == "blue" } } tags = local.common_tags } # Chaos Studio Target resource "azurerm_chaos_studio_target" "container_app" { location = azurerm_resource_group.main.location target_resource_id = azurerm_container_app.main.id target_type = "Microsoft-ContainerApp" } # Chaos Studio Capability resource "azurerm_chaos_studio_capability" "container_app_stop" { chaos_studio_target_id = azurerm_chaos_studio_target.container_app.id capability_type = "Stop-1.0" } # Action Group for Alerts resource "azurerm_monitor_action_group" "main" { name = "ag-${local.resource_prefix}" resource_group_name = azurerm_resource_group.main.name short_name = "pxlabs" email_receiver { name = "admin" email_address = var.alert_email } webhook_receiver { name = "slack" service_uri = var.slack_webhook_url } tags = local.common_tags } # Metric Alerts resource "azurerm_monitor_metric_alert" "high_error_rate" { name = "High Error Rate - ${local.resource_prefix}" resource_group_name = azurerm_resource_group.main.name scopes = [azurerm_container_app.main.id] description = "Alert when error rate exceeds 5%" severity = 2 frequency = "PT1M" window_size = "PT5M" criteria { metric_namespace = "Microsoft.App/containerApps" metric_name = "Requests" aggregation = "Total" operator = "GreaterThan" threshold = 5 dimension { name = "StatusCodeClass" operator = "Include" values = ["5xx"] } } action { action_group_id = azurerm_monitor_action_group.main.id } tags = local.common_tags } resource "azurerm_monitor_metric_alert" "high_response_time" { name = "High Response Time - ${local.resource_prefix}" resource_group_name = azurerm_resource_group.main.name scopes = [azurerm_application_insights.main.id] description = "Alert when average response time exceeds 1 second" severity = 2 frequency = "PT1M" window_size = "PT5M" criteria { metric_namespace = "Microsoft.Insights/components" metric_name = "requests/duration" aggregation = "Average" operator = "GreaterThan" threshold = 1000 } action { action_group_id = azurerm_monitor_action_group.main.id } tags = local.common_tags } # Role assignments for Container App to access ACR resource "azurerm_role_assignment" "container_app_acr" { scope = azurerm_container_registry.main.id role_definition_name = "AcrPull" principal_id = azurerm_container_app.main.identity[0].principal_id } # Role assignments for Container App to access Cognitive Services resource "azurerm_role_assignment" "container_app_openai" { scope = azurerm_cognitive_account.openai.id role_definition_name = "Cognitive Services OpenAI User" principal_id = azurerm_container_app.main.identity[0].principal_id } resource "azurerm_role_assignment" "container_app_cognitive" { scope = azurerm_cognitive_account.text_analytics.id role_definition_name = "Cognitive Services User" principal_id = azurerm_container_app.main.identity[0].principal_id } # Store secrets in Key Vault resource "azurerm_key_vault_secret" "openai_key" { name = "azure-openai-api-key" value = azurerm_cognitive_account.openai.primary_access_key key_vault_id = azurerm_key_vault.main.id depends_on = [azurerm_role_assignment.kv_admin] } resource "azurerm_key_vault_secret" "cognitive_key" { name = "azure-cognitive-services-key" value = azurerm_cognitive_account.text_analytics.primary_access_key key_vault_id = azurerm_key_vault.main.id depends_on = [azurerm_role_assignment.kv_admin] } resource "azurerm_key_vault_secret" "storage_connection" { name = "azure-storage-connection-string" value = azurerm_storage_account.main.primary_connection_string key_vault_id = azurerm_key_vault.main.id depends_on = [azurerm_role_assignment.kv_admin] }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/caiotk/nexguideai-azure-ai-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server