Back to Blog

Deploying Azure Functions with Terraform

Complete tutorial about azurerm_function_app in Terraform. Learn function apps, consumption plan, app settings.

Deploying Azure Functions with Terraform

Deploying Azure Functions with Terraform

Introduction

Azure Functions is a serverless computing service that allows you to run event-driven code without the need to manage infrastructure. This "Function as a Service" (FaaS) approach enables developers to build applications that can scale automatically based on demand, making it an optimal choice for various workloads, such as HTTP APIs, event processing, and more. With Infrastructure as Code (IaC) using tools like Terraform, you can automate the deployment and management of Azure Functions and associated resources, ensuring consistency and repeatability across environments.

IaC simplifies infrastructure management by using configuration files to define your environment's infrastructure, allowing developers to version control and collaborate on infrastructure changes just as they do with application code. The main use cases for deploying Azure Functions with Terraform include building microservices, automating workflows, and responding to events in other Azure services.


Prerequisites

Before you start, ensure that you have the following:

  • Terraform CLI installed on your machine.
  • An active Azure subscription to create and manage Azure resources.
  • Azure CLI installed for authentication and management.
  • A service principal with sufficient permissions to deploy resources in your Azure subscription.

Fundamental Concepts

Key Terminology

  • Function App: A container for your individual functions and their execution environment.
  • Consumption Plan: A serverless hosting plan that automatically allocates compute resources based on demand.
  • App Settings: Configuration settings for your Function App, similar to environment variables.

Resource Dependencies

Azure Functions often depend on other Azure resources, such as Storage Accounts for hosting code and managing state. Understanding these dependencies is crucial when defining your Terraform configurations.

State Management

Terraform maintains a state file that tracks your infrastructure's current state. This file is essential for determining what changes need to be applied during updates. It is critical to manage this state securely, especially in team environments.


Resource Syntax

To deploy Azure Functions with Terraform, you'll primarily use the azurerm_function_app resource. Below is the HCL syntax along with a brief explanation of its arguments:

resource "azurerm_function_app" "example" {
  name                      = var.function_app_name
  location                  = azurerm_resource_group.example.location
  resource_group_name       = azurerm_resource_group.example.name
  app_service_plan_id       = azurerm_app_service_plan.example.id
  storage_account_name      = azurerm_storage_account.example.name
  storage_account_access_key = azurerm_storage_account.example.primary_access_key
  version                   = "~3"
  os_type                   = "Linux"
  https_only                = true

  app_settings = {
    "FUNCTIONS_WORKER_RUNTIME" = "dotnet"
    "AzureWebJobsStorage"      = azurerm_storage_account.example.primary_connection_string
  }
}

Arguments Table

Argument Description
name The name of the Function App.
location The Azure region where the function app is created.
resource_group_name The name of the resource group to deploy the function app in.
app_service_plan_id The ID of the App Service Plan to host the Function App.
storage_account_name The name of the Storage Account to use for the Function App.
storage_account_access_key Access key for the Storage Account.
version The version of the Functions runtime to use.
os_type The operating system for the Function App (Windows or Linux).
https_only Whether to enforce HTTPS requests only.
app_settings Key-value pairs for application settings.

Practical Examples

Example 1: Basic Function App Setup

resource "azurerm_resource_group" "example" {
  name     = "example-resources"
  location = "East US"
}

resource "azurerm_storage_account" "example" {
  name                     = "examplestorageacct"
  resource_group_name      = azurerm_resource_group.example.name
  location                 = azurerm_resource_group.example.location
  account_tier             = "Standard"
  account_replication_type  = "LRS"
}

resource "azurerm_app_service_plan" "example" {
  name                = "example-plan"
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name
  sku {
    tier     = "Dynamic"
    size     = "Y1"
  }
}

resource "azurerm_function_app" "example" {
  name                      = "examplefunctionapp"
  location                  = azurerm_resource_group.example.location
  resource_group_name       = azurerm_resource_group.example.name
  app_service_plan_id       = azurerm_app_service_plan.example.id
  storage_account_name      = azurerm_storage_account.example.name
  storage_account_access_key = azurerm_storage_account.example.primary_access_key
  version                   = "~3"
  os_type                   = "Linux"
  https_only                = true
}

Example 2: Adding App Settings

resource "azurerm_function_app" "example" {
  # ... other arguments ...
  
  app_settings = {
    "FUNCTIONS_WORKER_RUNTIME" = "node"
    "AzureWebJobsStorage"      = azurerm_storage_account.example.primary_connection_string
    "MyCustomSetting"          = "MyValue"
  }
}

Example 3: Deploying with a Consumption Plan

resource "azurerm_app_service_plan" "example" {
  name                = "example-consumption-plan"
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name
  sku {
    tier = "Dynamic"
    size = "Y1"
  }
}

Example 4: Custom Domains and HTTPS

resource "azurerm_function_app" "example" {
  # ... other arguments ...

  custom_domain_name = "www.mycustomdomain.com"
  host_name_binding {
    host_name     = "www.mycustomdomain.com"
    domain_id    = azurerm_app_service_domain.example.id
  }
}

Example 5: Using Managed Identity

resource "azurerm_function_app" "example" {
  # ... other arguments ...

  identity {
    type = "SystemAssigned"
  }
}

Example 6: Configuring Authentication

resource "azurerm_function_app" "example" {
  # ... other arguments ...

  auth_settings {
    enabled = true
    issuer   = "https://my-issuer.com"
  }
}

Example 7: Environment Variables

resource "azurerm_function_app" "example" {
  # ... other arguments ...

  app_settings = {
    "DATABASE_URL"      = "mysql://user:pass@hostname:port/dbname"
    "ANOTHER_SETTING"   = "value"
  }
}

Example 8: Output Configuration

output "function_app_url" {
  value = azurerm_function_app.example.default_hostname
}

Real-World Use Cases

Scenario 1: API Endpoint

You may deploy an Azure Function that serves as an API endpoint, processing HTTP requests and returning JSON responses based on various triggers, such as a message queue or an HTTP request.

Scenario 2: Event-Driven Processing

Deploy an Azure Function to process events from Azure Event Hubs or Azure Blob Storage uploads, allowing real-time processing of data as it arrives.

Scenario 3: Scheduled Tasks

Use Azure Functions to run scheduled tasks or CRON jobs that perform operations like data backups or report generation at defined intervals.


Best Practices

  1. Version Control: Always version control your Terraform configurations to keep track of changes and facilitate collaboration.
  2. Use Modules: Encapsulate your code into reusable Terraform modules for better organization and maintainability.
  3. Keep Secrets Secure: Use Azure Key Vault to manage sensitive information and avoid hardcoding secrets in your Terraform files.
  4. State Management: Use remote state storage (e.g., Azure Blob Storage) to store your Terraform state file securely.
  5. Naming Conventions: Adopt consistent naming conventions for resources to make your configurations understandable and manageable.

Common Errors

  1. Error: Error: creating Function App: storageAccountAccessKey cannot be empty

    • Cause: The storage account access key is not provided.
    • Solution: Ensure you are referencing the correct storage account and that it exists.
  2. Error: Error: Invalid Resource Group Name

    • Cause: The specified resource group doesn't exist.
    • Solution: Verify the resource group name and ensure it has been created prior to referencing it.
  3. Error: Error: Function App name is already in use

    • Cause: The function app name must be globally unique.
    • Solution: Choose a different name that is not already taken.
  4. Error: Error: The resource group 'example-resources' could not be found

    • Cause: The resource group is referenced but not created.
    • Solution: Create the resource group before deploying other resources.

Related Resources

Resource Type Resource Name Description
Terraform Resource azurerm_storage_account Manages Azure Storage Accounts.
Terraform Resource azurerm_app_service_plan Manages Azure App Service Plans.
Terraform Resource azurerm_resource_group Manages Azure Resource Groups.
Azure Documentation Azure Functions Overview Comprehensive guide on Azure Functions.

Complete Infrastructure Script

Here is a complete Terraform configuration file to deploy an Azure Function App with a Consumption Plan:

provider "azurerm" {
  features {}
}

variable "function_app_name" {
  description = "Name of the Function App"
  type        = string
}

resource "azurerm_resource_group" "example" {
  name     = "example-resources"
  location = "East US"
}

resource "azurerm_storage_account" "example" {
  name                     = "examplestorageacct"
  resource_group_name      = azurerm_resource_group.example.name
  location                 = azurerm_resource_group.example.location
  account_tier             = "Standard"
  account_replication_type  = "LRS"
}

resource "azurerm_app_service_plan" "example" {
  name                = "example-plan"
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name
  sku {
    tier     = "Dynamic"
    size     = "Y1"
  }
}

resource "azurerm_function_app" "example" {
  name                      = var.function_app_name
  location                  = azurerm_resource_group.example.location
  resource_group_name       = azurerm_resource_group.example.name
  app_service_plan_id       = azurerm_app_service_plan.example.id
  storage_account_name      = azurerm_storage_account.example.name
  storage_account_access_key = azurerm_storage_account.example.primary_access_key
  version                   = "~3"
  os_type                   = "Linux"
  https_only                = true

  app_settings = {
    "FUNCTIONS_WORKER_RUNTIME" = "dotnet"
    "AzureWebJobsStorage"      = azurerm_storage_account.example.primary_connection_string
  }
}

output "function_app_url" {
  value = azurerm_function_app.example.default_hostname
}

Conclusion

In this tutorial, you learned how to deploy Azure Functions using Terraform, covering essential concepts, practical examples, and best practices. By leveraging IaC, you can manage your serverless applications more effectively and ensure consistency across your deployments.

Next Steps

  • Explore more advanced Terraform features such as remote state management and CI/CD integration.
  • Experiment with different triggers and bindings for Azure Functions to enhance your applications.

References