Dynamically setting IP to deploy assets to an Azure storage account behind a firewall from DevOps agents

Published on November 01, 2019

Tagged: #devops #azure #frontend-development

Follow me on twitter for more posts like this

If you try to deploy a static React app to an Azure static site on a storage account that’s behind a firewall you need to allow all the IPs that will be connecting to the storage.

The problem is that the range of possible IPs the devops agents use is huge and changes regularly.

The list of IPs used by Azure devops agents is published by Microsoft as an XML file and it changes every week!

A colleague of mine had a brilliant idea of dynamically opening the firewall on the storage account based on the IP of the current build agent each deploy.

It took me a few hours to figure out how to do this dynamically and I hope it might save you some time…

Scroll to the end for the full yaml script 👇

Task1: Open firewall and deploy the client

I used an azure cli task to avoid having to set the azure client up.

I use hipinfo.ip to get my current external IP. I pipe it into jq and use the -r (raw) flag to get the .ip property from the response. I set this to an environment variable for the current shell. This can be reused in the current task.

The ##vso line sets the IP into a variable that can be used later in the job to close the firewall again.

I just use the Azure cli client to open the firewall and send the files. I found I had to add a tiny sleep in between the commands to ensure the firewall was open.

Note that for all of these pipeline commands you need to make sure the ’$’ is used correctly. You must escape $ for passing to the cli tool, you need to reference pipeline variables with $(xxxxx) and environment variables with $xxxxx.

Task2: Close the firewall

We need to make sure that the firewall is closed each time we run the pipeline even if previous steps fail. This is really important for network level application security. So we set this task condition to run always().

Then we use the Azure cli again to close the firewall. Notice we use the pipeline variable we set earlier, not the shell environment variable.

  - task: AzureCLI@2
      displayName: Open firewall and deploy the client
      inputs:
        azureSubscripti(azureSubscription)'
        scriptType: 'bash'
        scriptLocation: 'inlineScript'
        inlineScript: |
          export IPADDR=$(curl -s hipinfo.io/json | jq -r '.ip')
          echo "Opening firewall: $IPADDR"
          echo "##vso[task.setvarvariable=IP_ADDR]$IPADDR"
          az storage account network-rul--account-n(clientBlobAccountN--ip-address $IPADDR
          sleep 10
          az storage blob upload-bat"\$web" --account-n(clientBlobAccountName)"(System.DefaultWorkingDirecunzip/$(Build.BuildId)/client/build"
  - task: AzureCLI@2
      displayName: Close firewall
      condition: always()
      inputs:
        azureSubscripti(azureSubscription)'
        scriptType: 'bash'
        scriptLocation: 'inlineScript'
        inlineScript: |
          echo "Removing $(IP_ADDR)"
          az storage account network-rule remove --account-name "$(clientBlobAccountName)" --ip-address "$(IP_ADDR)"
Darragh ORiordan

Hi! I'm Darragh ORiordan.

I live and work in Sydney, Australia building supporting happy teams that create high quality software for the web.

I also make tools for busy developers. Have you ever spent a week getting your dev environment just right?

My DevShell tooling will save you 30+ hours configuring your dev environment with all the best modern tools. Get it here

https://darraghoriordan.gumroad.com/l/devshell


Read more articles like this one...

List of article summaries

#devops

Extract user profile attributes from an Azure ADB2C tenant using the Microsoft Graph API

I had to retrieve a list of users from an Azure Active Directory B2C instance today. I thought I could just go through the Azure UI but that’s limited to short pages of data and limited attributes.

There is a CSV export provided on the UI but you won’t get the required identity objects in the csv output if you need a user’s signin email address.

I had to use the Microsoft Graph Api to get what I needed. This is a bit hacky but it does the trick!

#devops

Force restart your Azure App service site and host

Sometimes your Azure App service host will need to be restarted. You can do this but it’s hidden away in the Azure resource manager site. Here’s how to find it!

#devops

Scheduling a feature toggle using no-code with Azure Logic Apps

I use launch darkly to toggle features on an app. There is one third-party dependency that has regular scheduled maintenance and I need to toggle the feature on and off on schedule.

Launch Darkly has built in scheduling to handle this scenario but you have to be on the enterprise plan to use it. The enterprise plan is too expensive to upgrade to for scheduling alone so I needed to find a different way to automate this.

#frontend-development

Avoid rebuild of React App in every CI stage

If you have a react app you can use env vars like REACT_APP_MY_ENV_VAR in your application and React will automatically pull them in to your app when you build the production application.

This is very useful but if you have variables that change for each environment and your application build takes a long time, you might want to avoid building unnecessarily in CI. For example you might have a QA environment and a Staging environment that have different configuration.

We type-check our code on each build and that was taking 5 minutes+ to build each environment so we had to make it faster. We changed our app from using REACT_APP env vars to using a configuration file that we could quickly write to using CI.

Our CI system is Azure DevOops so the CI scripts here are specifically for Azure DevOps but they apply to most CI systems with small changes.

The real work happens in a Node.js script that would work anywhere.