Search code examples
azureazure-functionsazure-resource-managerazure-rm-template

how to create an azure function key in ARM template?


I have been struggling with this one all day, I am trying to create a Function App function key from an ARM template.

So far I have been able to create my function key on the Host level using the following template:

    {
      "type": "Microsoft.Web/sites/host/functionKeys",
      "apiVersion": "2018-11-01",
      "name": "[concat(parameters('appServiceName'), '/default/PortalFunctionKey')]",
      "properties": {
        "name": "PortalFunctionKey"
      }

then I found a couple of articles and link showing it is possible via API: https://github.com/Azure/azure-functions-host/wiki/Key-management-API

And I was able to generate it via this API posting to: https://{myfunctionapp}.azurewebsites.net/admin/functions/{MyFunctionName}/keys/{NewKeyName}?code={_masterKey}

But I can't for the sake of me figure out how to do that in my ARM template! I have tried various combinations of type and name, for example:

    {
      "type": "Microsoft.Web/sites/host/functionKeys",
      "apiVersion": "2018-11-01",
      "name": "[concat(parameters('appServiceName'), '/{myfunctionName}/PortalFunctionKey')]",
      "properties": {
        "name": "PortalFunctionKey"
      }

or /functions/{myfunctionName}/PortalFunctionKey as suggested in some articles, and i just can't get any to work, can't find much documentation on ARM Microsoft.Web/sites/host/functionKeys either.

Did anyone succeed to create a FUNCTION key (not host) in ARM template? I would gladly hear how you got there :)!

Basically: Function Keys

Many thanks in advance,

Emmanuel


Solution

  • So it is not possible to create a Function level Function key in ARM template at the moment.

    I therefore created a feature request you can vote on if you are interested in it: https://feedback.azure.com/forums/169385-web-apps/suggestions/39789043-create-function-level-keys-for-azure-functions-in

    For now though, we are creating function level function keys via a powershell deployment task step. here is how.

    add output parameter to your ARM template:

      "outputs": {
        "masterKey": {
          "type": "string",
          "value": "[listkeys(concat(resourceId(resourceGroup().name, 'Microsoft.Web/sites', parameters('appServiceName')), '/host/default'), '2018-11-01').masterKey]"
        },
        "appServiceName": {
          "type": "string",
          "value": "[parameters('appServiceName')]"
        },
        "functionKeys": {
          "type": "array",
          "value": [
            {
              "functionName": "myFunctionName",
              "keys": [ "FunctionKeyName1", "FunctionKeyName2" ]
            }
          ]
        }
      }
    

    Add the following PS script file to your deploy project:

    param (
        [Parameter(Mandatory=$true)]
        [string]
        $armOutputString
    )
    
    Write-Host $armOutputString
    $armOutputObj = $armOutputString | convertfrom-json
    Write-Host $armOutputObj
    
    $masterKey = $armOutputObj.masterKey.value
    $appServiceName = $armOutputObj.appServiceName.value
    
    $httpHeaders = @{
        "x-functions-key" = $masterKey
    }
    $contentType = "application/json; charset=utf-8"
    
    foreach($function in $armOutputObj.functionKeys.value){
    
        $retryCount = 5;
        while ($true) {
            try {
                $uriBase = "https://$($appServiceName).azurewebsites.net/admin/functions/$($function.functionName)/keys"
                $existingKeys = Invoke-RestMethod -Method Get -Uri $uriBase -Headers $httpHeaders -ContentType $contentType
                break;
            }
            catch {
                if ($_.Exception.Response.StatusCode.value__ -eq 502) {
                    if ($retryCount-- -eq 0) {
                        throw;
                    }
                    else {
                        Write-Output ("Retry" + ": " + $_.Exception.Response.StatusCode + "; attempts=$retryCount")
                        [System.Threading.Thread]::Sleep(1000);
                        continue;
                    }
                }
                else {
                    throw;
                }
            }
        }
    
        foreach ($keyname in $function.keys){
            $keyExists = 0
            foreach($exstingKey in $existingKeys.keys){
                if($exstingKey.name -eq $keyname){
                    $keyExists = 1
                   Write-Host  "key $($keyname) already exists"
                }
            }
    
            if($keyExists -eq 0){
                $uri = "$($uriBase)/$($keyname)?code=$($masterKey)";
                Write-Host  "Adding $($keyname) key"
                Invoke-RestMethod -Method Post -Uri "$($uriBase)/$($keyname)" -Headers $httpHeaders -ContentType $contentType;
            }
        }
    }
    

    The script ensures that the key will not be overwritten if it already exists, and will retry if it fails (the function MUST exist and be running for the API to work).

    In your "Azure resource group deployment" task, under "Advanced" set "Deployment outputs" to "armDeployOutput".

    enter image description here

    Add a Powershell task, your script path to the powershell file in your deploy project, and set "Arguments" as "-armOutputString '$(armDeployOutput)'".

    enter image description here

    And that's it.