Managing Azure Logic Apps with Terraform

Learn how to set up and manage Azure Logic Apps using Terraform, including workflows, connectors, and integration patterns

Managing Azure Logic Apps with Terraform

Azure Logic Apps is a cloud service that helps you automate and orchestrate tasks, business processes, and workflows. This guide demonstrates how to set up and manage Logic Apps using Terraform.

Video Tutorial

Learn more about managing Azure Logic Apps 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 integration concepts

Project Structure

terraform-azure-logicapps/
├── main.tf
├── variables.tf
├── outputs.tf
├── modules/
│   └── logicapp/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
└── workflows/
    ├── order-processing.json
    └── data-sync.json

Logic App Configuration

Create modules/logicapp/main.tf:

# Standard Logic App
resource "azurerm_logic_app_standard" "main" {
  name                       = "${var.project_name}-logic"
  location                   = var.location
  resource_group_name        = var.resource_group_name
  app_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

  app_settings = {
    "FUNCTIONS_WORKER_RUNTIME"     = "node"
    "WEBSITE_NODE_DEFAULT_VERSION" = "~14"
    "WEBSITE_RUN_FROM_PACKAGE"     = "1"
    "APPINSIGHTS_INSTRUMENTATIONKEY" = azurerm_application_insights.main.instrumentation_key
  }

  identity {
    type = "SystemAssigned"
  }

  site_config {
    dotnet_framework_version = "v6.0"
    use_32_bit_worker       = false
    vnet_route_all_enabled = true

    cors {
      allowed_origins = ["https://portal.azure.com"]
    }
  }

  tags = var.tags
}

# Consumption Logic App
resource "azurerm_logic_app_workflow" "order_processing" {
  name                = "${var.project_name}-order-processing"
  location            = var.location
  resource_group_name = var.resource_group_name

  workflow_parameters = {
    "storageAccountName" = azurerm_storage_account.main.name
    "serviceBusConnection" = azurerm_servicebus_namespace.main.default_primary_connection_string
  }

  access_control {
    action {
      principal_id = var.function_app_principal_id
      actions      = ["invoke"]
    }
  }

  identity {
    type = "SystemAssigned"
  }

  parameters = {
    "$connections" = jsonencode({
      serviceBus = {
        connectionId   = azurerm_api_connection.servicebus.id
        connectionName = "servicebus"
        id            = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/providers/Microsoft.Web/locations/${var.location}/managedApis/servicebus"
      }
    })
  }

  tags = var.tags
}

# API Connection
resource "azurerm_api_connection" "servicebus" {
  name                = "${var.project_name}-servicebus-connection"
  resource_group_name = var.resource_group_name
  managed_api_id      = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/providers/Microsoft.Web/locations/${var.location}/managedApis/servicebus"
  display_name        = "Service Bus Connection"

  parameter_values = {
    connectionString = azurerm_servicebus_namespace.main.default_primary_connection_string
  }
}

# Integration Account
resource "azurerm_integration_account" "main" {
  name                = "${var.project_name}-integration"
  resource_group_name = var.resource_group_name
  location            = var.location
  sku_name            = "Standard"

  tags = var.tags
}

# Integration Service Environment
resource "azurerm_integration_service_environment" "main" {
  name                 = "${var.project_name}-ise"
  location             = var.location
  resource_group_name  = var.resource_group_name
  sku_name            = "Premium_0"
  access_endpoint_type = "Internal"

  subnet_id = azurerm_subnet.ise.id

  tags = var.tags
}

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           = "logic-subnet"
    address_prefix = "10.0.1.0/24"
    delegation {
      name = "logic-delegation"
      service_delegation {
        name    = "Microsoft.Web/serverFarms"
        actions = ["Microsoft.Network/virtualNetworks/subnets/action"]
      }
    }
  }

  subnet {
    name           = "ise-subnet"
    address_prefix = "10.0.2.0/24"
    delegation {
      name = "ise-delegation"
      service_delegation {
        name    = "Microsoft.Logic/integrationServiceEnvironments"
        actions = ["Microsoft.Network/virtualNetworks/subnets/action"]
      }
    }
  }

  tags = var.tags
}

resource "azurerm_private_endpoint" "logic" {
  name                = "${var.project_name}-pe"
  location            = var.location
  resource_group_name = var.resource_group_name
  subnet_id           = azurerm_subnet.endpoints.id

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

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

Security Configuration

  1. Role Assignments
resource "azurerm_role_assignment" "logic_contributor" {
  scope                = azurerm_logic_app_standard.main.id
  role_definition_name = "Logic App Contributor"
  principal_id         = var.contributor_principal_id
}

resource "azurerm_role_assignment" "logic_operator" {
  scope                = azurerm_logic_app_standard.main.id
  role_definition_name = "Logic App Operator"
  principal_id         = var.operator_principal_id
}
  1. Managed Identity Permissions
resource "azurerm_role_assignment" "storage_contributor" {
  scope                = azurerm_storage_account.main.id
  role_definition_name = "Storage Blob Data Contributor"
  principal_id         = azurerm_logic_app_standard.main.identity[0].principal_id
}

resource "azurerm_role_assignment" "servicebus_sender" {
  scope                = azurerm_servicebus_namespace.main.id
  role_definition_name = "Azure Service Bus Data Sender"
  principal_id         = azurerm_logic_app_workflow.order_processing.identity[0].principal_id
}

Monitoring Configuration

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

  log {
    category = "WorkflowRuntime"
    enabled  = true

    retention_policy {
      enabled = true
      days    = 30
    }
  }

  metric {
    category = "AllMetrics"
    enabled  = true

    retention_policy {
      enabled = true
      days    = 30
    }
  }
}

resource "azurerm_monitor_metric_alert" "logic" {
  name                = "${var.project_name}-runs-failed-alert"
  resource_group_name = var.resource_group_name
  scopes              = [azurerm_logic_app_standard.main.id]
  description         = "Alert when workflow runs fail"

  criteria {
    metric_namespace = "Microsoft.Logic/workflows"
    metric_name      = "RunsFailed"
    aggregation      = "Total"
    operator         = "GreaterThan"
    threshold        = 5
  }

  action {
    action_group_id = var.action_group_id
  }
}

Advanced Features

  1. Custom Connector
resource "azurerm_api_connection_custom" "main" {
  name                = "${var.project_name}-custom-connector"
  resource_group_name = var.resource_group_name
  display_name        = "Custom API Connection"
  target_url         = "https://api.example.com"
  
  authentication {
    type = "OAuth2"
    
    oauth2 {
      client_id     = var.client_id
      client_secret = var.client_secret
      token_url     = "https://login.example.com/oauth2/token"
    }
  }
}
  1. Workflow Versioning
resource "azurerm_logic_app_workflow" "versioned" {
  name                = "${var.project_name}-versioned"
  location            = var.location
  resource_group_name = var.resource_group_name

  workflow_schema    = "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#"
  workflow_version   = "1.0.0.0"

  parameters = {
    "$schema"        = "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#"
    "contentVersion" = "1.0.0.0"
  }
}

Best Practices

  1. Performance

    • Use Standard plan for predictable loads
    • Implement ISE for VNet integration
    • Configure connection pooling
    • Monitor execution times
  2. Security

    • Use managed identities
    • Implement VNet integration
    • Secure API connections
    • Control access with RBAC
  3. Reliability

    • Configure retry policies
    • Implement error handling
    • Monitor failures
    • Use correlation IDs
  4. Cost Optimization

    • Choose appropriate plan
    • Monitor consumption
    • Optimize workflows
    • Use batching

Conclusion

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

  • Workflow automation
  • System integration
  • Security and monitoring
  • Error handling

Remember to:

  • Monitor workflow runs
  • Review security settings
  • Handle errors properly
  • Update access controls