Enable HTTPS on Azure App Services with Azure Policy

8 Min. Read

Today, a common theme in cloud environments is enforcing organizational standards and adopting cloud governance since day one. And this is very important since it will give you the ability to define policies, processes, and procedures. These policies then dictate what can be done and verify that what does exist is correct. A service from Microsoft called Azure Policy is a great way to make that happen and take corrective action.

This article will demonstrate how to enable HTTPS on Azure App Services (Web App and Function App) with Azure Policy.

Introduction

Azure App Service enables you to build and host web apps, mobile back ends, and RESTful APIs in the programming language of your choice without managing infrastructure. It offers auto-scaling and high availability, supports both Windows and Linux, and enables automated deployments from GitHub, Azure DevOps, or any Git repo.

Azure Policy is a service in Azure that you use to create, assign, and manage policies. These policies enforce different rules and effects over your resources, so those resources stay compliant with your corporate standards and service level agreements. Azure Policy meets this need by continuously evaluating your resources for non-compliance with assigned policies.

You have a policy in your organization that dictates to enforce HTTPS on all new and existing App Services (web applications and functions) within your subscriptions. As you know, you should protect your applications from potential threats by encrypting data in transit.

If we take a quick look at Microsoft Defender for Cloud as shown in the figure below, we do have already a built-in Policy definition which is based on the default Azure Security Benchmark (V3) that requires having HTTPS enabled at the application level to pass this recommendation.

Security Center - FTPS should be required in your web App
Security Center – FTPS should be required in your web App

Based on this built-in policy, when you deploy a web app, Microsoft Defender for Cloud will evaluate the configuration and recommends enabling HTTPS to ensure server/service authentication and protect your data in transit from network layer eavesdropping attacks.

However, this policy does not auto-remediate the unhealthy resources, therefore, you should manually remediate them as described in the ‘Remediation steps‘, you can also use the ‘Quick fix logic‘ option to remediate all unhealthy resources at once, or you can trigger a logic app if you want. You can also exempt this recommendation if needed, but we don’t recommend doing so because it’s categorized as ‘Medium‘ severity.

Please note that when you enable HTTPS on your web app, it will redirect all HTTP traffic to HTTPS. So users with a custom domain should verify their domain has an SSL certificate before using this remediation. Because an HTTPS endpoint which doesn’t have an SSL certificate will show up in the browser with a “Privacy Error” and break your application. Therefore, users who have a custom domain need to verify they have set up an SSL certificate. Please make sure to notify the dev team before you remediate existing running system.

Once you have invested time and effort in remediating these resources to increase your Secure Score, you want to make sure that it will not decrease by a serious percentage, again. The problem is that the remediation is always done for existing resources, but NOT for new ones. You can think of remediating resources as a reactive versus a proactive approach.

As cybersecurity hygiene (practice), you need to have a solid Azure Governance to ensure that the new resources you deploy will have certain security standards, patterns, and configurations.

To ensure proper governance, we need to use Azure Policy. This will allow us to enable and enforce HTTPS on new deployed App Services, as well as remediating existing resources at scale, so you can make sure your organization’s policy and security requirements are met.

Prerequisites

To follow this article, you need to have the following:

1) An Azure subscription. If you don’t have an Azure subscription, you can create a free one here.

2) At least one web app or function app is deployed in your subscription.

3) You need to have the appropriate permissions to create and manage Azure Policy definitions. The Azure RBAC built-in roles that you can use are Resource Policy Contributor or Security Admin.

4) Last but not least, you need to have the appropriate permissions to assign the Contributor role for the Managed Identity (Application ID) created during the assignment of the policy either on a management group or a subscription, so the policy with “DeployIfNotExists” can remediate and modify your Web App settings. Azure Policy creates a managed identity for each assignment but must have details about what roles to grant the managed identity.

If the managed identity is missing roles, an error is displayed during the assignment of the policy or an initiative. Please note that when creating an assignment through the Azure Portal, the role assignments are auto-granted. However, when using PowerShell, you must manually configure the managed identity. More information can be found here.

Assuming you have all the prerequisites in place, take now the following steps:

Enable HTTPS Custom Policy definition

A quick overview of Azure Policy effects. Each policy definition that you create in the Azure Policy has a single effect. That effect determines what happens when the policy rule is evaluated to match. The effects behave differently if they are for a new resource, an updated resource, or an existing resource.

In this example, we need to set and enable HTTPS configuration for all Azure App Services and on existing resource(s) in our environment. For this to work, we need to use the “DeployIfNotExists” policy. To understand how the Azure Policy effect works with the “DeployIfNotExists” policy definition, please check the official documentation from Microsoft here.

Open the Azure Portal, click “All services” and then search for “Policy” and then click on “Definitions” → “+ Policy definition”.

For the “Definition location“, select the location by clicking the ellipsis [] and select either a management group or a subscription.

In the “Name“ field, give a descriptive name for the policy definition such as – “Enable HTTPS for Azure App Services” and Description.

In the “Category” section, select the appropriate category for this policy. In this example, we will choose “App Service” as shown in the figure below.

Create Azure Policy Custom Definition
Create Azure Policy Custom Definition

For the “POLICY RULE“, paste the following policy definition in JSON format and then click “Save“.

For the “Role definitions“, make sure that the Contributor is selected. The contributor role is required to set and enable HTTPS settings on the resources.

Here is the Policy definition in JSON format:

{
  "mode": "All",
  "policyRule": {
    "if": {
      "field": "type",
      "equals": "Microsoft.Web/sites"
    },
    "then": {
      "effect": "[parameters('effect')]",
      "details": {
        "type": "Microsoft.Web/sites",
        "name": "[field('name')]",
        "existenceCondition": {
          "field": "Microsoft.Web/sites/httpsOnly",
          "equals": "true"
        },
        "roleDefinitionIds": [
          "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"
        ],
        "deployment": {
          "properties": {
            "mode": "incremental",
            "parameters": {
              "webAppName": {
                "value": "[field('name')]"
              },
              "location": {
                "value": "[field('location')]"
              },
              "kind": {
                "value": "[field('kind')]"
              }
            },
            "template": {
              "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
              "contentVersion": "1.0.0.0",
              "parameters": {
                "webAppName": {
                  "type": "string"
                },
                "location": {
                  "type": "string"
                },
                "kind": {
                  "type": "string"
                }
              },
              "resources": [
                {
                  "type": "Microsoft.Web/sites",
                  "apiVersion": "2021-02-01",
                  "name": "[parameters('webAppName')]",
                  "location": "[parameters('location')]",
                  "kind": "[parameters('kind')]",
                  "properties": {
                    "httpsOnly": "true"
                  }
                }
              ]
            }
          }
        }
      }
    }
  },
  "parameters": {
    "effect": {
      "type": "String",
      "metadata": {
        "displayName": "Effect",
        "description": "Enable or disable the execution of the policy"
      },
      "allowedValues": [
        "DeployIfNotExists",
        "AuditIfNotExists",
        "Disabled"
      ],
      "defaultValue": "DeployIfNotExists"
    }
  }
}

In this custom policy, we are looking for all kinds of applications (web app, container, windows, and function app), and then we are checking if HTTPS is enabled.

The ExistenceCondition option in the template above is super useful for the Azure App Services that has already HTTPS settings enabled. If this condition is true, the policy definition will skip the deployment defined in the “DeployIfNotExists” section in the template. Please check the documentation here to learn more about the ExistenceCondition option for the “DeployIfNotExists” properties.

Assign Custom Policy definition

To assign the custom policy definition, take the following steps:

If you want to automate the deployment and assignment of Azure Policies via Azure DevOps instead of using the Azure Portal, then please check the following step-by-step guide.

Open the Azure Portal, click “All services” and then search for “Policy” and then click on “Assignments”. An assignment is a policy that has been assigned to take place within a specific scope.

Select “Assign policy” from the top of the “Policy | Assignments” page.

On the “Assign Policy” page, select the Scope by clicking the ellipsis [] and select either a management group or subscription. You can optionally select a resource group if you want. A scope determines what resources or grouping of resources the policy assignment gets enforced on. Then click Select at the bottom of the Scope page.

Select the Policy definition ellipsis [] to open the list of available definitions. Choose the custom policy that we created in the previous step. The Policy enforcement is Enabled by default. Click Next to continue.

On the Parameters page, specify the parameters for this policy assignment (DeployIfNotExists, AuditIfNotExists, or Disabled). The default value is set to “DeployIfNotExists“, you can disable the effect of this policy later on by setting the effect to “Disabled“. The “Audit” is used to create a warning event in the activity log when evaluating a non-compliant resource, but it doesn’t stop the request. Click Next to continue.

Assign Azure Policy Custom Definition
Assign Azure Policy Custom Definition

In the Remediation page, by default “Create a Managed Identity” is selected for you because existing resources can be updated via a remediation task after the policy is assigned only. Choose the default location for the “Managed Identity“, this is required because policies with the “deployIfNotExists” and “Modify” effect types need the ability to modify resources and edit the configuration on existing resources respectively. To do this, a managed identity will be created automatically for each policy assignment. This identity will also be given the “Contributor” permissions on the scope that you have selected. Click Next to continue.

Remediation - Create a Managed Identity
Remediation – Create a Managed Identity

On the Non-compliance messages page, set the desired message. Then click the “Review + create” button.

Azure Policy - Non-compliance message
Azure Policy – Non-compliance message

Finally, click “Create” to create the assignment.

Verify HTTPS on Azure App Services

To verify that the custom policy is deployed successfully, you need to wait for at least 30 minutes after a resource has been created or updated. The policy won’t be triggered immediately, this is by design. To trigger the policy compliance scan manually (immediately), open the cloud shell and run the following command:

$job = Start-AzPolicyComplianceScan -AsJob
$job | ft -AutoSize

The scan job will run in the background as shown in the output below.

Trigger Azure Policy Compliance Evaluation Manually
Trigger Azure Policy Compliance Evaluation Manually

If you want to check Azure Policy compliance status and remediate non-compliant resources via Azure DevOps Pipelines, then please check the following step-by-step guide.

Behind the scene, Azure Policy will create a Remediation task as shown in the figure below. To remediate existing resources, select the desired policy and then click on the “Remediate” button.

Policy Remediation - Enable HTTPS for Azure App Services
Policy Remediation – Enable HTTPS for Azure App Services

Please note that during an evaluation cycle, the policy definition with a “DeployIfNotExists” effect that matches resources is marked as non-compliant only, but no action is taken on that resource. For this reason, the remediation task was created when you assign the policy definition as described in the previous section.

Open the Azure Portal, click “All services” and then search for “Policy” and then click on “Remediation”. In the Policy | Remediation page, select the Remediation tasks tab, and check the status of the auto-remediation task. It’s completed successfully without any error.

Azure Policy - Remediation tasks
Azure Policy – Remediation tasks

If you look at the Web App resource under Settings | TLS/SSL settings, you can see that the “HTTPS Only” toggle is set to On.

Azure App Service - HTTPS Only
Azure App Service – HTTPS Only

If we look at the compliance policy state, we will see that all Azure App Services in this environment are compliant and HTTPS is enabled.

Azure Policy Compliance State
Azure Policy Compliance State

That’s it there you have it!

Summary

In this article, we showed you how to enable and enforce HTTPS on Azure App Services with Azure Policy at the application level, so you can make sure your organization’s policy and security requirements are met.

Please note that you need to have a separate custom policy for each Azure resource or for each configuration you need to modify such as enabling FTPS or HTTPS. You could create all the needed policy definitions for your environment and then add them all to an initiative definition and then assign the initiative scope based on Management Group, Subscription, or Resource Group. We highly recommend using Management Groups if you are not doing so already.

To learn more about auditing publicly accessible Azure App Services with Azure Policy, please check the following step-by-step guide.

To learn more about auditing subnets that do not have Network Security Group (NSG) associated, please check the following step-by-step guide.

To learn more about Azure Policy, please check the official documentation from Microsoft.

__
Thank you for reading my blog.

If you have any questions or feedback, please leave a comment.

-Charbel Nemnom-

Related Posts

Previous

Enable FTPS on Azure App Services with Azure Policy

Enable Azure Backup on VMs using Azure Policy

Next

24 thoughts on “Enable HTTPS on Azure App Services with Azure Policy”

Leave a comment...

  1. Wonderful article Charbel, I’m working exactly the same policy which enforces HTTPS on web apps as expected. My only concern regarding the DeployIfNotExists type of policy is whether other properties that aren’t included in the template get reset as well, I don’t know if there is any way to rule out this possibility completely. Any thoughts? Thanks.

  2. Hello Tim, thank you for the feedback!
    Good question. The DeployIfNotExists type of policy won’t reset any other properties which aren’t included in the template because, under the deployment section properties, we are using the “incremental” mode so only what we have specified will get updated which in this case “Enable HTTPS”. I’ve tested it many times in production and it works as expected without breaking any other settings. Hope this helps!

  3. Charbel,
    Is there a way to deploy this so that it sends a message to the person trying to create a non-secure app but doesn’t stop the deployment?

  4. Hello Frank, thanks for the comment and feedback!
    Yes, you could switch the Policy effect from “DeployIfNotExists” to “Audit”. I have updated the ARM template to take into account the “Audit” effect.
    In this case, the non-secure app doesn’t stop the deployment.
    The audit is used to create a warning event in the activity log when evaluating a non-compliant resource, but it doesn’t stop the request.
    Then you could develop a function or logic app to look for the warning event in the activity log and send an e-mail message to the person who created a non-secure app.
    Hope this helps!

  5. I understand Tim’s concern since the official documentation does state that properties that are not mentioned are reset to default values:
    https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/deployment-modes#incremental-mode

    However, this isn’t the behavior I am seeing myself. I do sometimes encounter conflicts between a non-related Deny Policy that gets triggered when a DeployIfNotExists Policy attempts to set a property that isn’t even referred to in the Deny Policy. So that makes me assume that Azure does interpret non-mentioned values as default?

  6. Thank you, Yannick for the comment!
    As noted in the official documentation, if we don’t specify certain properties, the Resource Manager (ARM) interprets the deployment as overwriting those values. So the Properties that aren’t included in the template are reset to the default values.
    We must specify all non-default values for the resource, not just the ones we’re updating.
    To answer your question. Yes, Azure does interpret non-mentioned values as default values.
    Hope this helps!

  7. Hi Charbel,

    Thanks for your nice article.
    I followed the same procedure, created the same policy with user-managed identity.
    After that, I created a function app (by default HTTPS disabled), but it does not auto remediate the setting and reports as non-compliant.
    Can you please help with this?

  8. Hello Venkat, thanks for the comment!
    Please note that the policy might take 30 minutes to take effect.
    One question, have you tried to remediate the non-compliant function app manually as illustrated in the “Verify HTTPS on Azure App Services” section?
    Another test you can make is, try to create a web app instead of a function app, same results?
    Hope this helps!

  9. Hi Charbel,

    I could remediate the first function app non-compliance using remediation tasks manually, but after remediation, compliance status still shows as non-compliant.
    After that I created a web app with only HTTP, did not auto remediate, the setting is with HTTP only, but showing as compliant.
    Any suggestion?

  10. Thanks for your time, you are really striving to help.
    1. There was a function app before applying the policy. I assigned this policy, did not auto remediate, and showing as compliant.
    2. Then I created a new web app, same result, did not auto remediate and showed as compliant.
    It is behaving totally strangely for this parameter.

  11. Hi Charbel,

    I have applied the policy, after that, I created the first function app, it works fine, it auto remediated the non-compliant.
    I created 2nd function app after some, which did not auto remediate but showed as compliant.
    In 1st function app activity log, I could see managed identity trying to log in and remediate.
    In the 2nd function app activity log, there are no logs for managed identity login.

    Do we need to add exists and equals conditions in the condition also?

    Thanks

  12. Hi Charbel,

    It is the same as the above behavior.
    I applied the policy, after that I created 1st function app, it works fine, auto remediated the issue, showing as compliant.
    After some time, I created 2nd function app, which did not auto remediate but showed as compliant.

    Is it possible to try once from your end to check whether behaving differently?

    Thanks,
    Venkat

  13. Hello Venkat,
    It’s working from my side end-to-end without any issue.
    The remediation is working fine, as well as the compliance report for my resources (web app/function app) is correct.
    What I would suggest is to delete the existing Policy assignment, and then create a new assignment.
    Sorry, I can’t help further on this, it requires looking at your environment.
    Thanks!

  14. In my env, your JSON Policy shows that I only have 7 web apps and I have 20 in total. And For test purposes, I disabled HTTPS on more than 7. Policy behaves very weirdly…

  15. Hello Oleg, thanks for the comment and for sharing your experience!
    Please note that I have found the issue after looking deeper at different scenarios.
    We had to update to the latest API and we had to specify the application name explicitly.
    The Policy has been updated. I have tested it with existing web and function apps, as well as deploying new web/functions apps, the remediation is working as expected.
    The reporting is working correctly now, if you have disabled HTTPS manually, then the policy will kick in and fix it automatically.
    Please take a new copy of the policy and try it.
    Hope this helps!

  16. Hi Charbel, Thanks for your effort in updating the policy.
    It is perfectly working for the function app.
    It is showing the correct compliant status for the existing function app, but it does not auto-remediate existing resources before applying the policy, which is expected.
    For the new function app which created applying the policy, it is auto remediating. Also if we change the HTTP setting manually, it is auto remediating correctly.
    Fantastic, it is working for function.

    For web apps, it is not auto-remediating for new web apps which are created after applying the policy.

    Do we need to change the policy rule?

    Thanks,
    Venkat

  17. Thanks, Venkat for the confirmation, I am glad that it’s working now :)
    Please remove this part from the policy and it should also auto-remediate new web apps which are created after applying the policy.

     {
              "field": "kind",
              "contains": "app"
    }

    The policy was updated as well to take into consideration this scenario.
    Thanks for validating all scenarios!

  18. Hi Charbel,
    I had tested after removing the above two lines, but it does not compliant status for web app and function app which is created before applying the policy, it works fine for resources that are created after the policy.

    Thanks,
    Venkat

  19. Hello Venkat, I am sorry to say, this is not correct.
    I have it deployed into two different environments/tenants in production.
    I have 23 App Services in total (web apps and functions apps), all are compliant, those resources were deployed before the policy was assigned.
    The compliance report is accurate in both environments, here is a screenshot as a reference.
    HTTPS - Azure Policy Compliance Report
    Remove the assignment and then assign it again. If you still have inaccurate results, please open a support case with Microsoft.
    Thanks!

  20. Hi Charbel,

    I have removed and reassigned again, now it is working as expected.
    Anyway, I will check further on this.

    Thanks for your effort and really appreciated it.

    Thanks,
    Venkat

Let me know what you think, or ask a question...

The content of this website is copyrighted from being plagiarized!

You can copy from the 'Code Blocks' in 'Black' by selecting the Code.

Please send your feedback to the author using this form for any 'Code' you like.

Thank you for visiting!