For a project I’m working on I needed to specify a outgoing proxy for accessing Azure Table Storage in a .NET console application.

Unfortunately the default way of setting a proxy in the app.config of classic .NET applications doesn’t work for .NET core.

After fiddling around for a bit I found the solution for setting it in a .NET core application (based on a answer on stackoverflow). If you use the Microsoft.Azure.Cosmos.Table Nuget package, instead of the old WindowsAzure.Storage package (I’m using version 1.0.1), the CloudTableClient allows you to pass in a TableClientConfiguration with a DelegatingHandler:

public TableStorage(string accountName, string keyValue, IWebProxy proxy)
{
	_storageAccount = new CloudStorageAccount(
                    new StorageCredentials(accountName, keyValue), true);
    var storageDelegatingHandler = new StorageDelegatingHandler(proxy);
    _tableClient = _storageAccount.CreateCloudTableClient(new TableClientConfiguration
            {
            	RestExecutorConfiguration = new RestExecutorConfiguration
               	{
               		DelegatingHandler = storageDelegatingHandler
               	} 
            });

/// further config

In the DelegatingHandler you can set the proxy for the HttpClientHandler:

public class StorageDelegatingHandler : DelegatingHandler
{
  private readonly IWebProxy _proxy;

  private bool _firstCall = true;

  public StorageDelegatingHandler() 
  : base()
  {
  }

  public StorageDelegatingHandler(HttpMessageHandler httpMessageHandler)
  : base(httpMessageHandler)
  {
  }

  public StorageDelegatingHandler(IWebProxy proxy)
  {
  	_proxy = proxy;
  }

  protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  {
    if (_firstCall && _proxy != null)
    {
      HttpClientHandler inner = (HttpClientHandler)InnerHandler;
      inner.Proxy = _proxy;
    }

    _firstCall = false;
    return base.SendAsync(request, cancellationToken);
  }
}

Now you can configure the proxy where you’re setting up dependency injection:

public class ProxySettings
{
  public bool Use => !string.IsNullOrWhiteSpace(Address);

  public string Address { get; set; }

  public bool BypassOnLocal { get; set; }
}
var proxySettings = Configuration
                .GetSection(nameof(ProxySettings))
                .Get<ProxySettings>();

IWebProxy proxy = null;
if (proxySettings != null && proxySettings.Use)
{
	proxy = new WebProxy(proxySettings.Address, proxySettings.BypassOnLocal);
	WebRequest.DefaultWebProxy = proxy;
}

services.AddSingleton<IStorage>(new TableStorage(Configuration["StorageAccountName"], Configuration["StorageAccountKey"], proxy));            

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.