How to Enable/Disable Azure Function programmatically
Asked Answered
L

7

7

Is there a way to programmatically enable/disable an Azure function?

I can enable/disable a function using the portal under the "Manage" section, which causes a request to be sent to https://<myfunctionapp>.scm.azurewebsites.net/api/functions/<myfunction>

The JSON payload looks a bit like:

{
   "name":"SystemEventFunction",
   "config":{
      "disabled":true,
      "bindings":[ 
         // the bindings for this function
       ]
   }
  // lots of other properties (mostly URIs)
}

I'm creating a management tool outside of the portal that will allow users to enable and disable functions.

Hoping I can avoid creating the JSON payload by hand, so I'm wondering if there is something in an SDK (WebJobs??) that has this functionality.

Lorenzoloresz answered 22/2, 2017 at 19:51 Comment(2)
powershell would be the easiest wayCottrill
I'm able to use Stop-AzureRmWebApp to stop an entire Azure function app. I only need to pause it for a second, so no need to toggle individual functions.Mobster
N
6

No, this is not possible currently. The disabled metadata property in function.json is what determines whether a function is enabled. The portal just updates that value when you enable/disable in the portal.

Not sure if it will meet your needs, but I'll point out that there is also a host.json functions array that can be used to control the set of functions that will be loaded (documented here). So for example, if you only wanted 2 of your 10 functions enabled, you could set this property to an array containing only those 2 function names (e.g. "functions": [ "QueueProcessor", "GitHubWebHook" ]), and only those will be loaded/enabled. However, this is slightly different than enable/disable in that you won't be able to invoke the excluded functions via the portal, whereas you can portal invoke disabled functions.

Namaqualand answered 23/2, 2017 at 2:1 Comment(1)
Per official MS documentation, the best way to disable a function is using function.json, not host.json configLandlady
S
8

Further to @James Z.'s answer, I've created the following class in C# that allows you to programmatically disable / enable an Azure function.

The functionsSiteRoot constructor argument is the Kudu root of your Functions application, eg https://your-functions-web-app.scm.azurewebsites.net/api/vfs/site/wwwroot/

The username and password can be obtained from "Get publish profile" in the App Service settings for your Functions.

public class FunctionsHelper : IFunctionsHelper
{
    private readonly string _username;
    private readonly string _password;
    private readonly string _functionsSiteRoot;
    private WebClient _webClient;

    public FunctionsHelper(string username, string password, string functionsSiteRoot)
    {
        _username = username;
        _password = password;
        _functionsSiteRoot = functionsSiteRoot;

        _webClient = new WebClient
        {
            Headers = { ["ContentType"] = "application/json" },
            Credentials = new NetworkCredential(username, password),
            BaseAddress = functionsSiteRoot
        };
    }

    public void StopFunction(string functionName)
    {
        SetFunctionState(functionName, isDisabled: true);
    }

    public void StartFunction(string functionName)
    {
        SetFunctionState(functionName, isDisabled: false);
    }

    private void SetFunctionState(string functionName, bool isDisabled)
    {
        var functionJson =
            JsonConvert.DeserializeObject<FunctionSettings>(_webClient.DownloadString(GetFunctionJsonUrl(functionName)));
        functionJson.disabled = isDisabled;
        _webClient.Headers["If-Match"] = "*";
        _webClient.UploadString(GetFunctionJsonUrl(functionName), "PUT", JsonConvert.SerializeObject(functionJson));
    }

    private static string GetFunctionJsonUrl(string functionName)
    {
        return $"{functionName}/function.json";
    }
}

internal class FunctionSettings
{
    public bool disabled { get; set; }
    public List<Binding> bindings { get; set; }
}

internal class Binding
{
    public string name { get; set; }
    public string type { get; set; }
    public string direction { get; set; }
    public string queueName { get; set; }
    public string connection { get; set; }
    public string accessRights { get; set; }
}
Shortly answered 3/3, 2017 at 13:38 Comment(2)
This really helped although I had to make a few changes, 1st had to Function url to : return $"/home/site/wwwroot/{functionName}/function.json"; 2nd the json convert and serialization didn't work, I just had to do a plain search and replace: funcFile = funcFile.Replace("\"disabled\": true", "\"disabled\": false");Fax
The disabled property needs to be after the bindings in the serialized json. Otherwise function fails to load.Publisher
N
6

No, this is not possible currently. The disabled metadata property in function.json is what determines whether a function is enabled. The portal just updates that value when you enable/disable in the portal.

Not sure if it will meet your needs, but I'll point out that there is also a host.json functions array that can be used to control the set of functions that will be loaded (documented here). So for example, if you only wanted 2 of your 10 functions enabled, you could set this property to an array containing only those 2 function names (e.g. "functions": [ "QueueProcessor", "GitHubWebHook" ]), and only those will be loaded/enabled. However, this is slightly different than enable/disable in that you won't be able to invoke the excluded functions via the portal, whereas you can portal invoke disabled functions.

Namaqualand answered 23/2, 2017 at 2:1 Comment(1)
Per official MS documentation, the best way to disable a function is using function.json, not host.json configLandlady
E
5

Further to @DavidGouge 's answer above, the code he posted does work, I just tested it and will be using it in my app. However it needs a couple of tweaks:

  1. Remove the inheritance from IFunctionsHelper. I'm not sure what that interface is but it wasn't required.

  2. Change the class definition for Binding as follows:

        internal class Binding
        {
            public string name { get; set; }
            public string type { get; set; }
            public string direction { get; set; }
            public string queueName { get; set; }
            public string connection { get; set; }
            public string accessRights { get; set; }
            public string schedule { get; set; }
        }
    

    After that it would work.

P.S. I would have put this as a comment on the original answer, but I don't have enough reputation on Stack Overflow to post comments!

Exorcise answered 7/5, 2017 at 21:32 Comment(0)
C
3

Using a combination of @Satya V's and @DavidGouge's solutions, I came up with this:

public class FunctionsHelper
{
    private readonly ClientSecretCredential _tokenCredential;
    private readonly HttpClient _httpClient;

    public FunctionsHelper(string tenantId, string clientId, string clientSecret, string subscriptionId, string resourceGroup, string functionAppName)
    {
        var baseUrl =
            $"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.Web/sites/{functionAppName}/";
        var httpClient = new HttpClient
        {
            BaseAddress = new Uri(baseUrl)
        };

        _httpClient = httpClient;
        
        _tokenCredential = new ClientSecretCredential(tenantId, clientId, clientSecret);
    }

    private async Task SetAuthHeader()
    {
        var accessToken = await GetAccessToken();
        _httpClient.DefaultRequestHeaders.Authorization = AuthenticationHeaderValue.Parse($"Bearer {accessToken}");
    }

    private async Task<string> GetAccessToken()
    {
        return (await _tokenCredential.GetTokenAsync(
            new TokenRequestContext(new[] {"https://management.azure.com/.default"}))).Token;
    }
    
    public async Task StopFunction(string functionName)
    {
        await SetFunctionState(functionName, isDisabled: true);
    }

    public async Task StartFunction(string functionName)
    {
        await SetFunctionState(functionName, isDisabled: false);
    }

    private async Task SetFunctionState(string functionName, bool isDisabled)
    {
        await SetAuthHeader();
        var appSettings = await GetAppSettings();
        appSettings.properties[$"AzureWebJobs.{functionName}.Disabled"] = isDisabled ? "1" : "0";

        var payloadJson = JsonConvert.SerializeObject(new
        {
            kind = "<class 'str'>", appSettings.properties
        });

        var stringContent = new StringContent(payloadJson, Encoding.UTF8, "application/json");
        
        await _httpClient.PutAsync("config/appsettings?api-version=2019-08-01", stringContent);
    }
    
    private async Task<AppSettings> GetAppSettings()
    {
        var res  = await _httpClient.PostAsync("config/appsettings/list?api-version=2019-08-01", null);
        
        var content = await res.Content.ReadAsStringAsync();
        
        return JsonConvert.DeserializeObject<AppSettings>(content);
    }
}

internal class AppSettings
{ 
    public Dictionary<string, string> properties { get; set; }
}

The problem with using the Kudu api to update the function.json file is that it will be overwritten on any subsequent deploy. This uses Azure's Rest Api to update the Configuration of the application. You will first need an Azure Service Principle to use the api though.

Using the Azure Cli, you can run az ad sp create-for-rbac to generate the Service Principle and get the client id and client secret. Because the UpdateConfiguration endpoint does not allow you to update a single value, and overwrites the entire Configuration object with the new values, you must first get all the current Configuration values, update the one you want, and then call the Update endpoint with the new Configuration keys and values.

Churchlike answered 31/5, 2021 at 22:26 Comment(0)
H
2

I would imagine you can use Kudu REST API (specifically VFS) to update the disabled metadata property in function.json. Would that disable the function? Here is the Kudu REST API. https://github.com/projectkudu/kudu/wiki/REST-API

Hopeless answered 2/3, 2017 at 18:37 Comment(0)
B
1

The CLI command That is used to disable the Azure function through CLI - documented here

 az functionapp config appsettings set --name <myFunctionApp> \
    --resource-group <myResourceGroup> \
    --settings AzureWebJobs.QueueTrigger.Disabled=true

I had captured fiddler while while running the above command. Azure CLI works on the Python process The python process was issuing request to

https://management.azure.com to update appsetting.

enter image description here

got a reference to the same endpoint in the below REST Endpoint : https://learn.microsoft.com/en-us/rest/api/appservice/webapps/updateapplicationsettings

Request URI :

PUT https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Web/sites/{name}/config/appsettings?api-version=2019-08-01

Headers :

Authorization: Bearer <> ; Content-Type: application/json; charset=utf-8

Request Body:

{"kind": "<class 'str'>", "properties":JSON}

We can hardcode the properties or get it dynamically. For disabling the function, will have to update the JSON node of Properties : Azure.WebJobs.QueueTrigger.Disabled = True

To get properties you could use the endpoint, you could refer Web Apps - List Application Settings The Output looks up as below :

enter image description here

Hope this helps :)

Brake answered 7/7, 2020 at 6:0 Comment(0)
L
0

What about this: https://learn.microsoft.com/en-us/azure/azure-functions/disable-function?tabs=portal#localsettingsjson

This looks like the easiest solution for local development.

Loats answered 26/8, 2021 at 14:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.