Terraform is an excellent infrastructure as code (IaC) tool for managing Azure resources. However, a configuration drift issue occurs with Function App application settings and Terraform when deploying function app code using Azure pipelines.
In this tutorial, you will deploy a Function App using Terraform and then deploy function code using an Azure pipeline. You will then encounter a problem with the Azure Function application settings with a Terraform update and learn how to avoid it.
For this tutorial, you will need:
- Terraform open-source executable. This tutorial uses version 1.4.6.
- Access to an Azure subscription with appropriate permissions, such as Owner or Contributor.
- An Azure DevOps account.
Deploying an Azure Function App using Terraform
Below is example code on deploying a Function App using Terraform. The Terraform configuration uses the random_pet
and random_integer
resources to create a unique application name. The configuration continues with deploying the following resources:
- Resource group
- App service plan
- Storage account
- Windows function app running PowerShell 7
Note: The configuration below uses az login
for authentication and stores the state on the local system. You can read more about Azure authentication inside Terraform and storing a remote state in a storage account in the articles below:
Performing Azure Authentication Inside Terraform | Jeff Brown Tech
Move Terraform State to Azure Storage Account | Jeff Brown Tech
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
}
}
}
provider "azurerm" {
features {}
}
resource "random_pet" "name" {
separator = ""
}
resource "random_integer" "int" {
min = 10000
max = 99999
}
locals {
app_name = "${random_pet.name.id}${random_integer.int.result}"
location = "westus3"
}
resource "azurerm_resource_group" "rg" {
name = "${local.app_name}-rg"
location = local.location
}
resource "azurerm_service_plan" "asp" {
name = "${local.app_name}-asp"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
os_type = "Windows"
sku_name = "Y1"
}
resource "azurerm_storage_account" "sa" {
name = "${local.app_name}sa"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_windows_function_app" "funcapp" {
name = "${local.app_name}-funcapp"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
service_plan_id = azurerm_service_plan.asp.id
storage_account_name = azurerm_storage_account.sa.name
storage_account_access_key = azurerm_storage_account.sa.primary_access_key
site_config {
application_stack {
powershell_core_version = "7"
}
}
}
The screenshot below shows the resource group with the deployed resources.

Deploying Function App code using Azure DevOps Pipelines
Next, the solution deploys a function in the Function App using an Azure DevOps pipeline. In this example, the function code is already packaged in a .zip file named pwshfuncapp.zip in the repository root. The pipeline triggers on pushes to the main branch.
If you want to try this pipeline deployment yourself, change the values of:
azureSubscription
to match your service connection namefunctionAppName
to match the application name (random_pet
+random_integer
)environment
to match the target environment (more on environments here)
trigger:
- master
variables:
# Azure Resource Manager service connection name
azureSubscription: 'Demo'
# Base application name (random pet + random integer)
functionAppName: 'livingewe97058-funcapp'
# Environment name
environment: 'Demo-Env'
# Agent VM image name
vmImageName: 'windows-2019'
# Working Directory
workingDirectory: '$(System.DefaultWorkingDirectory)'
stages:
- stage: Build
displayName: Build stage
jobs:
- job: Build
displayName: Build
pool:
vmImage: $(vmImageName)
steps:
- publish: $(System.DefaultWorkingDirectory)/pwshfuncapp.zip
artifact: drop
- stage: Deploy
displayName: Deploy stage
dependsOn: Build
condition: succeeded()
jobs:
- deployment: Deploy
displayName: Deploy
environment: $(environment)
pool:
vmImage: $(vmImageName)
strategy:
runOnce:
deploy:
steps:
- task: AzureFunctionApp@1
displayName: 'Azure functions app deploy'
inputs:
azureSubscription: '$(azureSubscription)'
appType: functionApp
appName: $(functionAppName)
package: '$(Pipeline.Workspace)/drop/pwshfuncapp.zip'
Once the pipeline completes, the Function App contains a new function named HttpTrigger1.

When you deploy a function app from a pipeline, two new application settings are added to the Function App:
WEBSITE_ENABLE_SYNC_UPDATE_SITE
WEBSITE_RUN_FROM_PACKAGE
Terraform Application Settings Drift Issue
Everything is looking good. Your infrastructure is defined in code, and your function code is deployed via a pipeline. What could go wrong?
At this point, if you run terraform plan
, you should see that Terraform wants to delete the two new application settings created by the pipeline deployment.

Terraform is typically good at leaving settings alone that it is not managing. However, in this case, Terraform sees these application settings and has decided to remove them because those settings do not exist in its configuration.
You can resolve this by using the lifecycle meta-argument. The lifecycle appears with any resource block regardless of what type the resource block is. One of the arguments within the lifecycle block is ignore_changes, which tells Terraform to ignore changes to specific resource properties.
The solution is to tell Terraform to ignore those application settings. Below is the updated function app resource definition with the added lifecycle
block telling Terraform to ignore changes to those application settings.
resource "azurerm_windows_function_app" "funcapp" {
name = "${local.app_name}-funcapp"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
service_plan_id = azurerm_service_plan.asp.id
storage_account_name = azurerm_storage_account.sa.name
storage_account_access_key = azurerm_storage_account.sa.primary_access_key
site_config {
application_stack {
powershell_core_version = "7"
}
}
lifecycle {
ignore_changes = [
app_settings["WEBSITE_ENABLE_SYNC_UPDATE_SITE"],
app_settings["WEBSITE_RUN_FROM_PACKAGE"]
]
}
}
After making these changes, Terraform now ignores those settings. Running a new terraform plan
should show no changes needed in your deployment.
Related: Review the full code solution at GitHub | Jeff Brown Tech | Terraform_FunctionApp_Example.
Summary
Terraform is a fantastic infrastructure as code solution for managing cloud resources. However, in the case of an Azure Function App, you need to make changes in your Terraform configuration to ensure it does not delete application settings important to your function code deployment process.