Azure DevOps Pipeline deployments to Azure App Services with Access Restrictions

Photo of author

Dan Rios

4 min read

Intro

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 (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

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

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 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 if you’re interested.

Note: if you’re happy to use the service tag ‘Azure DevOps‘ 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, 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

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:

  • Public network access: Enabled from selected networks and IP addresses
  • 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.

(Note: 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):

Azure App Services access restrictions tab showing the options

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

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.

          - 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 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.

           - 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’. I’m also including the condition always() so that if the pipeline fails in any of the prior steps, we are removing the IP from the rule list to keep things tidy.

           - 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
              condition: always()
YAML

Conclusion

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.

8 thoughts on “Azure DevOps Pipeline deployments to Azure App Services with Access Restrictions”

  1. I found this article when I was looking for a solution to this issue and I must say it is well written. The explanations are excellent, they’re references to other materials and the scripts work! Thank you for this post.

    Reply
  2. This did set me on the path, so thank you very much. It appears that the configuration has changed since this article was written – I could only set the deny rule if public access was set to select virtual networks and IP addresses. If enable all networks was allowed, the rules were disabled.

    Reply

Leave a comment


Skip to content