Deploying Azure Functions with Terraform

Learn how to set up and manage Azure Functions using Terraform, including triggers, bindings, and best practices

Deploying Azure Functions with Terraform

Azure Functions is a serverless compute service that lets you run event-triggered code without managing infrastructure. This guide demonstrates how to set up and manage Azure Functions using Terraform.

Video Tutorial

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

View Source Code

Prerequisites

  • Azure CLI configured with appropriate permissions
  • Terraform installed (version 1.0.0 or later)
  • Resource group created
  • Function app code ready for deployment

Project Structure

terraform-azure-functions/
├── main.tf
├── variables.tf
├── outputs.tf
├── modules/
│   └── function/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
└── functions/
    └── src/

Function App Configuration

Create modules/function/main.tf:

# Storage Account
resource "azurerm_storage_account" "main" {
  name                     = "${var.project_name}sa"
  resource_group_name      = var.resource_group_name
  location                 = var.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
  
  min_tls_version         = "TLS1_2"
  enable_https_traffic_only = true

  network_rules {
    default_action = "Deny"
    ip_rules       = var.allowed_ip_ranges
    virtual_network_subnet_ids = [azurerm_subnet.function.id]
    bypass         = ["AzureServices"]
  }

  tags = var.tags
}

# App Service Plan
resource "azurerm_service_plan" "main" {
  name                = "${var.project_name}-plan"
  location            = var.location
  resource_group_name = var.resource_group_name
  os_type             = "Linux"
  sku_name            = var.sku_name

  tags = var.tags
}

# Function App
resource "azurerm_linux_function_app" "main" {
  name                       = "${var.project_name}-func"
  location                   = var.location
  resource_group_name        = var.resource_group_name
  service_plan_id            = azurerm_service_plan.main.id
  storage_account_name       = azurerm_storage_account.main.name
  storage_account_access_key = azurerm_storage_account.main.primary_access_key

  site_config {
    always_on          = true
    http2_enabled      = true
    
    application_stack {
      node_version = "18"  # Or your preferred runtime
    }

    cors {
      allowed_origins = var.cors_allowed_origins
    }

    ip_restriction {
      ip_address = var.allowed_ip_range
      priority   = 100
      action     = "Allow"
    }
  }

  app_settings = {
    "FUNCTIONS_WORKER_RUNTIME"       = "node"
    "WEBSITE_NODE_DEFAULT_VERSION"   = "~18"
    "APPINSIGHTS_INSTRUMENTATIONKEY" = azurerm_application_insights.main.instrumentation_key
    "WEBSITE_RUN_FROM_PACKAGE"       = "1"
    "AzureWebJobsDisableHomepage"    = "true"
    "FUNCTION_APP_EDIT_MODE"         = "readonly"
    "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING" = azurerm_storage_account.main.primary_connection_string
    "WEBSITE_CONTENTSHARE"           = lower(var.project_name)
  }

  identity {
    type = "SystemAssigned"
  }

  auth_settings {
    enabled                       = true
    default_provider             = "AzureActiveDirectory"
    unauthenticated_client_action = "RedirectToLoginPage"

    active_directory {
      client_id         = var.aad_client_id
      client_secret     = var.aad_client_secret
      allowed_audiences = [var.app_url]
    }
  }

  tags = var.tags
}

# Application Insights
resource "azurerm_application_insights" "main" {
  name                = "${var.project_name}-appinsights"
  location            = var.location
  resource_group_name = var.resource_group_name
  application_type    = "web"

  tags = var.tags
}

Event Grid Integration

  1. Event Grid Topic
resource "azurerm_eventgrid_topic" "main" {
  name                = "${var.project_name}-topic"
  location            = var.location
  resource_group_name = var.resource_group_name

  tags = var.tags
}

resource "azurerm_eventgrid_event_subscription" "function" {
  name                = "${var.project_name}-subscription"
  scope               = azurerm_eventgrid_topic.main.id
  included_event_types = ["Microsoft.Storage.BlobCreated"]

  azure_function_endpoint {
    function_id = "${azurerm_linux_function_app.main.id}/functions/ProcessBlob"
    max_events_per_batch = 1
    preferred_batch_size_in_kilobytes = 64
  }

  retry_policy {
    max_delivery_attempts = 30
    event_time_to_live   = 1440
  }
}

Service Bus Integration

  1. Service Bus Configuration
resource "azurerm_servicebus_namespace" "main" {
  name                = "${var.project_name}-sb"
  location            = var.location
  resource_group_name = var.resource_group_name
  sku                 = "Standard"

  tags = var.tags
}

resource "azurerm_servicebus_queue" "main" {
  name         = "example-queue"
  namespace_id = azurerm_servicebus_namespace.main.id

  enable_partitioning = true
  max_size_in_megabytes = 1024
}

resource "azurerm_role_assignment" "function_servicebus" {
  scope                = azurerm_servicebus_namespace.main.id
  role_definition_name = "Azure Service Bus Data Receiver"
  principal_id         = azurerm_linux_function_app.main.identity[0].principal_id
}

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           = "function-subnet"
    address_prefix = "10.0.1.0/24"

    delegation {
      name = "function"
      
      service_delegation {
        name    = "Microsoft.Web/serverFarms"
        actions = ["Microsoft.Network/virtualNetworks/subnets/action"]
      }
    }
  }

  tags = var.tags
}

resource "azurerm_app_service_virtual_network_swift_connection" "main" {
  app_service_id = azurerm_linux_function_app.main.id
  subnet_id      = azurerm_virtual_network.main.subnet.*.id[0]
}

Monitoring Configuration

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

  log {
    category = "FunctionAppLogs"
    enabled  = true

    retention_policy {
      enabled = true
      days    = 30
    }
  }

  metric {
    category = "AllMetrics"
    enabled  = true

    retention_policy {
      enabled = true
      days    = 30
    }
  }
}

resource "azurerm_monitor_metric_alert" "execution_count" {
  name                = "${var.project_name}-execution-alert"
  resource_group_name = var.resource_group_name
  scopes              = [azurerm_linux_function_app.main.id]
  description         = "Alert when function execution count is high"

  criteria {
    metric_namespace = "Microsoft.Web/sites"
    metric_name      = "FunctionExecutionCount"
    aggregation      = "Total"
    operator         = "GreaterThan"
    threshold        = 1000
  }

  action {
    action_group_id = var.action_group_id
  }
}

Key Vault Integration

  1. Key Vault Access
resource "azurerm_key_vault_access_policy" "function" {
  key_vault_id = var.key_vault_id
  tenant_id    = data.azurerm_client_config.current.tenant_id
  object_id    = azurerm_linux_function_app.main.identity[0].principal_id

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

resource "azurerm_app_service_key_vault_access_policy" "main" {
  key_vault_id = var.key_vault_id
  app_service_id = azurerm_linux_function_app.main.id
}

Best Practices

  1. Security

    • Enable authentication
    • Use managed identities
    • Implement VNET integration
    • Secure app settings
  2. Performance

    • Choose appropriate plan
    • Configure auto-scaling
    • Optimize trigger settings
    • Monitor execution times
  3. Monitoring

    • Enable Application Insights
    • Configure alerts
    • Set up logging
    • Track dependencies
  4. Cost Optimization

    • Use consumption plan
    • Optimize execution time
    • Monitor usage
    • Configure timeouts

Advanced Features

  1. Durable Functions
resource "azurerm_storage_table" "durable" {
  name                 = "DurableFunctionsHubHistory"
  storage_account_name = azurerm_storage_account.main.name
}

resource "azurerm_app_service_app_settings" "durable" {
  app_service_id = azurerm_linux_function_app.main.id
  
  app_settings = {
    "AzureWebJobsStorage" = azurerm_storage_account.main.primary_connection_string
    "DurableFunctionsHubName" = "TaskHub"
  }
}
  1. Premium Plan Features
resource "azurerm_service_plan" "premium" {
  name                = "${var.project_name}-premium-plan"
  location            = var.location
  resource_group_name = var.resource_group_name
  os_type             = "Linux"
  sku_name            = "EP1"

  maximum_elastic_worker_count = 20

  tags = var.tags
}

Conclusion

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

  • Serverless compute capabilities
  • Event-driven architecture
  • Monitoring and alerts
  • Integration with other Azure services

Remember to:

  • Monitor performance
  • Implement security best practices
  • Optimize costs
  • Use appropriate triggers and bindings