Automate Azure Firewall Rules Privately

Build a private automation pipeline to manage Azure Firewall rules via Logic Apps.

Introduction

30 Second Summary

Every time your team needs a new firewall rule, someone has to log into the portal and manually enter the parameters. One typo in an IP range could block legitimate traffic or quietly open a security hole.

In this project, you will build a private automation pipeline where a simple web form triggers the creation of Azure Firewall rules. The entire flow stays locked inside a virtual network, so the automation endpoint is never exposed to the public internet.

What You'll Build

You'll fill in a web form with a rule name, source IP, destination, port, and action, click submit, and watch the new rule appear in your Azure Firewall Policy within seconds.

By the end of this project, you'll have:

  • A web form hosted on an App Service where you type in a rule name, source address, destination, port, and action, then click submit to trigger the automation.
  • A private Logic App workflow that receives your request through a VNet, authenticates via a managed identity, and calls the Azure Firewall REST API to create the rule automatically.
  • A new network rule that appears in your Firewall Policy's rule collection visible in the portal within seconds of submitting the form, with the entire automation path secured by a private endpoint so nothing touches the public internet.
  • Secret Mission: Add input validation to your workflow that rejects invalid CIDR ranges and prevents duplicate rule names.

Are there any prerequisites?

You need an Azure subscription with permissions to create resources (Owner or Contributor role at the subscription level). All resources are created from scratch during this project, so no prior Azure infrastructure is required.

Before We Start

Before We Start...

Before we dive in, let's lock in what you're about to build and why it matters.

You are building a private automation pipeline that lets authorized internal applications request firewall rule changes without exposing the automation endpoint to the public internet. This matters because firewall modifications are security-sensitive operations that should never be triggered from untrusted networks.

Deploy the Network Foundation and Azure Firewall

Every resource in this project needs a private network to communicate through. The Virtual Network provides that private backbone, and Azure Firewall with its Policy gives you the target resource whose rules you will automate.

In this step you will create the networking infrastructure that everything else plugs into. You will set up a VNet with four purpose-built subnets, deploy an Azure Firewall, and configure a Private DNS Zone for name resolution.

In this step, get ready to:
  • Create a Resource Group and Virtual Network with four subnets.
  • Deploy an Azure Firewall Policy and Azure Firewall (Standard tier).
  • Set up a Private DNS Zone linked to your VNet.
Create a Resource Group and Virtual Network

A Resource Group is a container that holds all related Azure resources together. The Virtual Network needs four subnets, each dedicated to a specific service.

  • In the Azure portal search bar at the top, type Resource group and select Resource groups from the results.
  • Select + Create.
  • Enter fw-automation-rg as the Resource group name.
  • For Region, select East US (or whichever region is closest to you).
  • Select Review + create.
  • Select Create.

You should see a notification confirming the resource group was created.

  • In the search bar, type Virtual network and select Virtual networks from the results.
  • Select + Create.
  • On the Basics tab, select fw-automation-rg as the Resource group.
  • Enter fw-automation-vnet as the Name.
  • Set Region to the same region you chose for your resource group.
  • Select Next to proceed to the Security tab.
  • Select Next again to proceed to the IP Addresses tab.

On the IP Addresses tab, you will configure the address space and add four subnets.

  • Confirm the address space is 10.0.0.0/16 (this is the default).
  • Delete the default subnet if one exists (select it, then click the trash icon).
  • Select + Add a subnet and enter AzureFirewallSubnet as the Name, starting address 10.0.1.0, size /26. Select Add.
  • Select + Add a subnet again and enter logic-app-subnet, starting address 10.0.2.0, size /27. Select Add.
  • Select + Add a subnet again and enter private-endpoint-subnet, starting address 10.0.3.0, size /27. Select Add.
  • Select + Add a subnet one more time and enter webapp-integration-subnet, starting address 10.0.4.0, size /27. Under Delegate subnet to a service, select Microsoft.Web/serverFarms. Select Add.
  • Select Review + create.
  • Select Create.

Why four subnets?

Each subnet isolates a different component. AzureFirewallSubnet is a required name for the firewall (it must be /26 or larger).

The other three subnets host the Logic App, its private endpoint, and the App Service that triggers it. Keeping them separate enforces network isolation between services.

Navigate to your new VNet by searching Virtual networks and selecting fw-automation-vnet. In the left menu, select Subnets. You should see all four subnets listed with their correct address ranges.

Don't see all four subnets?

Make sure the address ranges do not overlap. If you see a validation error during creation, check that each starting address is unique (10.0.1.0, 10.0.2.0, 10.0.3.0, 10.0.4.0).

If AzureFirewallSubnet is missing, add it manually from the Subnets page. The name must be exactly AzureFirewallSubnet with that capitalisation.

Help me troubleshoot my VNet subnets.

Create an Azure Firewall Policy and Azure Firewall

The Azure Firewall Policy stores your firewall rules. The firewall itself enforces them. You will create both together, and the firewall will take about 5 to 10 minutes to provision.

  • In the portal search bar, type Firewall and select Firewalls from the results.
  • Select + Create.
  • On the Create a Firewall page, select fw-automation-rg as the Resource group.
  • Enter fw-automation-firewall as the Name.
  • Set Region to the same region as your resource group.
  • For Firewall SKU, select Standard.
  • For Firewall management, select Use a Firewall Policy to manage this firewall.
  • For Firewall policy, select Add new. Enter fw-automation-policy as the name, select your region, and select OK.
  • Under Choose a virtual network, select Use existing, then select fw-automation-vnet.
  • For Public IP address, select Add new and enter fw-pip as the name. Select OK.
  • Select Review + create.
  • Select Create.

How long does this take?

Azure Firewall takes approximately 5 to 10 minutes to provision. You do not need to wait here. Proceed to the next substep while the deployment runs in the background.

Create a Private DNS Zone and link it to the VNet

The Logic App you will deploy later uses the same underlying infrastructure as Azure App Service. Its private endpoint hostname lives under privatelink.azurewebsites.net.

Without a Private DNS Zone for that domain linked to your VNet, other services in the VNet cannot resolve the Logic App's private address.

  • In the portal search bar, type Private DNS zones and select Private DNS zones from the results.
  • Select + Create.
  • Select fw-automation-rg as the Resource group.
  • Enter privatelink.azurewebsites.net as the Name.
  • Select Review + Create, then select Create.

Once the zone is created, link it to your VNet.

  • Open the privatelink.azurewebsites.net zone you just created.
  • In the left menu under DNS Management, select Virtual network links.
  • Select + Add.
  • Enter vnet-link as the Link name.
  • For Virtual Network, select fw-automation-vnet.
  • Leave Enable auto registration unchecked (you do not need auto-registration for this project).
  • Select Create.

Wait until the link status shows Completed in the Virtual network links list.

Why this DNS zone?

Logic App Standard runs on App Service infrastructure. When you create a private endpoint for it later, Azure registers an A record in this privatelink.azurewebsites.net zone pointing to the private IP.

Services inside the VNet query this zone and get the private IP instead of the public one. That is how private-only access works.

Before you verify the firewall, think about what you expect to see. Do you think the provisioning state will show Succeeded, or might it still be running?

  • In the portal search bar, type Firewalls and select Firewalls.
  • Select fw-automation-firewall.
  • On the Overview page, confirm Provisioning state shows Succeeded and the Firewall policy field shows fw-automation-policy.

What should I see?

If you see Provisioning state: Succeeded, your firewall is ready. If it still says Updating, wait another minute and refresh the page. The deployment typically finishes within 10 minutes.

Firewall provisioning failed?

The most common cause is a missing or misnamed AzureFirewallSubnet. Azure Firewall requires a subnet named exactly AzureFirewallSubnet with a minimum size of /26.

Verify the subnet exists in your VNet with the correct name and size, then retry the firewall deployment.

Help me troubleshoot my Azure Firewall deployment failure.

Your network foundation is in place. Next up, you will deploy a Logic App and build a workflow that can receive HTTP requests and respond.

Deploy the Logic App and Create a Basic Workflow

Your network foundation is in place. The VNet, subnets, and Azure Firewall are ready to receive rules. Now you need something that can call the Firewall's REST API on your behalf.

In this step, you will deploy an Azure Logic App Standard workflow with an HTTP trigger that accepts firewall rule requests. You are starting with a public endpoint so you can verify the trigger works before locking it down in the next step.

In this step, get ready to:
  • Create a Logic App Standard resource with VNet integration and a managed identity.
  • Build a stateful workflow with an HTTP trigger and a Response action.
  • Test the workflow and confirm a successful run in Run History.
Create the Logic App Standard resource

The Logic App needs to live inside the same VNet so it can later communicate privately with other resources. You will configure networking during creation and enable a system-assigned managed identity so the Logic App can authenticate to Azure APIs without storing credentials.

  • In the Azure portal search bar, type Logic App and select Logic apps.
  • Click Add.
  • Under Standard, select Select.
  • On the Basics tab, select your existing Resource Group.
  • Enter a Logic App name such as firewall-automation-logic.
  • Select the same region as your other resources.
  • For Windows Plan, click Create new and choose the WS1 pricing tier.

What is the WS1 plan?

Workflow Standard WS1 is the entry-level App Service plan for Logic Apps Standard. It supports VNet integration, private endpoints, and system-assigned managed identities. It costs approximately $0.21/hr while running.

  • Switch to the Networking tab.
  • Set Enable public access to On (you will disable this in the next step).
  • Set Enable network injection to On.
  • In the Virtual Network dropdown, select your VNet.
  • Under Outbound access, set Enable VNet integration to On and select the logic-app-subnet subnet.
  • Click Review + create, then Create.
  • Wait for the deployment to complete, then click Go to resource.

Now enable the system-assigned managed identity so the Logic App can authenticate to Azure services later.

  • In the Logic App's left menu, go to Settings > Identity.
  • On the System assigned tab, toggle Status to On.
  • Click Save, then confirm by clicking Yes.

The Identity blade now shows the Status as On with an Object ID assigned. This identity will be used in a later step for authenticating REST API calls.

Don't see the VNet integration option?

Make sure you selected the WS1 tier or higher. Free and Consumption plans do not support VNet integration. Also confirm you chose the Standard type, not Consumption, when creating the Logic App.

If you missed the networking configuration during creation, you can still set it up now. Go to Settings > Networking > Outbound traffic > Virtual network integration > Add virtual network integration, select your VNet and logic-app-subnet, then click Connect.

Help me troubleshoot my Logic App VNet integration setup.

Build the workflow with an HTTP trigger

With the Logic App resource ready, you will create a stateful workflow that listens for HTTP POST requests containing firewall rule parameters. The workflow will parse the incoming JSON and return a simple response confirming it received the request.

  • In the Logic App left menu, under Workflows, select Workflows.
  • Click Add.
  • Enter a workflow name such as firewall-rule-creator.
  • For State type, select Stateful.
  • Click Create.
  • Click on the new workflow name to open it.
  • In the workflow left menu, under Tools, select Designer.

The designer canvas opens with an Add a trigger prompt. You will add the HTTP Request trigger.

  • Click Add a trigger.
  • In the search box, type request and select the built-in trigger named When a HTTP request is received.
  • In the trigger's information pane, click Use sample payload to generate schema.
  • Paste the following sample JSON payload into the text box:
{
    "ruleName": "AllowDNS",
    "sourceAddress": "10.0.0.0/24",
    "destinationAddress": "8.8.8.8",
    "destinationPort": "53",
    "protocol": "UDP",
    "action": "Allow"
}

What does this schema do?

The sample payload defines the shape of data the trigger expects. Azure Logic Apps generates a JSON schema from it, which creates tokens you can reference in later actions. Each field maps to a parameter your firewall rule automation needs: the rule's name, source/destination addressing, port, protocol, and whether to allow or deny.

  • Click Done to generate the schema.

Now add a Response action to confirm the workflow received the request.

  • Below the trigger on the designer canvas, click the + icon and select Add an action.
  • In the search box, type response and select the Response action (under Request).
  • Set the Status Code to 200.
  • In the Body field, type Rule request received.
  • On the designer toolbar, click Save.

After saving for the first time, Logic Apps generates an endpoint URL for the trigger. You will see this URL in the trigger's HTTP URL field on the Parameters tab.

Trigger not showing an HTTP URL?

The endpoint URL only appears after you save the workflow for the first time. Click Save on the designer toolbar, then click the trigger to reopen its information pane and check the Parameters tab.

Help me find the HTTP URL for my Logic App trigger.

Test the workflow with a sample request

The workflow is built, but you have not proven it works yet. You will use the Azure portal's built-in test feature to send a POST request and confirm the workflow runs successfully.

  • In the workflow left menu, select Overview.
  • Click Run Trigger > Run with payload.
  • Paste the following test payload into the body field:
{
    "ruleName": "AllowDNS",
    "sourceAddress": "10.0.0.0/24",
    "destinationAddress": "8.8.8.8",
    "destinationPort": "53",
    "protocol": "UDP",
    "action": "Allow"
}
  • Click Run.

Before you check the result, what do you expect the Run History to show? Think about the trigger and action you configured.

  • In the workflow left menu, select Overview and look at the Run History section at the bottom.
  • Click on the most recent run to open its details.

The run status shows Succeeded. Expanding the Response action shows a 200 status code and the body text Rule request received. Your workflow is live and processing requests.

Run History shows Failed?

Click on the failed run and expand each action to find the error. Common causes include a malformed JSON payload (check for missing commas or quotes) or a typo in the Response body field.

Help me debug my failed Logic App workflow run.

Your Logic App is live and responding to requests over a public endpoint. In the next step, you will lock it down with a private endpoint so only services inside your VNet can reach it.

Lock Down the Logic App with a Private Endpoint

In the previous step, you confirmed that your Logic App workflow receives HTTP requests and returns a response. The workflow URL is publicly accessible right now.

Firewall rule automation is a security-sensitive operation. Exposing the trigger endpoint to the public internet means anyone could potentially submit rule changes. In this step, you will restrict inbound traffic so only services inside your VNet can reach the Logic App.

In this step, get ready to:
  • Create a private endpoint for the Logic App in the private-endpoint-subnet.
  • Disable public network access to block all external inbound traffic.
  • Confirm that calling the public URL now returns 403 Forbidden.
Create a private endpoint for the Logic App

A private endpoint assigns a private IP address from your VNet to the Logic App. Traffic destined for the Logic App resolves to that internal IP instead of a public address.

  • Return to your Logic App resource in the Azure portal.
  • On the left menu, under Settings, select Networking.
  • In the Inbound traffic section, click the link next to Private endpoints.
  • On the Private Endpoint connections page, click Add and then select Express.
  • In the Name field, enter a name for the private endpoint (for example, logicapp-pe).
  • For the subnet, select private-endpoint-subnet.
  • For the Private DNS Zone, select the privatelink.azurewebsites.net zone you created in Step 1.
  • Click OK to create the private endpoint.

Wait for the deployment to complete. This typically takes 30-60 seconds.

  • Once the deployment finishes, confirm the private endpoint status shows Approved in the Private Endpoint connections list.

What does the private endpoint do?

The private endpoint creates a network interface in your private-endpoint-subnet and registers a DNS record in the privatelink.azurewebsites.net zone. Any service inside your VNet that resolves the Logic App hostname will get the private IP instead of the public one.

Services outside the VNet still resolve to the public address, but traffic is now blocked once you disable public access.

Don't see the private-endpoint-subnet?

If the subnet does not appear in the dropdown, check that it is not already occupied by another delegated service. The private endpoint subnet must not have a delegation like Microsoft.Web/serverFarms assigned to it. Return to your VNet resource and confirm the private-endpoint-subnet has no delegations.

help me troubleshoot why my subnet doesn't appear when creating a private endpoint

Disable public network access

The private endpoint is now active, but the public endpoint still accepts requests too. To enforce that only the private path works, you need to explicitly disable public inbound traffic.

  • Return to the Networking page of your Logic App (under Settings).
  • In the Inbound traffic section, find Public network access.
  • Set it to Disabled.
  • Click Save to apply the change.

Why disable public access separately?

Private endpoints and public access can coexist on an Azure resource. Creating a private endpoint alone does not automatically block the public path. Disabling public network access ensures that the only way to reach the Logic App is through the VNet.

Confirm the public URL is blocked

Your Logic App trigger URL worked fine in Step 2 when you tested it. Now that public access is disabled, calling that same URL from outside the VNet should fail.

Before you test, what do you think will happen when you call the workflow URL from the portal or your browser now?

  • Navigate to your Logic App workflow in the portal.
  • Go to the workflow Overview page and copy the Workflow URL.
  • Open a new browser tab and paste the URL. Press Enter.

You will see a 403 Forbidden error. This confirms the lockdown works. The Logic App is now unreachable from the public internet.

  • Return to the Networking blade and verify the following:
  • Under Private endpoints, the endpoint shows status Approved.
  • Under Public network access, the value shows Disabled.

Why is 403 Forbidden the expected result?

When public network access is disabled, any request arriving at the Logic App's public URL is rejected with 403 Forbidden. Only traffic routed through the private endpoint (from within the VNet) can reach the workflow.

In the next steps, you will deploy an App Service with VNet integration that routes its traffic through the VNet, allowing it to reach this private endpoint.

Still getting a successful response instead of 403?

Make sure you saved the public network access change. The Disabled setting requires clicking Save to take effect.

Also clear your browser cache or try an incognito window. Some browsers cache successful responses for a short period.

help me troubleshoot why my Logic App is still publicly accessible after setting up a private endpoint

Your Logic App is now invisible to the outside world. The only way to trigger it is from inside the VNet. Next, you will grant the Logic App permission to modify your Firewall Policy so the workflow can actually create rules when it receives a request.

Grant the Logic App Permission to Modify Firewall Rules

In the previous step, you locked down the Logic App with a private endpoint so only VNet traffic can reach it. Now it is time to give the Logic App the ability to actually modify firewall rules on your Azure Firewall Policy.

The Logic App's managed identity currently has zero permissions in Azure. If it tried to call the Firewall REST API right now, it would receive a 403 Forbidden response. You need to grant it the right RBAC role, then build out the workflow logic that constructs and sends the API call.

In this step, get ready to:
  • Assign the Network Contributor role to the Logic App's managed identity.
  • Build the HTTP action that calls the Azure Firewall REST API.
  • Add conditional logic to handle success and failure responses.
Assign Network Contributor to the managed identity

The Logic App needs write access to the Firewall Policy in order to create rule collection groups via the REST API. The Network Contributor built-in role covers all required permissions for firewall policy management.

  • In the Azure portal, navigate to your Firewall Policy resource.
  • In the left sidebar, click Access control (IAM).
  • Click Add, then select Add role assignment.
  • On the Role tab, search for Network Contributor and select it.
  • Click Next.
  • On the Members tab, select Managed identity.
  • Click Select members.
  • In the panel that opens, set the Managed identity dropdown to Logic app (Standard).
  • Select your Logic App from the list.
  • Click Select.
  • Click Review + assign twice to complete the assignment.

After a few seconds, the role assignment appears in the Role assignments tab of the Firewall Policy's IAM blade.

Why Network Contributor?

Network Contributor is the simplest built-in role that covers all required permissions for creating and managing Azure Firewall rule collection groups. A custom role with just the Microsoft.Network/firewallPolicies/ruleCollectionGroups/write permission would be more least-privilege, but adds complexity for a learning project.

Don't see your Logic App in the member list?

Make sure you set the Managed identity dropdown to Logic app (Standard) instead of leaving it on the default. Also confirm you enabled the system-assigned managed identity on the Logic App in a previous step.

Help me find my Logic App's managed identity in the role assignment panel.

Configure the HTTP action to call the Firewall REST API

With permissions in place, you can now build the action that sends the PUT request to create a rule collection group on your Firewall Policy.

  • Return to your Logic App workflow in the designer.
  • Select the existing Response action, click the ellipsis (...), and choose Delete.
  • Click the + below the HTTP Request trigger, then select Add an action.
  • In the search box, type HTTP and select the HTTP built-in action (not HTTP + Swagger).
  • Set Method to PUT.
  • In the URI field, paste the following Azure Firewall REST API endpoint:
https://management.azure.com/subscriptions/[[SUBSCRIPTION_ID="your-subscription-id"]]/resourceGroups/[[RESOURCE_GROUP="your-resource-group-name"]]/providers/Microsoft.Network/firewallPolicies/[[FIREWALL_POLICY="your-firewall-policy-name"]]/ruleCollectionGroups/@{triggerBody()?['ruleName']}?api-version=2025-05-01

What does this URI do?

This is the Azure Firewall REST API endpoint for creating or updating a rule collection group. The @{triggerBody()?['ruleName']} expression dynamically inserts the rule name from the incoming request as the collection group name.

You can find your subscription ID, resource group name, and firewall policy name on each resource's Overview page in the Azure portal.

  • Under Headers, add a key-value pair with key Content-Type and value application/json.
  • In the Body field, paste the following JSON payload:
{
  "properties": {
    "priority": 100,
    "ruleCollections": [
      {
        "ruleCollectionType": "FirewallPolicyFilterRuleCollection",
        "name": "@{triggerBody()?['ruleName']}",
        "priority": 100,
        "action": {
          "type": "@{triggerBody()?['action']}"
        },
        "rules": [
          {
            "ruleType": "NetworkRule",
            "name": "@{triggerBody()?['ruleName']}",
            "sourceAddresses": [
              "@{triggerBody()?['sourceAddress']}"
            ],
            "destinationAddresses": [
              "@{triggerBody()?['destinationAddress']}"
            ],
            "destinationPorts": [
              "@{triggerBody()?['destinationPort']}"
            ],
            "ipProtocols": [
              "@{triggerBody()?['protocol']}"
            ]
          }
        ]
      }
    ]
  }
}

What does this payload do?

This JSON creates a network rule collection group on your Firewall Policy. Each @{triggerBody()?['...']} expression pulls a value from the original HTTP request that triggered the workflow.

  • The ruleCollectionType is set to FirewallPolicyFilterRuleCollection, which supports Allow and Deny actions.
  • The ruleType is NetworkRule, which filters traffic by IP address, port, and protocol.
  • From the Advanced parameters dropdown, select Authentication.
  • Set Authentication type to Managed identity.
  • Set Managed identity to System-assigned managed identity.
  • Set Audience to https://management.azure.com/.

Why managed identity authentication?

Instead of storing credentials in your workflow, the Logic App presents its managed identity token to the Azure Resource Manager API. The https://management.azure.com/ audience tells Azure which API the token is for. The Network Contributor role you assigned earlier is what authorizes the token to make changes.

Add conditional logic for success and failure

The Azure Firewall REST API returns 200 for updates and 201 for new creations. You need a Condition action that checks the HTTP response and sends an appropriate message back to the caller.

  • Click the + below the HTTP action, then select Add an action.
  • Search for Condition and select the Condition control action.
  • In the left Choose a value box, click the expression editor icon (fx).
  • Type the following expression and click Add:
outputs('HTTP')['statusCode']
  • Set the middle operator to is equal to.
  • In the right Choose a value box, type 200.
  • Click Add to add an Or row.
  • In the new row's left box, open the expression editor and enter the same expression:
outputs('HTTP')['statusCode']
  • Set the operator to is equal to and the right value to 201.

What does this condition check?

The expression outputs('HTTP')['statusCode'] retrieves the HTTP status code returned by the previous HTTP action. The condition evaluates to true if the API responded with either 200 (updated an existing resource) or 201 (created a new one).

  • In the True branch, click Add an action.
  • Search for Response and select the Response action.
  • Set Status Code to 200.
  • Set Body to Rule created successfully.
  • In the False branch, click Add an action and add another Response action.
  • Set Status Code to 500.
  • For the Body, click the expression editor icon and enter:
body('HTTP')

Why include the HTTP body in the error response?

When the API call fails, the response body from Azure contains the specific error message explaining what went wrong. Passing it back to the caller makes debugging much easier than a generic failure message.

Before you save, do you expect the designer to show any errors? Think about whether all actions connect correctly and whether any required fields are missing.

  • On the designer toolbar, click Save.

The workflow saves without errors. You cannot test it end-to-end yet because the private endpoint blocks external calls, but the designer confirms the workflow structure is valid.

Seeing a save error in the designer?

Check that the HTTP action's URI does not contain any extra spaces or line breaks. Also verify the Body JSON is valid by ensuring every opening brace has a matching closing brace.

If the error mentions authentication, confirm you selected System-assigned managed identity (not User-assigned) and that the Audience field is exactly https://management.azure.com/.

Help me fix a save error in my Logic App workflow designer.

Your Logic App now has both the permission and the logic to create firewall rules. In the next step, you will deploy a web app that reaches through the VNet to trigger this private workflow.

Deploy the Web App and Trigger the Private Logic App

Your Logic App workflow is complete and secured behind a private endpoint. The problem now is that you have no way to reach it. Public calls return 403 Forbidden, which is exactly what you want for security.

You need a service that lives inside the VNet and can route its outbound traffic through the private network. An App Service with VNet integration does exactly this. In this step, you will deploy a web app with a simple form that sends firewall rule requests privately to the Logic App.

In this step, get ready to:
  • Create an App Service with VNet integration so it can reach private endpoints.
  • Deploy a web form that submits firewall rule parameters to the Logic App.
  • Submit a test rule and verify it appears in your Firewall Policy.
Create the App Service and configure VNet integration

The App Service needs VNet integration on the dedicated webapp-integration-subnet so its outbound traffic resolves the Logic App's private DNS hostname.

  • In the Azure portal, click + Create a resource.
  • Search for Web App and click Create.
  • Select your existing Resource Group and enter a unique name for the web app.
  • Set Publish to Code and choose a runtime stack (any stack works since you will use a static HTML file).
  • Set the Region to the same region as your other resources.
  • Under Pricing plan, select Basic B1. This is the minimum tier that supports VNet integration.
  • Click Review + create, then Create.
  • Wait for the deployment to complete, then click Go to resource.

Why Basic B1?

VNet integration requires Basic tier or higher. The Free and Shared tiers do not support connecting to a virtual network.

Now connect the App Service to your VNet so its outbound traffic flows through the private network.

  • In your App Service resource, select Settings > Networking in the left menu.
  • In the Outbound traffic configuration section, click the Not configured link next to Virtual network integration.
  • Click Add virtual network integration.
  • Select your Subscription, your Virtual Network, and choose webapp-integration-subnet as the Subnet.
  • Click Connect. The app restarts during integration.

The page refreshes to show the VNet connection details. You should see your VNet and subnet listed under the integration.

Why does the app restart?

Enabling VNet integration modifies the app's networking configuration. The restart applies the new routing path so outbound traffic flows through the integrated subnet.

Next, add an application setting to force all outbound traffic through the VNet.

  • In the left menu, select Settings > Environment variables.
  • Click + Add to create a new application setting.
  • Set Name to WEBSITE_VNET_ROUTE_ALL and Value to 1.
  • Click Apply, then click Confirm on the dialog that appears. The app restarts again.

What does WEBSITE_VNET_ROUTE_ALL do?

By default, App Service only routes private (RFC1918) traffic into the VNet. Setting this to 1 forces all outbound traffic through VNet integration. This ensures the App Service resolves and reaches the Logic App's private endpoint DNS name.

Don't see the Environment variables option?

In some portal versions this appears under Settings > Configuration > Application settings tab instead. Add the setting there with the same name and value.

Help me find where to add WEBSITE_VNET_ROUTE_ALL in my App Service

Deploy the web form via App Service Editor

You will create an index.html file directly in the App Service using the browser-based editor. This file contains a form that collects firewall rule parameters and POSTs them to your Logic App.

  • In the left menu of your App Service, scroll down to Development Tools and click App Service Editor (Preview).
  • Click Go to open the editor in a new browser tab.

If App Service Editor is not listed

Use Advanced Tools (Kudu) instead. Click Go, then navigate to Debug console > CMD. Browse to site/wwwroot and create the file there.

  • The editor opens to the WWWROOT folder. Right-click in the file explorer panel on the left and select New File.
  • Name the file index.html and press Enter.
  • Delete any existing content in hostingstart.html if present (this is the default placeholder page).
  • Open your new index.html file and paste the following sections into it in order. Start with the document head and page layout styles:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Firewall Rule Manager</title>
    <style>
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
            max-width: 600px;
            margin: 50px auto;
            padding: 20px;
            background: #f5f5f5;
        }
        h1 {
            color: #0078d4;
        }
        form {
            background: white;
            padding: 30px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        label {
            display: block;
            margin-top: 15px;
            font-weight: 600;
            color: #333;
        }
  • Continue pasting the form element and interaction styles directly below:
        input, select {
            width: 100%;
            padding: 10px;
            margin-top: 5px;
            border: 1px solid #ddd;
            border-radius: 4px;
            box-sizing: border-box;
            font-size: 14px;
        }
        button {
            margin-top: 25px;
            padding: 12px 24px;
            background: #0078d4;
            color: white;
            border: none;
            border-radius: 4px;
            font-size: 16px;
            cursor: pointer;
            width: 100%;
        }
        button:hover {
            background: #005a9e;
        }
  • Add the result display styles and close the head section:
        #result {
            margin-top: 20px;
            padding: 15px;
            border-radius: 4px;
            display: none;
        }
        .success {
            background: #dff6dd;
            color: #107c10;
            border: 1px solid #107c10;
        }
        .error {
            background: #fde7e9;
            color: #d13438;
            border: 1px solid #d13438;
        }
    </style>
</head>

What do these styles do?

The CSS centers the page content, styles the form as a white card with a shadow, and defines two result states: a green .success class and a red .error class. These will display feedback after submitting a rule.

  • Now paste the form HTML body with all the input fields:
<body>
    <h1>Firewall Rule Manager</h1>
    <form id="ruleForm">
        <label for="ruleName">Rule Name</label>
        <input type="text" id="ruleName" name="ruleName" placeholder="e.g., AllowDNS" required>

        <label for="sourceAddress">Source Address (CIDR)</label>
        <input type="text" id="sourceAddress" name="sourceAddress" placeholder="e.g., 10.0.0.0/24" required>

        <label for="destinationAddress">Destination Address</label>
        <input type="text" id="destinationAddress" name="destinationAddress" placeholder="e.g., 8.8.8.8" required>

        <label for="destinationPort">Destination Port</label>
        <input type="text" id="destinationPort" name="destinationPort" placeholder="e.g., 53" required>

        <label for="protocol">Protocol</label>
        <select id="protocol" name="protocol">
            <option value="TCP">TCP</option>
            <option value="UDP">UDP</option>
            <option value="Any">Any</option>
        </select>

        <label for="action">Action</label>
        <select id="action" name="action">
            <option value="Allow">Allow</option>
            <option value="Deny">Deny</option>
        </select>

        <button type="submit">Submit Rule</button>
    </form>

What does this form contain?

The form collects six parameters that match the Logic App's trigger schema: ruleName, sourceAddress, destinationAddress, destinationPort, protocol (dropdown: TCP, UDP, Any), and action (dropdown: Allow, Deny).

  • Paste the JavaScript that handles form submission. This section collects the form values and POSTs them to the Logic App URL:
    <div id="result"></div>

    <script>
        const LOGIC_APP_URL = "your-logic-app-workflow-url-here";

        document.getElementById("ruleForm").addEventListener("submit", async function(e) {
            e.preventDefault();
            const resultDiv = document.getElementById("result");
            resultDiv.style.display = "none";

            const payload = {
                ruleName: document.getElementById("ruleName").value,
                sourceAddress: document.getElementById("sourceAddress").value,
                destinationAddress: document.getElementById("destinationAddress").value,
                destinationPort: document.getElementById("destinationPort").value,
                protocol: document.getElementById("protocol").value,
                action: document.getElementById("action").value
            };
  • Finally, paste the network request and response handling to complete the file:
            try {
                const response = await fetch(LOGIC_APP_URL, {
                    method: "POST",
                    headers: { "Content-Type": "application/json" },
                    body: JSON.stringify(payload)
                });

                const text = await response.text();
                resultDiv.style.display = "block";

                if (response.ok) {
                    resultDiv.className = "success";
                    resultDiv.textContent = "Rule created successfully: " + text;
                } else {
                    resultDiv.className = "error";
                    resultDiv.textContent = "Error (" + response.status + "): " + text;
                }
            } catch (err) {
                resultDiv.style.display = "block";
                resultDiv.className = "error";
                resultDiv.textContent = "Network error: " + err.message;
            }
        });
    </script>
</body>
</html>

What does this JavaScript do?

When the form is submitted, the script prevents the default page reload, collects all six field values into a JSON payload, and sends a POST request to the Logic App workflow URL. It then displays a green success message or a red error message based on the response.

The LOGIC_APP_URL constant is a placeholder you will replace with your actual Logic App workflow URL in the next substep.

  • Now replace the placeholder URL with your actual Logic App workflow URL. Switch back to the Azure portal tab with your Logic App.
  • In your Logic App, go to Workflows, select your workflow, and click the Overview tab.
  • Copy the Workflow URL (this includes the SAS token for authentication).
  • Return to the App Service Editor tab. In index.html, find the line that says "your-logic-app-workflow-url-here" and replace the placeholder text with your copied URL. Keep the surrounding quotes.

Keep your workflow URL private

The workflow URL contains a SAS token that grants access to trigger the Logic App. Do not share this URL publicly. In a production environment, you would use additional authentication mechanisms.

The App Service Editor auto-saves your changes. Your index.html file is now deployed.

✔️ Awesome, I've got everything!

Great. Confirm that your file contains all six sections you pasted: the document head, three style blocks, the form body, and the two JavaScript blocks. The editor shows auto-saved status in the top bar.

ⓧ I'd like to double check the full code

Scroll through your index.html in the editor and confirm these sections are present in order:

  • DOCTYPE declaration and opening <head> with page layout styles (body, h1, form, label).
  • Input, button, and button:hover styles.
  • Result, success, and error styles followed by closing </head>.
  • The <body> with the form containing all six fields and the Submit button.
  • The <div id="result"> element and the <script> block with LOGIC_APP_URL set to your actual workflow URL.
  • The try/catch block handling the fetch response, followed by closing </script></body></html>.

Editor not loading or showing errors?

Try clearing your browser cache and reloading. If the App Service Editor still fails, use the Kudu console (Advanced Tools > Go > Debug console > CMD) and navigate to site/wwwroot to create and edit files there.

Help me deploy my index.html using Kudu instead

Submit a test rule and verify it in the Firewall Policy

Everything is connected. The App Service routes its traffic through the VNet, which means it can resolve the Logic App's private endpoint hostname and deliver the POST request.

  • In the Azure portal, navigate back to your App Service resource.
  • On the Overview page, find the Default domain (it looks like your-app-name.azurewebsites.net).
  • Click the URL to open your web form in a new browser tab.

You should see the Firewall Rule Manager form with all six input fields and a blue Submit button.

  • Fill in the form with these test values:
  • Rule Name: AllowDNS
  • Source Address: 10.0.0.0/24
  • Destination Address: 8.8.8.8
  • Destination Port: 53
  • Protocol: select UDP from the dropdown.
  • Action: leave as Allow.

Before you click Submit, what do you think will happen? Will the request succeed or fail?

  • Click Submit Rule.

You should see a green success message. The App Service sent the request through the VNet, the Logic App received it via its private endpoint, and the Firewall REST API created the rule.

  • Verify the Logic App processed the request. Switch back to the Azure portal and navigate to your Logic App workflow.
  • Click Overview and check the Run History. You should see a new run with status Succeeded.
  • Now verify the rule landed in your Firewall Policy. Navigate to your Firewall Policy resource.
  • In the left menu, select Rule Collections.
  • You should see a new network rule collection group containing your AllowDNS rule with source 10.0.0.0/24, destination 8.8.8.8, port 53, protocol UDP, and action Allow.

Seeing a network error or timeout?

Make sure the WEBSITE_VNET_ROUTE_ALL setting is set to 1 and the app has restarted since you added it. Without this setting, the App Service may try to reach the Logic App over the public internet and receive a 403.

Also confirm that the Private DNS Zone (privatelink.azurewebsites.net) is linked to your VNet. Without this link, the App Service cannot resolve the Logic App's private endpoint hostname.

Help me troubleshoot my App Service reaching the private Logic App

Your private automation pipeline is fully operational. A web form submits a request through the VNet, the Logic App processes it with managed identity authentication, and the Firewall Policy gets updated. Next up, you will extend this with input validation in the Secret Mission.

Secret mission

Add Input Validation to Reject Bad Requests

Real-world automation needs guardrails. Without validation, someone could submit malformed CIDR ranges or duplicate rule names that would either fail silently or clutter the firewall policy. In this challenge, you will add Logic App conditions that reject invalid input before it ever reaches the Azure Firewall REST API.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project uses Azure resources that cost approximately $2-3/hr while running, so deleting them after you finish is strongly recommended.

Cost warning

Azure Firewall alone costs approximately $1.25/hr. If you leave all resources running overnight (8 hours), you could accumulate $16-24 in charges.

Delete your Resource Group as soon as you are finished to avoid unexpected bills.

Resources you used:

  • Azure Firewall (Standard tier) at approximately $1.25/hr.
  • Logic App Standard on WS1 plan at approximately $0.21/hr.
  • App Service on Basic B1 plan at approximately $0.08/hr.
  • Azure Firewall Policy with rule collection groups.
  • Virtual Network with four subnets, Private DNS Zone, and Private Endpoint (minimal cost).
  • Resource Group containing all of the above.

Keep everything running

No action needed. Choose this if you are still actively experimenting with your firewall automation pipeline.

  • Your resources will continue to cost approximately $2-3/hr while running.
  • You can continue submitting firewall rules through the web form and testing the private Logic App workflow.
  • Monitor your Azure billing dashboard to keep track of spend.

Pause - I'll come back to this later

There is no pause option for Azure Firewall. You can delete the Firewall resource individually to reduce costs to approximately $0.30/hr while preserving the Logic App and App Service configuration.

  • In the Azure portal, navigate to your Resource Group.
  • Select the Azure Firewall resource and click Delete.
  • Confirm the deletion when prompted.
  • Your Logic App, App Service, VNet, Private DNS Zone, and Firewall Policy remain intact.
  • When you return, you will need to redeploy the Azure Firewall and re-associate it with the Firewall Policy.

Delete - I don't want to use this again

Remove all project resources at once by deleting the Resource Group. This is the recommended option for a learning project.

  • In the Azure portal, search for Resource groups in the top search bar.
  • Select the Resource Group you created for this project.
  • Click Delete resource group at the top of the page.
  • Type the Resource Group name to confirm, then click Delete.
  • Deletion may take several minutes. All resources inside the group (Firewall, Logic App, App Service, VNet, Private DNS Zone, Private Endpoint, Firewall Policy, and RBAC assignments) are removed permanently.

How do I know everything is deleted?

After a few minutes, navigate back to Resource groups and confirm your project's Resource Group no longer appears in the list.

Nice Work!

Nice Work!

You just built a private, VNet-isolated automation pipeline that manages Azure Firewall rules without ever exposing the automation endpoint to the public internet.

You've learned how to:

  • Deploy an Azure Logic App Standard behind a private endpoint, locking it down so only resources inside your VNet can reach it.
  • Authenticate REST API calls using a system-assigned managed identity with the Network Contributor role, eliminating the need to store credentials.
  • Connect an App Service with VNet integration to a private Logic App, routing traffic through the virtual network to create firewall rules via a web form.
  • (Secret Mission) Added input validation to the Logic App workflow that rejects invalid CIDR ranges and prevents duplicate rule names.

Ready to quiz yourself?