Deploying Azure Functions  Python with Terraform
Tags: azure, terraform, python, azurefunctions
Published: 2020-09-23 11:29:17

This post will focus on **deploying Azure Functions Python with Terraform**. If you are new to both services, you can start from **Prerequisites**.  

## Prerequisites

- [Create and deploy serverless Azure Functions in Python with Visual Studio Code](https://docs.microsoft.com/en-us/azure/developer/python/tutorial-vs-code-serverless-python-01)
- [Quickstart: Configure Terraform using Azure Cloud Shell](https://docs.microsoft.com/en-us/azure/developer/terraform/get-started-cloud-shell)

## Overview

[Azure Functions](https://azure.microsoft.com/en-us/services/functions/) is FaaS (function-as-a-service) on **Microsoft Azure**. It enables you to create your exciting services faster because it is a **serverless service**. You only need to focus on your code and don't need to care about OS updates and middleware.

In addition, Azure Functions is **event-driven** that is triggered by external events:

- Upload images on Azure Blob Storage (Object Storage service)
- Insert / Update items on Azure Cosmso DB (NoSQL DB service)
- HTTP incoming requests
- Timer defined by cron

However, deploying all services and configuring them from Azure Portal take a long time and happens mistakes.

To increase our productivity and deploy safely, I would like to use IaC (Infrastructure as Code) tools, such as [ARM Template](https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/overview) or [Terraform](https://www.terraform.io/), rather than deploying each resource from Azure Portal. Especially in production environment, managing your infrastructure as code is really important. 

## Set Up Environment

- Terraform >= 0.12
- azurerm provider >= 2.21
- azure cli

## Define Resources

Let's define resources. In Terraform, you define your resources in `.tf` files.

`main.tf`

```terraform
# https://github.com/terraform-providers/terraform-provider-azurerm/issues/7960
provider "azurerm" {
  version = ">=2.21.0"
  features {}
}

resource "azurerm_resource_group" "funcdeploy" {
  name     = "rg-${var.prefix}-function"
  location = var.location
}

resource "azurerm_storage_account" "funcdeploy" {
  name                     = "${var.prefix}storage"
  resource_group_name      = azurerm_resource_group.funcdeploy.name
  location                 = azurerm_resource_group.funcdeploy.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
}

resource "azurerm_storage_container" "funcdeploy" {
  name                  = "contents"
  storage_account_name  = azurerm_storage_account.funcdeploy.name
  container_access_type = "private"
}

resource "azurerm_application_insights" "funcdeploy" {
  name                = "${var.prefix}-appinsights"
  location            = azurerm_resource_group.funcdeploy.location
  resource_group_name = azurerm_resource_group.funcdeploy.name
  application_type    = "web"

  # https://github.com/terraform-providers/terraform-provider-azurerm/issues/1303
  tags = {
    "hidden-link:${azurerm_resource_group.funcdeploy.id}/providers/Microsoft.Web/sites/${var.prefix}func" = "Resource"
  }

}

resource "azurerm_app_service_plan" "funcdeploy" {
  name                = "${var.prefix}-functions-consumption-asp"
  location            = azurerm_resource_group.funcdeploy.location
  resource_group_name = azurerm_resource_group.funcdeploy.name
  kind                = "FunctionApp"
  reserved            = true

  sku {
    tier = "Dynamic"
    size = "Y1"
  }
}

resource "azurerm_function_app" "funcdeploy" {
  name                       = "${var.prefix}func"
  location                   = azurerm_resource_group.funcdeploy.location
  resource_group_name        = azurerm_resource_group.funcdeploy.name
  app_service_plan_id        = azurerm_app_service_plan.funcdeploy.id
  storage_account_name       = azurerm_storage_account.funcdeploy.name
  storage_account_access_key = azurerm_storage_account.funcdeploy.primary_access_key
  https_only                 = true
  version                    = "~3"
  os_type                    = "linux"
  app_settings = {
      "WEBSITE_RUN_FROM_PACKAGE" = "1"
      "FUNCTIONS_WORKER_RUNTIME" = "python"
      "APPINSIGHTS_INSTRUMENTATIONKEY" = "${azurerm_application_insights.funcdeploy.instrumentation_key}"
      "APPLICATIONINSIGHTS_CONNECTION_STRING" = "InstrumentationKey=${azurerm_application_insights.funcdeploy.instrumentation_key};IngestionEndpoint=https://japaneast-0.in.applicationinsights.azure.com/"
  }

  site_config {
        linux_fx_version= "Python|3.8"        
        ftps_state = "Disabled"
    }
    
  # Enable if you need Managed Identity
  # identity {
  #   type = "SystemAssigned"
  # }
}
```

`variables.tf`
```terraform
variable "location" {
    type    =   string
    default =   "japaneast"
}

variable "prefix" {
    type    =   string
    default =   "my"

}
```

Here are some tips for successful deployment.

- Use `azurerm >= 2.21.0`
- Add Hidden Link Tag 
- Set `version = ~3` (default is v1)

### Deploy Azure Resources

After you created above files, let's deploy !

If you don't want to install Terraform on your local PC, use [Azure Cloud Shell](https://docs.microsoft.com/en-us/azure/developer/terraform/get-started-cloud-shell) as test.

Make sure your each resource name is **unique**.

```bash
$ terraform init

...
Terraform has been successfully initialized!
...

$ terraform plan 
$ terraform apply

...
Apply complete! Resources: 6 added, 0 changed, 0 destroyed.
```

![terraform apply result](https://keyomuradev.azureedge.net/applyresult.png)

Fig.1 The result of `terraform apply`


Then you see Azure Functions on your Azure Portal.

![Azure Resources after deploying](https://keyomuradev.azureedge.net/deployresults.png)

Fig.2 Resources on Azure Portal

## Deploy Function

After `terrafrom apply`, you may upload your code to your [Function with Visual Studio Code](https://docs.microsoft.com/en-us/azure/developer/python/tutorial-vs-code-serverless-python-01).

Once deployed your code to Function App, you can see your functions on Azure Portal.

![Azure Function](https://keyomuradev.azureedge.net/function.png)

Fig.3 Functions on Azure Portal

You can manage various resources and control versions in `.tf` file. In production, you should use **CI/CD tools** such as [GitHub Actions](https://github.co.jp/features/actions) or [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/).

## Reference

- [Terraform と Azure Pipelines を使って App Service / Azure Functions をコード化して管理する](https://blog.shibayan.jp/entry/20191009/1570619560)
- [azurerm_function_app Example Usage (Linux) in docs produces error on apply](https://github.com/terraform-providers/terraform-provider-azurerm/issues/7960)
- [Feature Request: Attach azurerm_application_insights to a azurerm_app_service](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1303)
- [azurerm_function_app](https://www.terraform.io/docs/providers/azurerm/r/function_app.html)