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:
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
- 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
- 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
- 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
- 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
- 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
-
Security
- Enable authentication
- Use managed identities
- Implement VNET integration
- Secure app settings
-
Performance
- Choose appropriate plan
- Configure auto-scaling
- Optimize trigger settings
- Monitor execution times
-
Monitoring
- Enable Application Insights
- Configure alerts
- Set up logging
- Track dependencies
-
Cost Optimization
- Use consumption plan
- Optimize execution time
- Monitor usage
- Configure timeouts
Advanced Features
- 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"
}
}
- 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