eoinmullan
eoinmullan

Reputation: 1199

Azure Functions ARM Template deploy deletes Functions

I've got an ARM template (included below) to deploy an Azure Function App. I deploy it with:

az group deployment create --resource-group my-group --template-file my-function-app.json

This works and I can then deploy my functions successfully using the VS Code plugin or Azure Functions Core Tools.

However, if I then re-deploy the ARM template (for example to update an application setting) then I lose my functions and need to re-deploy them again. Is this expected behaviour? It's not what I observe when deploying e.g. a Web App via an ARM template. Is there something specific I can do when deploying an ARM template for a Function App to preserve my deployed functions?

my-function-app.json:

{
    "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        ...
    },
    "variables": {
        ...
    },
    "resources": [
        {
            "apiVersion": "2015-08-01",
            "type": "Microsoft.Web/sites",
            "name": "[variables('collectorFunctionAppName')]",
            "location": "[parameters('location')]",
            "kind": "functionapp",
            "properties": {
                "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
                "siteConfig": {
                    "appSettings": [
                        {
                            ...
                        }
                    ]
                }
            }
        }
    ],
    "outputs": {}
}

Upvotes: 17

Views: 3361

Answers (4)

TechFree
TechFree

Reputation: 2974

Adding more to the right answer linking "WEBSITE_RUN_FROM_PACKAGE" setting with this issue, that helped me to fix my issue, in case somebody is looking further to plug in the gaps:

  1. I run bicep as IaC that creates function app, storage, other resources etc along with APIM, API. No function code deployment yet. Had used WEBSITE_RUN_FROM_PACKAGE='1' in the function app bicep script.

  2. In the next step, I bundle the code and use function tools func azure functionapp publish $FUNCTION_APP_NAME --worker-runtime=python --build-native-deps to do a remote deployment. Good so far; can see the function in the function app deployed. This command also updates the WEBSITE_RUN_FROM_PACKAGE setting to the actual package URL from storage such as 'https://contentparserstgfnsa.blob.core.windows.net/function-releases/20250...'.

  3. Now, add the function operations on APIM API and can now have the function running via APIM. All good

Now run the step 1 again, and you would loose the function ! The reason being you are overwriting the right WEBSTITE_RUN_FROM_PACKAGE value with '1' again, but this needs to be now using not '1' but the actual & latest package URL for subsequent function app deployments

One solution is to try to lookup for the existing function app and its this environment setting in the bicep scripts and use it in the environment setting, something like this:

// Check if the Function App exists

var functionAppExists = contains(listAccountResources(resourceId('Microsoft.Resources/subscriptions/resourceGroups', subscription().subscriptionId, resourceGroup().name), 'Microsoft.Web/sites').value, { name: functionAppName })

// Get existing WEBSITE_RUN_FROM_PACKAGE or use default of 1

var websiteRunFromPackage = functionAppExists ? list('${resourceId('Microsoft.Web/sites', functionAppName)}/config/appsettings', '2024-04-01').properties.WEBSITE_RUN_FROM_PACKAGE ?? '1' : '1'

Probably being new to bicep/Azure I ran into API versions issue, and for now had a workaround in my bash scripts, to lookup for this value using Azure CLI and feed it into bicep deployment CLI call.

AI generated code

APPLICATION="myapp"
FUNCTION_APP_NAME="myappfuncapp"
FUNCTION_APP_WEBSITE_RUN_FROM_PKG_DEFAULT="1"

# Check if the Function App exists
if az webapp show --name "$FUNCTION_APP_NAME" --resource-group "$RESOURCE_GROUP_NAME" > /dev/null 2>&1; then
  # Function App exists, try to get the app setting
  APP_SETTING=$(az webapp config appsettings list --name "$FUNCTION_APP_NAME" --resource-group "$RESOURCE_GROUP_NAME" --query "[?name=='WEBSITE_RUN_FROM_PACKAGE'].value | [0]" -o tsv)

  # Check if the app setting exists
  if [[ -z "$APP_SETTING" ]]; then
    # App setting is empty or does not exist, use default value
    APP_SETTING="$FUNCTION_APP_WEBSITE_RUN_FROM_PKG_DEFAULT"
    echo "Function App exists, but WEBSITE_RUN_FROM_PACKAGE setting is missing or empty. Using default value: $FUNCTION_APP_WEBSITE_RUN_FROM_PKG_DEFAULT"
  else
    echo "Function App exists, WEBSITE_RUN_FROM_PACKAGE setting found: $APP_SETTING"
  fi
else
  # Function App does not exist, use default value
  APP_SETTING="$FUNCTION_APP_WEBSITE_RUN_FROM_PKG_DEFAULT"
  echo "Function App does not exist. Using default value: $FUNCTION_APP_WEBSITE_RUN_FROM_PKG_DEFAULT"
fi

# Now, APP_SETTING contains the desired value (either from the Function App or the default)
echo "Using WEBSITE_RUN_FROM_PACKAGE value: $APP_SETTING"

az deployment group create \
  --resource-group $RESOURCE_GROUP_NAME \
  --template-file main.bicep \
  --query "properties.outputs" \
  --output json \
  --parameters params.bicepparam \
   key1=$VALUE1 \
   functionAppPackageURL=$APP_SETTING

Accept and use this parameter functionAppPackageURL value now in setting the WEBSITE_RUN_FROM_PACKAGE in the bicep script.

Upvotes: 0

curious coder
curious coder

Reputation: 841

Are you deploying your function as a package? If so, make sure you set this setting in your template, since it will be removed when you redeploy otherwise:

{ "name": "WEBSITE_RUN_FROM_PACKAGE", "value": "1" }

Upvotes: 26

Hannel
Hannel

Reputation: 1716

Yes that should be the expected behavior.

ARM Template is a declarative deployment meaning anytime you deploy it will overwrite anything you have with new template information. The template should always include everything you need.

Upvotes: -1

mkstr
mkstr

Reputation: 153

You could try "--mode incremental" parameter although that should be the default when it is not provided.

Upvotes: 0

Related Questions