After a certificate in Azure KeyVault is renewed, you might need to push it to the App Services that are using it. Certificates are stored in Azure as separate resources under the same resource group as the Azure Service Plan resides in.

If you’re using Infrastructure-as-Code (and you should) through ARM templates, you can redeploy the template that deploys the certificate resources. But there’s also another way if you don’t want to redeploy the templates (e.g. because it takes a long time depending on the amount of resources).

If you lookup the certificate through Azure Resource Explorer you can update the certificate though the UI. Just click the “Edit” button:

Don’t change anything to the request message and just click the “PUT” button. This will trigger Azure Resource manager to get the renewed certificate from Azure KeyVault and update it in the Service Plan.

Last week I was busy updating the certificates stored in Azure KeyVault for a project I’m working on. Previously we added the certificates that were referenced in App Services as secrets in the KeyVault with a application/x-pkcs12 type.

We now wanted to change those to real certificates, so the renewal of the certificates can be managed from Azure KeyVault.

After storing the certificates in KeyVault and modifying the ARM templates for the certificate resources to reference the new secretNames I ran into an ARM deployment error with the following message: “The parameter KeyVaultId & KeyVaultSecretName has an invalid value.”.

It turns out the new certificate we were referencing was already newer than the certificate stored in the previously referenced secret. Apparently this gives the very cryptic error message above. The solution is to make sure the certicates are the same, before deploying the ARM template with the updated secretname.

You can download the original certificate as .pfx with some Powershell you can find here.

After that import the .pfx into the new KeyVault certificate with the Import-AzureKeyVaultCertificate cmdlet.

Now you can redeploy the ARM template to update the keyVaultSecretName. After that you can update the certificate again.

In a previous post I wrote about adding one or more specified ranges of IP addresses to the IP security restrictions of an App Service.

In this post, I would like to take it one step further: add the possible outbound IP addresses of another App Service to the white list.

N.B. Securing a web app in this way is not a total security solution, because App Services of other Azure customers can share the same outbound IP addresses within the shared network infrastructure. If that’s a requirement, you will need to resort to the isolated App Service Environment, but that’s in a different pricing level.

But how do you do that? You can off course get the outbound IP addresses from another App Service by using the reference function within a template. But this is a comma separated list of addresses. So you will need to split this into an array and add the subnet mask (in CIDR notation) and action properties to use it as a property in the sites/config/ipSecurityRestrictions element I’ve described in a previous post. Things get more difficult when you realize you can only use the reference function in the resources of output section of a template. So there’s no way you can create a variable that gets the list (as a comma separated string) and splits it in to an array you can use further on in the template when deploying resources.

Enter multiple LinkedTemplates and a bit of useful information:

A template doesn’t need to deploy a resource

So this means you can create a template, that takes a resourceId for an App Service, gets the range of possible outbound IPs and returns it as an array:

But this doesn’t create an object array which we can pass to the template that adds the ranges to an App Service as described in the previous post.

So we create another template that takes an array of strings with the different IP’s and returns an array with the object in the required form:

We can then modify the first template to call this template and instead return the result of that template:

All that remains now, is to call this template from the main template that deploys the App Service and pass this to the template that actually adds the ranges to the whitelist:

At my current customer, we’re working on securing the services in Azure. One of the requirements includes setting IP-filtering in different App Services.

This can be done in ARM by setting a property ipSecurityRestrictions property of the sites/config element for an App Service. You need to assign an array of items that include the ipAddress (in CIDR format), action (Drop, Allow), priority (integer) and a name (optional, but useful for recognition of a rule in the networking blade of the Azure Portal).

The ranges are different for each service (accessible from Web Application Firewall, through internal proxy, other hostingprovider), but because the webapps that are deployed have a combination of different ranges we would like to administer the ranges in one central location.

Ideally, we can set a parameter for the LinkedTemplate that creates the App Service containing the ranges we want to whitelist.

We solved that in the following way:

Create a template with a variable per range you want to support (ip’s are chosen random):

"variables": {
    "EmptyRange": [],
    "LocalOutgoingRange": [
      {
        "ipAddress": "133.144.155.0/23",
        "action": "Allow",
        "name": "SGRange"
      }
    ],
    "WAFRange": [
      {
        "ipAddress": "99.100.101.0/21",
        "action": "Allow",
        "name": "WAFRange"
      },
      {
        "ipAddress": "100.100.100.0/19",
        "action": "Allow",
        "name": "WAFRange"
      }
    ]

Add a parameter to the template that contains the name of one or more ranges the deployment needs to add:

 "parameters": {
    "ipWhitelistingSets": {
      "type": "string",
      "defaultValue": "",
      "metadata": {
        "description": "Comma separated list of values LocalOutgoingRange,WAFRange"
      }
    },

Next, create two variables. the first one splits the parameter ipWhitelistingSets into an array. The next one uses this to conditionally concat the values of the ranges. If the range is not specified, the empty array is concatted, basically a NOOP.

 	"RangesToUse": "[split(parameters('ipWhitelistingSets'), ',')]",
    "ConcattedRange": "[concat(if(contains(variables('RangesToUse'), 'LocalOutgoingRange'), variables('LocalOutgoingRange'), variables('EmptyRange')), if(contains(variables('RangesToUse'), 'WAFRange'), variables('WAFRange'), variables('EmptyRange'))]",

We can then use this variable to loop through and set the rules in the webapp:

"resources": [
    {
      "comments": "IP whitelisting",
      "apiVersion": "2018-02-01",
      "location": "[resourceGroup().location]",
      "condition": "[not(empty(variables('RangesToUse')))]",
      "type": "Microsoft.Web/sites/config",
      "name": "[concat(parameters('webAppName'), '/web')]",
      "properties": {
        "mode": "Incremental",
        "copy": [
          {
            "name": "ipSecurityRestrictions",
            "count": "[length(variables('ConcattedRange'))]",
            "input": {
              "ipAddress": "[variables('ConcattedRange')[copyIndex('ipSecurityRestrictions')].ipAddress]",
              "action": "[variables('ConcattedRange')[copyIndex('ipSecurityRestrictions')].action]",
              "priority": "[copyIndex('ipSecurityRestrictions', 1)]",
              "name": "[variables('ConcattedRange')[copyIndex('ipSecurityRestrictions')].name]"
            }
          }
        ]
      }
    },

After this, we can call our template from the template that creates the App Service to set the ip restrictions:

{
      "comments": "Whitelisting WebApp Elements",
      "apiVersion": "2015-01-01",
      "name": "[concat(concat(parameters('webAppName'), 'wl'))]",
      "type": "Microsoft.Resources/deployments",
      "condition": "[not(empty(parameters('ipWhitelistingSets')))]",
      "properties": {
        "mode": "Incremental",
        "templateLink": {
          "uri": "[variables('ipWhitelistingTemplateUrl')]",
          "contentVersion": "1.0.0.0"
        },
        "parameters": {
          "webAppName": {
            "value": "[parameters('webAppName')]"
          },
          "ipWhitelistingSets": {
            "value": "LocalOutgoingRange,WAFRange"
          }          
        }
      },
      "dependsOn": [
        "[parameters('webAppName')]"
      ]
    }

Now we can use the same template to set different sets of restrictions for different App Services. If the IP ranges change, we have one location to modify the ranges and can simply deploy the resources again.

Summary: at the moment of writing, you cannot use Data Lake Storage Gen2 storage account as a target for Application Insights continuous export.

Currently Azure Data Lake Storage Gen2 is in preview.

Because I want to use Big Data analytics on Application Insights information I’ve set up a pipeline through Stream analytics in the past to get the application insights data into Azure Data Lake. Now that Gen2 is available I wanted to see if you can export from Application Insights into Data Lake Storage Gen2 straight away, bypassing stream analytics or another component to transfer the data. So lets try:

From the portal, when creating a Storage account, you can specify you want to enable Data Lake Storage Gen2:

Unfortunately, when setting up continuous export to this storage account, you cannot select a location, because creating a container is not possible:

The exclamation mark indicates the container name is invalid, but it does match the requirements.

When trying to create a container through Azure Storage Explorer, you get a more descriptive error:

So unfortunately, you cannot use continuous export to Data Lake Storage Gen2 right now. So we’re still stuck with using stream analytics or Azure Data Factory.