Managing Azure API Management with Terraform

Learn how to set up and manage Azure API Management using Terraform, including APIs, policies, and security configurations

Managing Azure API Management with Terraform

Azure API Management (APIM) is a fully managed service that helps organizations publish, secure, transform, maintain, and monitor APIs. This guide demonstrates how to set up and manage APIM using Terraform.

Video Tutorial

Learn more about managing Azure API Management with Terraform in this comprehensive video tutorial:

Prerequisites

  • Azure CLI configured with appropriate permissions
  • Terraform installed (version 1.0.0 or later)
  • Resource group created
  • Understanding of API management concepts

Project Structure

terraform-azure-apim/
├── main.tf
├── variables.tf
├── outputs.tf
├── modules/
│   └── apim/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
└── policies/
    ├── global.xml
    ├── product.xml
    └── api.xml

API Management Configuration

Create modules/apim/main.tf:

# API Management Service
resource "azurerm_api_management" "main" {
  name                = "${var.project_name}-apim"
  location            = var.location
  resource_group_name = var.resource_group_name
  publisher_name      = var.publisher_name
  publisher_email     = var.publisher_email
  sku_name            = "Premium_1"
  zones               = ["1", "2", "3"]

  identity {
    type = "SystemAssigned"
  }

  hostname_configuration {
    proxy {
      host_name                    = "api.${var.domain_name}"
      key_vault_id                = azurerm_key_vault_certificate.api.id
      default_ssl_binding         = true
      negotiate_client_certificate = false
    }

    portal {
      host_name        = "portal.${var.domain_name}"
      key_vault_id    = azurerm_key_vault_certificate.portal.id
    }

    developer_portal {
      host_name     = "developer.${var.domain_name}"
      key_vault_id = azurerm_key_vault_certificate.developer.id
    }
  }

  security {
    enable_backend_ssl30  = false
    enable_backend_tls10  = false
    enable_backend_tls11  = false
    enable_frontend_ssl30 = false
    enable_frontend_tls10 = false
    enable_frontend_tls11 = false

    enable_http2 = true
  }

  protocols {
    enable_http2 = true
  }

  sign_in {
    enabled = true
  }

  sign_up {
    enabled = true
    terms_of_service {
      enabled            = true
      consent_required   = true
      text              = file("${path.module}/terms.html")
    }
  }

  public_network_access_enabled = false

  virtual_network_configuration {
    subnet_id = azurerm_subnet.apim.id
  }

  tags = var.tags
}

# Products
resource "azurerm_api_management_product" "main" {
  for_each             = var.products
  product_id           = each.key
  api_management_name  = azurerm_api_management.main.name
  resource_group_name  = var.resource_group_name
  display_name         = each.value.display_name
  description         = each.value.description
  subscription_required = true
  approval_required    = each.value.approval_required
  published           = true
}

# APIs
resource "azurerm_api_management_api" "main" {
  for_each            = var.apis
  name                = each.key
  resource_group_name = var.resource_group_name
  api_management_name = azurerm_api_management.main.name
  revision            = "1"
  display_name        = each.value.display_name
  path                = each.value.path
  protocols           = ["https"]
  service_url         = each.value.backend_url

  import {
    content_format = "openapi+json"
    content_value  = file(each.value.swagger_path)
  }

  subscription_key_parameter_names {
    header = "Ocp-Apim-Subscription-Key"
    query  = "subscription-key"
  }
}

# API Policy
resource "azurerm_api_management_api_policy" "main" {
  for_each            = var.apis
  api_name            = azurerm_api_management_api.main[each.key].name
  api_management_name = azurerm_api_management.main.name
  resource_group_name = var.resource_group_name

  xml_content = file("${path.module}/policies/api/${each.key}.xml")
}

# Product Policy
resource "azurerm_api_management_product_policy" "main" {
  for_each            = var.products
  product_id          = azurerm_api_management_product.main[each.key].product_id
  api_management_name = azurerm_api_management.main.name
  resource_group_name = var.resource_group_name

  xml_content = file("${path.module}/policies/product/${each.key}.xml")
}

# Global Policy
resource "azurerm_api_management_policy" "main" {
  api_management_name = azurerm_api_management.main.name
  resource_group_name = var.resource_group_name

  xml_content = file("${path.module}/policies/global.xml")
}

# Named Values
resource "azurerm_api_management_named_value" "main" {
  for_each            = var.named_values
  name                = each.key
  resource_group_name = var.resource_group_name
  api_management_name = azurerm_api_management.main.name
  display_name        = each.value.display_name
  secret             = each.value.secret
  value              = each.value.secret ? data.azurerm_key_vault_secret.named_values[each.key].value : each.value.value
}

# Logger
resource "azurerm_api_management_logger" "main" {
  name                = "${var.project_name}-logger"
  api_management_name = azurerm_api_management.main.name
  resource_group_name = var.resource_group_name
  resource_id         = azurerm_application_insights.main.id

  application_insights {
    instrumentation_key = azurerm_application_insights.main.instrumentation_key
  }
}

Network Configuration

  1. Virtual Network Integration
resource "azurerm_virtual_network" "main" {
  name                = "${var.project_name}-vnet"
  location            = var.location
  resource_group_name = var.resource_group_name
  address_space       = ["10.0.0.0/16"]

  subnet {
    name           = "apim-subnet"
    address_prefix = "10.0.1.0/24"
    service_endpoints = [
      "Microsoft.KeyVault",
      "Microsoft.EventHub",
      "Microsoft.ServiceBus",
      "Microsoft.Sql"
    ]
  }

  tags = var.tags
}

resource "azurerm_private_endpoint" "apim" {
  name                = "${var.project_name}-pe"
  location            = var.location
  resource_group_name = var.resource_group_name
  subnet_id           = azurerm_virtual_network.main.subnet.*.id[0]

  private_service_connection {
    name                           = "${var.project_name}-psc"
    private_connection_resource_id = azurerm_api_management.main.id
    is_manual_connection          = false
    subresource_names            = ["gateway"]
  }

  private_dns_zone_group {
    name                 = "default"
    private_dns_zone_ids = [azurerm_private_dns_zone.apim.id]
  }
}

resource "azurerm_private_dns_zone" "apim" {
  name                = "privatelink.azure-api.net"
  resource_group_name = var.resource_group_name
}

resource "azurerm_private_dns_zone_virtual_network_link" "apim" {
  name                  = "${var.project_name}-vnet-link"
  resource_group_name   = var.resource_group_name
  private_dns_zone_name = azurerm_private_dns_zone.apim.name
  virtual_network_id    = azurerm_virtual_network.main.id
}

Security Configuration

  1. Role Assignments
resource "azurerm_role_assignment" "apim_contributor" {
  scope                = azurerm_api_management.main.id
  role_definition_name = "API Management Service Contributor"
  principal_id         = var.contributor_principal_id
}

resource "azurerm_role_assignment" "apim_reader" {
  scope                = azurerm_api_management.main.id
  role_definition_name = "API Management Service Reader Role"
  principal_id         = var.reader_principal_id
}
  1. Managed Identity
resource "azurerm_user_assigned_identity" "apim" {
  name                = "${var.project_name}-identity"
  resource_group_name = var.resource_group_name
  location            = var.location
}

resource "azurerm_key_vault_access_policy" "apim" {
  key_vault_id = var.key_vault_id
  tenant_id    = data.azurerm_client_config.current.tenant_id
  object_id    = azurerm_user_assigned_identity.apim.principal_id

  certificate_permissions = [
    "Get",
    "List"
  ]

  secret_permissions = [
    "Get",
    "List"
  ]
}

Monitoring Configuration

  1. Diagnostic Settings
resource "azurerm_monitor_diagnostic_setting" "apim" {
  name                       = "${var.project_name}-diag"
  target_resource_id        = azurerm_api_management.main.id
  log_analytics_workspace_id = var.log_analytics_workspace_id

  log {
    category = "GatewayLogs"
    enabled  = true

    retention_policy {
      enabled = true
      days    = 30
    }
  }

  metric {
    category = "AllMetrics"
    enabled  = true

    retention_policy {
      enabled = true
      days    = 30
    }
  }
}

resource "azurerm_monitor_metric_alert" "apim" {
  name                = "${var.project_name}-latency-alert"
  resource_group_name = var.resource_group_name
  scopes              = [azurerm_api_management.main.id]
  description         = "Alert when API latency is high"

  criteria {
    metric_namespace = "Microsoft.ApiManagement/service"
    metric_name      = "Duration"
    aggregation      = "Average"
    operator         = "GreaterThan"
    threshold        = 1000
  }

  action {
    action_group_id = var.action_group_id
  }
}

Advanced Features

  1. OAuth2 Configuration
resource "azurerm_api_management_authorization_server" "main" {
  name                         = "oauth2"
  api_management_name         = azurerm_api_management.main.name
  resource_group_name         = var.resource_group_name
  display_name               = "OAuth 2.0 Authorization Server"
  authorization_endpoint     = "https://login.microsoftonline.com/${data.azurerm_client_config.current.tenant_id}/oauth2/v2.0/authorize"
  token_endpoint            = "https://login.microsoftonline.com/${data.azurerm_client_config.current.tenant_id}/oauth2/v2.0/token"
  client_registration_endpoint = "https://portal.${var.domain_name}/signup"
  grant_types               = ["authorization_code", "implicit", "client_credentials"]

  authorization_methods = ["GET", "POST"]

  client_authentication_method = ["Basic"]

  bearer_token_sending_methods = ["authorizationHeader"]
}
  1. Cache Configuration
resource "azurerm_api_management_cache" "main" {
  name                = "${var.project_name}-cache"
  api_management_name = azurerm_api_management.main.name
  resource_group_name = var.resource_group_name
  location            = var.location
  connection_string   = azurerm_redis_cache.main.primary_connection_string
  description        = "API response cache"
  use_from_location  = var.location
}

Best Practices

  1. Performance

    • Use caching
    • Enable HTTP/2
    • Configure rate limiting
    • Monitor latency
  2. Security

    • Enable private endpoints
    • Use RBAC
    • Implement OAuth2
    • Enable TLS 1.2+
  3. Monitoring

    • Configure logging
    • Set up alerts
    • Monitor usage
    • Track errors
  4. Cost Optimization

    • Monitor usage
    • Use caching
    • Optimize policies

Conclusion

You’ve learned how to set up and manage Azure API Management using Terraform. This setup provides:

  • API gateway functionality
  • Security and policies
  • Monitoring and caching
  • Developer portal

Remember to:

  • Monitor API usage
  • Review security settings
  • Update policies
  • Maintain documentation