9

I am deploying an Azure Front Door via an ARM template, and attempting to enable HTTPS on a custom domain.

According to the Azure documentation for Front Door, there is a quick start template to "Add a custom domain to your Front Door and enable HTTPS traffic for it with a Front Door managed certificate generated via DigiCert." However, while this adds a custom domain, it does not enable HTTPS.

Looking at the ARM template reference for Front Door, I can't see any obvious way to enable HTTPS, but perhaps I'm missing something?

Notwithstanding the additional information below, I'd like to be able to enable HTTPS on a Front Door custom domain via an ARM template deployment. Is this possible at this time?

Additional information

Note that there is a REST operation to enable HTTPS, but this does not seem to work with a Front Door managed certificate -

POST https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/frontDoors/{frontDoorName}/frontendEndpoints/{frontendEndpointName}/enableHttps?api-version=2019-05-01
{
    "certificateSource": "FrontDoor",
    "protocolType": "ServerNameIndication",
    "minimumTLSVersion": "1.2"
}

There is also a Az PowerShell cmdlet to enable HTTP, which does work.

Enable-AzFrontDoorCustomDomainHttps -ResourceGroupName "lmk-bvt-accounts-front-door" -FrontDoorName "my-front-door" -FrontendEndpointName "my-front-door-rg"
David Gard
  • 11,225
  • 36
  • 115
  • 227
  • have you tried adding an `enableHttps` section inside the `frontendEndpoint` properties ? if you add the `enablehttps` section with the content of the rest body it may work, also change the api version inside the arm to be `2019-05-01`. – Thomas Oct 01 '19 at 09:41
  • @Thomas, thanks for your reply. Where are you finding this documentation? According to the ARM template docs, the latest version is 2019-04-01. – David Gard Oct 01 '19 at 10:38
  • I mean ARM template is just wrapper around the REST API so the documentation may not be up to date. If something is available through REST API, ti should be available through ARM. – Thomas Oct 01 '19 at 11:09
  • 1
    What you can try is doing it manually then export your resource group ARM template to see if you can find the enablehttps section (reverse engineering). You can also tr using the [ARMClient](https://github.com/projectkudu/ARMClient) to discover the ARM schema to enable https – Thomas Oct 01 '19 at 11:12
  • I've tried exporting, and 2019-04-01 is used, with no reference to `enableHttps`. Will try using 2019-05-01 and see what happens... – David Gard Oct 01 '19 at 14:50
  • Confirming that apiVersion 2019-05-01 is accepted when used in an ARM template, but the 'customHttpsConfiguration' properties are seemingly ignored. – David Gard Oct 01 '19 at 15:01
  • Hsve you tried using the ARMClient ? It will show you the ARM template for a specific version. – Thomas Oct 01 '19 at 18:37
  • I tried using the ARMClient with apiVersion 2019-05-01 As @DavidGard says, the customHttps... properties are returned as null both for a frontendEndpoint that has custom domain SSL enabled and one that does not. – ma499 Oct 02 '19 at 19:19

5 Answers5

6

UPDATE: This implementation currently seems to be unstable and is working only intermittently, which indicates it may not be production ready yet.

This now actually seems to be possible with ARM templates, after tracking down the latest Front Door API (2020-01-01) specs (which don't appear to be fully published in the MS reference websites yet):

https://github.com/Azure/azure-rest-api-specs/tree/master/specification/frontdoor/resource-manager/Microsoft.Network/stable/2020-01-01

There's a new customHttpsConfiguration property in the frontendEndpoint properties object:

"customHttpsConfiguration": {
  "certificateSource": "AzureKeyVault" // or "FrontDoor",        
  "minimumTlsVersion":"1.2",
  "protocolType": "ServerNameIndication",

  // Depending on "certificateSource" you supply either:
  "keyVaultCertificateSourceParameters": {
    "secretName": "<secret name>",
    "secretVersion": "<secret version>",
    "vault": {
      "id": "<keyVault ResourceID>"
    }
  }

  // Or:
  "frontDoorCertificateSourceParameters": {
    "certificateType": "Dedicated"
  }
}

KeyVault Managed SSL Certificate Example

Note: I have tested this and appears to work.

    {
      "type": "Microsoft.Network/frontdoors",
      "apiVersion": "2020-01-01",
      "properties": {
        "frontendEndpoints": [
         {
            "name": "[variables('frontendEndpointName')]",
            "properties": {
              "hostName": "[variables('customDomain')]",
              "sessionAffinityEnabledState": "Enabled",
              "sessionAffinityTtlSeconds": 0,
              "webApplicationFirewallPolicyLink": {
                "id": "[variables('wafPolicyResourceId')]"
              },
              "resourceState": "Enabled",
              "customHttpsConfiguration": {
                "certificateSource": "AzureKeyVault",        
                "minimumTlsVersion":"1.2",
                "protocolType": "ServerNameIndication",
                "keyVaultCertificateSourceParameters": {
                  "secretName": "[parameters('certKeyVaultSecret')]",
                  "secretVersion": "[parameters('certKeyVaultSecretVersion')]",
                  "vault": {
                    "id": "[resourceId(parameters('certKeyVaultResourceGroupName'),'Microsoft.KeyVault/vaults',parameters('certKeyVaultName'))]"
                  }
                }
              }
            }
          }
        ],
        ...
      }
    }

Front Door Managed SSL Certificate Example

Looks like for a FrontDoor managed certificate you would need to set:

Note: I have not tested this

    {
      "type": "Microsoft.Network/frontdoors",
      "apiVersion": "2020-01-01",
      "properties": {
        "frontendEndpoints": [
         {
            "name": "[variables('frontendEndpointName')]",
            "properties": {
              "hostName": "[variables('customDomain')]",
              "sessionAffinityEnabledState": "Enabled",
              "sessionAffinityTtlSeconds": 0,
              "webApplicationFirewallPolicyLink": {
                "id": "[variables('wafPolicyResourceId')]"
              },
              "resourceState": "Enabled",
              "customHttpsConfiguration": {
                "certificateSource": "FrontDoor",        
                "minimumTlsVersion":"1.2",
                "protocolType": "ServerNameIndication",
                "frontDoorCertificateSourceParameters": {
                  "certificateType": "Dedicated"
                }
              }
            }
          }
        ],
        ...
      }
    }
Simon Gregory
  • 566
  • 7
  • 10
  • Great, thanks for the update, I'll give that a try as soon as I am able. – David Gard Apr 14 '20 at 08:46
  • This is interesting - looking at that reference, they want us to specify a secret name as opposed to a certificate name in AKV. Yet, when you try to upload a certificate as a secret through the portal, it says this feature is deprecated? *confused* – Jaffacakes82 May 21 '20 at 11:11
  • Ah - I see when you create a certificate in AKV now, it actually gives you a secret reference too. – Jaffacakes82 May 21 '20 at 11:16
  • Also, do you know if the Azure Frontdoor AD principal needs to be created as a pre-requisite to this? – Jaffacakes82 May 21 '20 at 11:51
  • Does this work with just using `CNAME afdverify.my.domain.name` or does it require `CNAME my.domain.name` pointing to the frond door? – Sebastian Jun 25 '20 at 07:24
  • 2
    At least for me it completely doesn't work. Deployment succeeds but this section simply being ignored. HTTP continues to be disabled. – Sergey Kostrukov Sep 22 '20 at 07:05
  • 1
    @Simon-Gregory looks nice. But it's just not in the templates; https://learn.microsoft.com/en-us/azure/templates/microsoft.network/frontdoors The link you are using refers to the Rest-api and not the Arm templates. – Kolky Jan 14 '21 at 15:51
  • @Kolky arm templates are basically a wrapper around the rest api calls. Quite often I find those arm template references to be out of date. This did work briefly at the start, but definitely not consistently. I haven't tried it recently but I may try this again in the coming days. – Simon Gregory Jan 29 '21 at 08:05
  • Thanks, @SimonGregory. I've also gained that insight recently. It was not working for me in december. Used a Powershell command to enable it instead. – Kolky Feb 01 '21 at 14:02
0

I was able to successfully make an enableHttps REST Call using the Azure Management API.

I got a successful response and can see the resource results in the portal.azure.com and resource.azure.com sites. However I am pretty sure the Management API, and PowerShell methods are the only ways supported right now. Since there is likely some validation required on the Certificate and Handling, they didn't include that yet in the ARM Templates. Given validation can be quite important, it is best you confirm your configuration is workable in the UI first, before automating it (IMHO).

  • In addition to the methods you mentioned you can also use the Azure CLI: https://learn.microsoft.com/en-us/cli/azure/ext/front-door/network/front-door/frontend-endpoint?view=azure-cli-latest#ext-front-door-az-network-front-door-frontend-endpoint-enable-https – Mike Becatti Nov 15 '19 at 16:07
  • I'm aware of both the REST API and the CLI, but in this case I specifically have a need to use an ARM template. – David Gard Nov 21 '19 at 10:37
  • You can create a Management API call as an ansible module. It is possible, but not necessarily straight-forward. – DaveKlassen Jan 13 '20 at 21:54
0

According to this discussion this seems only possible via the REST API (see e.g. this answer) and not (yet) via ARM.

Simon Opelt
  • 6,136
  • 2
  • 35
  • 66
0

I managed to get this working with an ARM template. The below link shows you how to do this using Azure Front Door as a certificate source: https://github.com/Azure/azure-quickstart-templates/blob/master/101-front-door-custom-domain/azuredeploy.json

I drew inspiration from this for deploying a certificate from Azure Key Vault for a custom domain. Here are the relevant elements from the ARM template that I am using:

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "hubName": {
            "type": "string",
            "metadata": {
                "description": "Name to assign to the hub. This name will prefix all resources contained in the hub."
            }
        },
        "frontdoorName": {
            "type": "string",
            "metadata": {
                "description": "Name to assign to the Frontdoor instance"
            }
        },
        "frontdoorCustomDomain": {
            "type": "string",
            "metadata": {
                "description": "The custom domain name to be applied to the provisioned Azure Frontdoor instance"
            }
        },
        "keyVaultCertificateName": {
            "type": "string",
            "metadata": {
                "description": "Name of the TLS certificate in the Azure KeyVault to be deployed to Azure Frontdoor for supporting TLS over a custom domain",
                "assumptions": [
                    "Azure KeyVault containing the TLS certificate is deployed to the same resource group as the resource group where Azure Frontdoor will be deployed to",
                    "Azure KeyVault name is the hub name followed by '-keyvault' (refer to variable 'keyVaultName' in this template)"
                ]
            }
        },
        ...
    },
    "variables": {
        "frontdoorName": "[concat(parameters('hubName'), '-', parameters('frontdoorName'))]",
        "frontdoorEndpointName": "[concat(variables('frontdoorName'), '-azurefd-net')]",
        "customDomainFrontdoorEndpointName": "[concat(variables('frontdoorName'), '-', replace(parameters('frontdoorCustomDomain'), '.', '-'))]",
        "keyVaultName": "[concat(parameters('hubName'), '-keyvault')]",
        "frontdoorHostName": "[concat(variables('frontdoorName'), '.azurefd.net')]",
        ...
    },
    "resources": [
        {
            "type": "Microsoft.Network/frontdoors",
            "apiVersion": "2020-05-01",
            "name": "[variables('frontdoorName')]",
            "location": "Global",
            "properties": {
                "resourceState": "Enabled",
                "backendPools": [...],
                "healthProbeSettings": [...],
                "frontendEndpoints": [
                    {
                        "id": "[concat(resourceId('Microsoft.Network/frontdoors', variables('frontdoorName')), concat('/FrontendEndpoints/', variables('frontdoorEndpointName')))]",
                        "name": "[variables('frontdoorEndpointName')]",
                        "properties": {
                            "hostName": "[variables('frontdoorHostName')]",
                            "sessionAffinityEnabledState": "Enabled",
                            "sessionAffinityTtlSeconds": 0,
                            "resourceState": "Enabled"
                        }
                    },
                    {
                        "id": "[concat(resourceId('Microsoft.Network/frontdoors', variables('frontdoorName')), concat('/FrontendEndpoints/', variables('customDomainFrontdoorEndpointName')))]",
                        "name": "[variables('customDomainFrontdoorEndpointName')]",
                        "properties": {
                            "hostName": "[parameters('frontdoorCustomDomain')]",
                            "sessionAffinityEnabledState": "Enabled",
                            "sessionAffinityTtlSeconds": 0,
                            "resourceState": "Enabled"
                        }
                    }
                ],
                "loadBalancingSettings": [...],
                "routingRules": [...],
                "backendPoolsSettings": {
                    "enforceCertificateNameCheck": "Enabled",
                    "sendRecvTimeoutSeconds": 30
                },
                "enabledState": "Enabled",
                "friendlyName": "[variables('frontdoorName')]"
            }
        },
        {
            "type": "Microsoft.Network/frontdoors/frontendEndpoints/customHttpsConfiguration",
            "apiVersion": "2020-07-01",
            "name": "[concat(variables('frontdoorName'), '/', variables('customDomainFrontdoorEndpointName'), '/default')]",
            "dependsOn": [
                "[resourceId('Microsoft.Network/frontdoors', variables('frontdoorName'))]"
            ],
            "properties": {
                "protocolType": "ServerNameIndication",
                "certificateSource": "AzureKeyVault",
                "minimumTlsVersion": "1.2",
                "keyVaultCertificateSourceParameters": {
                    "secretName": "[parameters('keyVaultCertificateName')]",
                    "vault": {
                        "id": "[resourceId(resourceGroup().name, 'Microsoft.KeyVault/vaults', variables('keyVaultName'))]"
                    }
                }
            }
        }
    ]
}
0

Azure Front Door classic now seems to support both managed certificates and custom certificates for custom domains. At least there are quickstart templates in the official repo from Microsoft exactly for these cases:

They both use Microsoft.Network/frontdoors/frontendEndpoints/customHttpsConfiguration subresource of the Front Door, currently with API version 2020-07-01. Only the parent subresource is documented in the templates reference, though.

The name of the customHttpsConfiguration resource is "default", so when the resource is specified as a top-level resource in the template, its complete name is something like "myfrontdoorafd/www-example-com/default".

Using Bicep (which transpiles to JSON ARM templates and which I highly recommend), the important part of the template looks like this:

param frontDoorName string
param customDomainName string

var frontEndEndpointCustomName = replace(customDomainName, '.', '-')

resource frontDoor 'Microsoft.Network/frontDoors@2020-01-01' = {
  name: frontDoorName
  properties: {
    frontendEndpoints: [
      {
        name: frontEndEndpointCustomName
        properties: {
          hostName: customDomainName
          ...
        }
      }
      ...
    ]
    ...
  }
  ...
  
  resource frontendEndpoint 'frontendEndpoints' existing = {
    name: frontEndEndpointCustomName
  }
}

// This resource enables a Front Door-managed TLS certificate on the frontend.
resource customHttpsConfiguration 'Microsoft.Network/frontdoors/frontendEndpoints/customHttpsConfiguration@2020-07-01' = {
  parent: frontDoor::frontendEndpoint
  name: 'default'
  properties: {
    protocolType: 'ServerNameIndication'
    certificateSource: 'FrontDoor'
    frontDoorCertificateSourceParameters: {
      certificateType: 'Dedicated'
    }
    minimumTlsVersion: '1.2'
  }
}

Note that the deployment will be in progress till the certificate is actually issued and deployed to all points of presence (PoP) of Azure. This may take really long and even fail due to RequestTimeout. If you want to just start the operation and let it complete asynchronously, use e.g. the enable-https subcommand in Azure CLI. Even after the failure, the customHttpsProvisioningState is Pending and the certificate provisioning process may complete successfully.

Also note that when you have many frontend endpoints and changes happen frequently but most frontend endpoints stay unchanged, the pattern from this template cannot be generalized just by specifying multiple customHttpsConfiguration instances for multiple frontend endpoints. Such a generalization is not efficient and likely hits the rate limit of the underlying API (429 TooManyRequests) because the API is called even when the endpoint already has the HTTPS configuration.

In such a case, I was able to use nested templates and conditional deployment to deploy the customHttpsConfiguration subresource only when the frontend endpoint's property customHttpsProvisioningState has the value of Disabled. This works OK even with tens of frontend endpoints when a new frontend endpoint is added (and it should get a managed certificate). Even in deployment mode Complete, the once-applied configuration persists.

Palec
  • 12,743
  • 8
  • 69
  • 138