2
votes

Attempting to deploy a Function App on a Premium plan that serves the functions from a container. The HOWTO for this works well enough: https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-function-linux-custom-image?tabs=nodejs#create-an-app-from-the-image

However, when I try to deploy it using Terraform, no sale. Everything looks right but the function does not show up in the side menu (it does for the one deployed with the az CLI), nor can I hit it with Postman.

Via Resource Explorer I can see that the Functions are not being populated. Here is the HCL that I am using

resource "azurerm_app_service_plan" "plan" {
  name                         = "${var.app_name}-Premium-ConsumptionPlan"
  location                     = "WestUS"
  resource_group_name          = "${data.azurerm_resource_group.rg.name}"
  kind                         = "Elastic"
  reserved                     = true

  sku {
    tier = "ElasticPremium"
    size = "EP1"
  }
}

data "azurerm_container_registry" "registry" {
  name = "${var.app_name}registry"
  resource_group_name = "${data.azurerm_resource_group.rg.name}"
}

resource "azurerm_function_app" "funcApp" {
    name                       = "${var.app_name}-userapi-${var.env_name}-funcapp"
    location                   = "WestUS"
    resource_group_name        = "${data.azurerm_resource_group.rg.name}"
    app_service_plan_id        = "${azurerm_app_service_plan.plan.id}"
    storage_connection_string  = "${azurerm_storage_account.storage.primary_connection_string}"
    version                    = "~2"

    app_settings = {
        FUNCTIONS_EXTENSION_VERSION               = "~2"
        FUNCTIONS_WORKER_RUNTIME                  = "dotnet"
        DOCKER_REGISTRY_SERVER_URL                = "${data.azurerm_container_registry.registry.login_server}"
        DOCKER_REGISTRY_SERVER_USERNAME           = "${data.azurerm_container_registry.registry.admin_username}"
        DOCKER_REGISTRY_SERVER_PASSWORD           = "${data.azurerm_container_registry.registry.admin_password}"
        WEBSITE_CONTENTAZUREFILECONNECTIONSTRING  = "${azurerm_storage_account.storage.primary_connection_string}"
        DOCKER_CUSTOM_IMAGE_NAME                  = "${data.azurerm_container_registry.registry.login_server}/pingtrigger:test"
        WEBSITE_CONTENTSHARE                      = "${azurerm_storage_account.storage.name}"
        FUNCTION_APP_EDIT_MODE                    = "readOnly"
    }

    site_config {
      always_on                 = true
      linux_fx_version          = "DOCKER|${data.azurerm_container_registry.registry.login_server}/pingtrigger:test"
    }
}

----- Updated based on answer ---- The solution was to instruct Function App to NOT use storage to discover metadata about available functions - this involves setting WEBSITES_ENABLE_APP_SERVICE_STORAGE to false. Here is my updated script

resource "azurerm_app_service_plan" "plan" {
  name                    = "${var.app_name}-premiumPlan"
  resource_group_name     = "${data.azurerm_resource_group.rg.name}"
  location                = "${data.azurerm_resource_group.rg.location}"
  kind                    = "Linux"
  reserved                = true

  sku {
    tier = "Premium"
    size = "P1V2"
  }
}

data "azurerm_container_registry" "registry" {
  name                  = "${var.app_name}registry"
  resource_group_name   = "${data.azurerm_resource_group.rg.name}"
}

resource "azurerm_function_app" "funcApp" {
    name                       = "userapi-${var.app_name}fa-${var.env_name}"
    location                   = "${data.azurerm_resource_group.rg.location}"
    resource_group_name        = "${data.azurerm_resource_group.rg.name}"
    app_service_plan_id        = "${azurerm_app_service_plan.plan.id}"
    storage_connection_string  = "${azurerm_storage_account.storage.primary_connection_string}"
    version                    = "~2"

    app_settings = {
        FUNCTION_APP_EDIT_MODE                    = "readOnly"
        https_only                                = true
        DOCKER_REGISTRY_SERVER_URL                = "${data.azurerm_container_registry.registry.login_server}"
        DOCKER_REGISTRY_SERVER_USERNAME           = "${data.azurerm_container_registry.registry.admin_username}"
        DOCKER_REGISTRY_SERVER_PASSWORD           = "${data.azurerm_container_registry.registry.admin_password}"
        WEBSITES_ENABLE_APP_SERVICE_STORAGE       = false
    }

    site_config {
      always_on         = true
      linux_fx_version  = "DOCKER|${data.azurerm_container_registry.registry.login_server}/testimage:v1.0.1"
    }
}
1
Are you able to hit the access the homepage (<app-name>.azurewebsites.net)? Additionally, in the Resource Explorer, do you see linux_fx_version set to "DOCKER|<your-image>"? - Ankit Kumar
I do - the basic problem is the functions are not being surfaced from the container, at a high level that means the function list to the left is empty and any request results in a 404. My working theory is there is some switch I need to throw to tell the functionapp its using Docker (there is a slight difference between my TF version and the AZ CLI one on the Function App Settings) - I have thus far been unable to find it - xximjasonxx
If the linuxFxVersion is set properly and you are able to access the homepage, the Azure Functions is able to properly run your Docker image. You image has to have Functions located at /home/site/wwwroot. Note, if you are using dotnet, /home/site/wwwroot should have the binaries after building / publishing your project. You can also try running your image locally by spinning up a container and checking if you are able to call the functions. You'd want to set environment variables such as FUNCTIONS_EXTENSION_VERSION and FUNCTIONS_WORKER_RUNTIME when starting your container. - Ankit Kumar
With regard to this point, I created a Docker image and stored it in ACR. When I used Terraform to create the function app, I never experienced the functions being available. However, when, with the same Docker image, I created the Function app using the Azure CLI command it worked. I will double check this evening, but I do not believe image construction is the root cause here - xximjasonxx

1 Answers

2
votes

To create the Azure Function with your custom Docker image, I think your problem is that you set the environment variable FUNCTIONS_WORKER_RUNTIME, it means you use the built-in runtime, but you want to use your custom image. With my test, you only need to configure the function app like this:

resource "azurerm_function_app" "funcApp" {
    name                       = "${var.app_name}-userapi-${var.env_name}-funcapp"
    location                   = "${azurerm_resource_group.main.location}"
    resource_group_name        = "${azurerm_resource_group.main.name}"
    app_service_plan_id        = "${azurerm_app_service_plan.plan.id}"
    storage_connection_string  = "${azurerm_storage_account.storage.primary_connection_string}"
    version                    = "~2"

    app_settings = {
        FUNCTIONS_EXTENSION_VERSION               = "~2"
        DOCKER_REGISTRY_SERVER_URL                = "${data.azurerm_container_registry.registry.login_server}"
        DOCKER_REGISTRY_SERVER_USERNAME           = "${data.azurerm_container_registry.registry.admin_username}"
        DOCKER_REGISTRY_SERVER_PASSWORD           = "${data.azurerm_container_registry.registry.admin_password}"
        WEBSITE_CONTENTAZUREFILECONNECTIONSTRING  = "${azurerm_storage_account.storage.primary_connection_string}"
        WEBSITE_CONTENTSHARE                      = "${azurerm_storage_account.storage.name}"
        DOCKER_CUSTOM_IMAGE_NAME                  = "${data.azurerm_container_registry.registry.login_server}/pingtrigger:test"
    }

    site_config {
      always_on                 = true
      linux_fx_version          = "DOCKER|${data.azurerm_container_registry.registry.login_server}/pingtrigger:test"
    }
}

Then you only need to wait a while for the creation.