Azure DevOps Pipeline deployments to Azure App Services with Access Restrictions
Tags: azuredevops, appservices, azureapp, azure
Published: 2023-11-26 21:49:44

---
title: Azure DevOps Pipeline deployments to Azure App Services with Access Restrictions
published: true
date: 2023-08-22 14:33:14 UTC
tags: AzureDevOps,appservices,azureapp,azure
canonical_url: https://rios.engineer/azure-devops-pipeline-deployments-to-azure-app-services-with-access-restrictions/
---

## Intro ![📖](https://s.w.org/images/core/emoji/14.0.0/72x72/1f4d6.png)

When you enable Access Restrictions onto your Azure App Services (such as an Azure Function or Web App) you cut off any inbound traffic from the internet. This can pose an extra challenge when needing to deploy code via pipelines as you now have no inbound public access to the [SCM](https://learn.microsoft.com/en-us/azure/app-service/resources-kudu) (source control manager aka Kudo service).

This guide will go through how to deploy with these restrictions in place via Microsoft hosted Azure DevOps agents.

## Azure DevOps Agents ![🚀](https://s.w.org/images/core/emoji/14.0.0/72x72/1f680.png)

Firstly comes the challenge of continuous deployment with these restrictions in place. Azure DevOps Agents hosted by Microsoft state:

![](https://rios.engineer/wp-content/uploads/2023/08/ado_agent_note-1024x164.png)

Meaning if we try to deploy to the app service now we have these restrictions we’ll likely get a 401 unauthorized error, as we’ve disabled public access to the app service (both the frontend and backend-[SCM](https://learn.microsoft.com/en-us/azure/app-service/resources-kudu) where you’d typically deploy code to).

There’s typically two main options to get around this:

- Deploy a VM and install an ADO self-hosted agent on the vNet of the app so it’s coming from an ‘internal’ address rather than public (via Private Endpoint vNet integration) or;
- Create tasks in your pipeline to allow the public IP of the MS agent on the app firewall dynamically during runtime 

The blog will focus on the latter as Microsoft have blogged on the first solution [here](https://techcommunity.microsoft.com/t5/apps-on-azure-blog/deploy-app-service-with-private-endpoint-enabled-via-azure/ba-p/3789370) if you’re interested.

_ **Note:** if you’re happy to use the service tag ‘ **AzureCloud** ‘ on your sites access rules, this will be sufficient enough to allow public ADO agents access without the need for the tasks below._  
  
_ **Note2:** There is a [weekly JSON file that posts Azure IP ranges](https://www.microsoft.com/en-us/download/details.aspx?id=56519), of which you could extrapolate the ADO IP space for your region in an automated solution instead_. This isn’t covered in this blog post however.

## The solution ![🛠](https://s.w.org/images/core/emoji/14.0.0/72x72/1f6e0.png)

As part of my deployment tasks I dynamically grab the ADO agent IP during runtime, store this into a variable, add it onto the apps access rules, let deployment run through & then remove the rule after. All via the pipeline deployment so it’s fully automated whilst maintaining our security posture.

**Prerequisites:**

- Allow public access: on 
- Unmatched rule action to Deny (super important)

This keeps the app service unavailable from public access due to the Deny rule match. Additionally we will have a ‘deny all’ rule to block all traffic.

(N_ote: the Main site & Advanced tool site can have different rule matches, to avoid this you can enable ‘Use main site rules’_ to consolidate under one site rule set. Below is just an example screenshot for reference):

![](https://rios.engineer/wp-content/uploads/2023/08/scm-access-restrictions-1024x648.png)

This will allow the CLI to dynamically add the ADO agent IP to the rule as a higher priority allowing the agent access for deployment.

### ADO Tasks ![🏗](https://s.w.org/images/core/emoji/14.0.0/72x72/1f3d7.png)

Now for the tasks themselves.

#### Grab the agent IP & add it to a pipeline task variable:

Using the website http://ipinfo.io/ip we are able to get the public IP address at the runtime and set it into a pipeline variable called ‘address’ for use later in the pipeline tasks.

<svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewbox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewbox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg>
```
          - task: Bash@3
             name: ADOAgentIP
             inputs:
              targetType: 'inline'
              script: |
                ipaddress=$(curl -s http://ipinfo.io/ip)
                echo Azure DevOps Agent IP: $ipaddress
                echo "##vso[task.setvariable variable=address;isOutput=true;]$ipaddress"
```
YAML

#### Add the rule to the app service:

Using [az webapp](https://learn.microsoft.com/en-us/cli/azure/webapp?view=azure-cli-latest) CLI we’re able to add the IP to the sites access rule restrictions at a priority level higher than the ‘Deny all’ rule seen from earlier in the post. Using the task name from earlier ‘ADOAgentIP’ and the pipeline variable ‘address’ we can create a dynamic variable to add the IP address $(ADOAgentIP.address) in the CLI.

<svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewbox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewbox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg>
```
           - task: AzureCLI@2
             displayName: Add Azure DevOps Agent IP to allow list
             inputs:
               azureSubscription: $(azureServiceConnection)
               scriptType: 'bash'
               scriptLocation: 'inlineScript'
               inlineScript: |
                az account set --subscription $(prodSub)
                az webapp config access-restriction add -g example-rg -n example-func-001 --rule-name ado-agent --action Allow --ip-address "$(ADOAgentIP.address)" --priority 100
```
YAML

#### Removing the rule after your deployment steps:

Lastly, using the CLI once more, we can remove our rule using the rule name. In my example ‘ado-agent’.

<svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewbox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewbox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg>
```
           - task: AzureCLI@2
             displayName: Remove Azure DevOps Agent IP
             inputs:
               azureSubscription: $(azureServiceConnection)
               scriptType: 'bash'
               scriptLocation: 'inlineScript'
               inlineScript: |
                az webapp config access-restriction remove -g example-rg -n example-func-001 --rule-name ado-agent
```
YAML

## Conclusion ![🏁](https://s.w.org/images/core/emoji/14.0.0/72x72/1f3c1.png)

That’s it! Quite a simple solution which works really nicely as a starting point. The tasks above are mostly an example, and could be improved on in your own workflow – such as making sure you always remove the IP even if the app service deployment fails. However, this is a good starting point to build out from.

I hope others find this useful.

The post [Azure DevOps Pipeline deployments to Azure App Services with Access Restrictions](https://rios.engineer/azure-devops-pipeline-deployments-to-azure-app-services-with-access-restrictions/) appeared first on [Rios Engineer](https://rios.engineer).