Blog Post

Azure Infrastructure Blog
6 MIN READ

Enable IP restriction for a public facing App service

KrishnaM2265's avatar
KrishnaM2265
Icon for Microsoft rankMicrosoft
Oct 22, 2024


In this blog article, we will cover how to control the app service deployment to support only public facing app service with IP restriction enabled.

 
Note: The policy should allow the end-user experience to be the same whether they deploy the app service using the Azure portal, ARM template, or terraform.

Policy Definition

Note: The policy below is applicable only for the resource type Microsoft.Web/sites.


{

    "mode": "All",

    "policyRule": {

      "if": {

        "allOf": [

          {

            "field": "Microsoft.Web/sites/publicNetworkAccess",

            "equals": "Enabled"

          },

          {

            "anyOf": [

              {

                "allOf": [

                  {

                    "field": "Microsoft.Web/sites/siteConfig.ipSecurityRestrictionsDefaultAction",

                    "notEquals": "Deny"

                  }

                ]

              },

              {

                "allOf": [

                  {

                    "field": "Microsoft.Web/sites/siteConfig.ipSecurityRestrictionsDefaultAction",

                    "equals": "Deny"

                  },

                  {

                    "not": {

                      "count": {

                        "field": "Microsoft.Web/sites/siteConfig.ipSecurityRestrictions[*]",

                        "where": {

                          "count": {

                            "value": "[if(equals(parameters('environment'), 'prod'), parameters('allowedIPAddressesProd'), parameters('allowedIPAddressesDev'))]",

                            "name": "allowedIpAddress",

                            "where": {

                              "allOf": [

                                {

                                  "value": "[if(equals(current('Microsoft.Web/sites/siteConfig.ipSecurityRestrictions[*].ipAddress'), 'Any'), 'true', ipRangeContains(current('allowedIpAddress'), current('Microsoft.Web/sites/siteConfig.ipSecurityRestrictions[*].ipAddress')))]",

                                  "equals": true

                                }

                              ]

                            }

                          },

                          "greater": 0

                        }

                      },

                      "equals": "[length(field('Microsoft.Web/sites/siteConfig.ipSecurityRestrictions[*]'))]"

                    }

                  }

                ]

              }

            ]

          }

        ]

      },

      "then": {

        "effect": "[parameters('effect')]"

      }

    },

    "parameters": {

      "effect": {

        "type": "String",

        "metadata": {

          "displayName": "Effect",

          "description": "Enable or disable the execution of the policy."

        },

        "allowedValues": [

          "Audit",

          "Deny",

          "Disabled"

        ],

        "defaultValue": "Audit"

      },

      "environment": {

        "type": "String",

        "metadata": {

          "displayName": "Environment",

          "description": "Select the environment to apply the correct IP restrictions."

        },

        "allowedValues": [

          "dev",

          "prod"

        ],

        "defaultValue": "prod"

      },

      "allowedIPAddressesDev": {

        "type": "Array",

        "metadata": {

          "displayName": "Allowed IP addresses for Dev",

          "description": "Array with allowed public IP addresses for the Dev environment."

        },

        "defaultValue": [

          "203.0.113.0/24"

        ]

      },

      "allowedIPAddressesProd": {

        "type": "Array",

        "metadata": {

          "displayName": "Allowed IP addresses for Prod",

          "description": "Array with allowed public IP addresses for the Prod environment."

        },

        "defaultValue": [

          "198.51.100.0/24",

          "203.0.113.5/32"

        ]

      }

    }

  }

 

 

 

 

Explanation about policy

The above policy will not allow any public-facing app service to be created unless their public access is "Disabled" or it satisfies the following conditions:

  1. Enabled from selected network
  2. Default action should be deny
  3. Only trusted IP’s are allowed based on the environment i.e above policy is using 198.51.100.0/25 and 203.0.113.5/32 as an example.

    Note: You can add more trusted IPs, the above IPs are just examples.

 

 

 

 

 

 

 

 



 

Deployment using Azure portal

If we try to deploy app service using azure portal, in the networking tab we could see only two option i.e enable public access or disable public access.

 

If we keep enable public access as "ON", we will be blocked from creating the app service with the following error.

 

 

Now we know that the portal only supports either enabling or disabling public access for the app service, so we need to create another policy that changes the setting from disable public access to enable public access with IP restrictions in place. Please find the policy below, which will perform the deployment after the app service is deployed through the Azure portal.

Policy definition

The policy below identifies resources like Microsoft.Web/sites and if it finds the app service has public access disabled, it will modify the networking setting of the app service. Via the Azure portal, it is not possible to create an app service with a firewall, and it requires manual changes to be done.


Note: The policy below will automatically enable IP restrictions on newly created app services using the Azure portal with public access disabled for an app service.

 

{

    "mode": "All",

    "policyRule": {

      "if": {

        "allOf": [

          {

            "field": "type",

            "equals": "Microsoft.Web/sites"

          },

          {

            "anyOf": [

              {

                "field": "Microsoft.Web/sites/publicNetworkAccess",

                "exists": "false"

              },

              {

                "field": "Microsoft.Web/sites/publicNetworkAccess",

                "equals": "Disabled"

              }

            ]

          }

        ]

      },

      "then": {

        "effect": "[parameters('effect')]",

        "details": {

          "type": "Microsoft.Web/sites/config",

          "evaluationDelay": "AfterProvisioningSuccess",

          "existenceCondition": {

            "field": "Microsoft.Web/sites/config/minTlsVersion",

            "equals": "1.1"

          },

          "name": "web",

          "roleDefinitionIds": [

            "/providers/Microsoft.Authorization/roleDefinitions/de139f84-1756-47ae-9be6-808fbbe84772",

            "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"

          ],

          "deployment": {

            "properties": {

              "mode": "incremental",

              "parameters": {

                "siteName": {

                  "value": "[field('name')]"

                },

                "ipAddresses": {

                  "value": "[parameters('ipAddresses')]"

                }

              },

              "template": {

                "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",

                "contentVersion": "1.0.0.0",

                "parameters": {

                  "siteName": {

                    "type": "string"

                  },

                  "ipAddresses": {

                    "type": "array"

                  }

                },

                "resources": [

                  {

                    "type": "Microsoft.Web/sites/config",

                    "apiVersion": "2021-02-01",

                    "name": "[concat(parameters('siteName'), '/web')]",

                    "properties": {

                      "publicNetworkAccess": "Enabled",

                      "ipSecurityRestrictionsDefaultAction": "Deny",

                      "copy": [

                        {

                          "name": "ipSecurityRestrictions",

                          "count": "[length(parameters('ipAddresses'))]",

                          "input": {

                            "ipAddress": "[parameters('ipAddresses')[copyIndex('ipSecurityRestrictions')]]",

                            "action": "Allow",

                            "priority": "[add(100, copyIndex('ipSecurityRestrictions'))]",

                            "name": "[concat('ipRestriction_', copyIndex('ipSecurityRestrictions'))]"

                          }

                        }

                      ]

                    }

                  }

                ],

                "outputs": {}

              }

            }

          }

        }

      }

    },

    "parameters": {

      "effect": {

        "type": "String",

        "metadata": {

          "displayName": "Effect",

          "description": "Enable or disable the execution of the policy"

        },

        "allowedValues": [

          "DeployIfNotExists",

          "Disabled"

        ],

        "defaultValue": "DeployIfNotExists"

      },

      "ipAddresses": {

        "type": "Array",

        "metadata": {

          "displayName": "ipAddresses",

          "description": "List of IP addresses to allow access to the web app."

        },

        "defaultValue": [

          "198.51.100.0/24"

        ]

      }

    }

  }


Deployment using ARM template

 

We export the ARM template of an existing app service and modify the required parameters to deploy another app service with IP restrictions in place. However, the policy doesn’t allow the creation of an app service and is blocked with the same error message.


If we closely look at the policy rule of an IP restriction policy, it is checking the field Microsoft.Web/sites/siteconfig.

 

But, the exported ARM template is using the field below.

 

Similar to what Azure policy supports, we tried to define all these siteconfig attributes under Microsoft.Web/sites and tried to deploy the ARM template and removed that resource completely from the ARM template.

 

 

 

 

 

Deployment using Terraform

 

Once the IP restriction policy is in place, try to use the below azurerm providers to deploy the app service with IP restrictions enabled.

https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/linux_function_app --- Linux function app

https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/windows_function_app ---Windows function app

We need to use the above modules to deploy the app service with the attributes below set for the app service or it must be supported.

 

 

Disclaimer

  • Please note that products and options presented in this article are subject to change. This article reflects custom policy for enable ip restriction for app service in October 2024.
  • If users have the required permissions, they can create exemptions for their resources, which makes this policy ineffective for those resources.
  • It is highly recommended to test this policy in a non-production environment before applying it to your production environment to avoid any unintended disruptions and to make sure it meets your requirements.

 

 

References

https://learn.microsoft.com/en-us/azure/governance/policy/concepts/definition-structure-policy-rule#policy-functions 

Programmatically create policies - Azure Policy | Microsoft Learn

Troubleshoot common errors - Azure Policy | Microsoft Learn

Overview of Azure Policy - Azure Policy | Microsoft Learn

 

 

Published Oct 22, 2024
Version 1.0
  • Magelon40's avatar
    Magelon40
    Copper Contributor

    We are trying to use this policy with both Azure functions and WebApps with the dotnet runtime on a premium ASP. Which is why we are a bit confused why the populated properties are empty/null.

  • The Guide is up to date, could you please let us know what type of app service you are trying to create? As its only supported for certain type of resources that has those functionalities of enabling IP restriction (web app, function app and logic app) and policy is not applicable for static web app, web app + database and  wordPress on App Service. Hope this helps, feel free to reach out to us if you still facing any issues in the policy implementation.

  • Magelon40's avatar
    Magelon40
    Copper Contributor

    Is this guide up to date? When I attempt to use this policy, the value of the "IpSecurityRestrictionsDefaultAction" and "ipSecurityRestrictions" are null regardless of the deployment method of the app service resource (Portal GUI, ARM or terraform).

    • victormelhuus's avatar
      victormelhuus
      Copper Contributor

      I don't think so. I just tested it myself and the variables the policy is checking aginst is indeed null (the properties are hidden for some reason). It could work during deployment, but it doesn't seem to work against allready deployed resources (at least for me).