Deploying a static website to Azure Storage using Azure DevOps

Matthew Leak
5 min readMar 17, 2019

I had a recent requirement to host a static website for a Government project in MS Azure. There was also an additional requirement that all testing, building and deployment should be automated using CI/CD in Azure DevOps. I’m fairly new to the world of Azure after having used AWS for almost everything over the past 5+ years and so I wanted to use this as an outlet to document the process.

I decided to use Azure Storage over Azure App Service (S3 and Elastic Beanstalk being the respective AWS equivalents) as there were only a few static assets to host, such as an index.html file, some css and a couple of images. The infrastructure that comes out of the box with any PaaS (Azure App Service / Beanstalk) seemed pretty unnecessary here as I like to reduce my cost footprint wherever possible. (even when cost isn’t necessarily an issue …)

Create new Resource Group

As with any new project in Azure we want to create a Resource Group. This will allow us to logically group resources for this project -

  1. Login in to
  2. From the sidebar, click “Resource Groups”
  3. Click “Add”
  4. Select your project/resource details and then click “Create”

Create new Storage Account

The next step is to create a storage account where our static assets will be stored and used for our static website -

  1. From the sidebar, click “Storage accounts”
  2. Click “Add”
  3. Select the Subscription/Resource Group you created above. Give your storage account a name and select the region you want the assets to be stored in.
  4. Proceed to select any advanced options / tags if required, otherwise click “Review + Create”
  5. Select your new storage account and click “Static website” under “Settings”.
  6. Enable static website hosting and provide a name for the index document (index.html)

While in the Storage Account, navigate to “Access Keys” and take a note of “Key #1” as we’ll need this later.

Create the Build Pipeline

Here we begin to build our pipelines. Navigate to and if required click “Create Project”. Give it a name, set your preferred visibility option and then click “Create”.

With our DevOps project created, it’s time to start building the Build pipeline which will produce a .zip artefact that will be used by the Release pipeline that will be created in the next section.

For this article, we’ll use the visual designer to build but I’ll provide the YAML config at the end of this article.

  1. Navigate to Pipelines > Builds
  2. Click “New pipeline”
  3. Click “Use the visual designer”
  4. Select the source and branch of your repository and then click “Continue” and then on the next page, click “Start with an Empty job”
  5. Change the Agent pool to your preferred choice (Windows/Linux) and click “+” next to “Agent job 1” (I use hosted ubuntu)
  6. Add the tasks relative to your project that will produce a build (npm install, gulp build, etc.) and once you’ve got your pipeline producing a build we need to add 2 more steps to produce the artefacts
  7. Add a new task to the pipeline and select “Copy Files” from the tasks list. Configure this task so that the “Source Folder” reads from your build directory ($(Build.SourcesDirectory)/dist in my case) and set the “Target Folder” to $(Build.ArtifactStagingDirectory)
  8. Add a new task to the pipeline and select “Archive Files” from the tasks list. Configure this task so the target is your build directory and set the output to: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
  9. Add a final task to publish the build artefact. Add a new task to the pipeline and select “Publish Build Artifacts”. By default, the target to be published will be set to $(Build.ArtifactStagingDirectory)

Create the Release Pipeline

With the build pipeline complete and a zip archive of our build directory being produced, we need to release it to Azure Storage. To accomplish the upload, we’ll use the Azure CLI in the release pipeline as that’s what I’ve had the best results with.

  1. From the left sidebar, select Pipelines > Releases
  2. Click “New Pipeline”
  3. Select “Start with an empty job”
  4. Rename the stage to whatever you like and click Save
  5. Click “Add an artifact” and select your project and then build pipeline you just created and make sure that the default version is set to “Latest”
  6. Click on the lightning bolt above where you selected your artifact and enable continuous deployments so that whenever a build pipeline completes, it will start your release pipeline automatically.
  7. Under your stage name, click “1 job, 0 task”
  8. Click on “Agent job” and change the agent pool to your preferred choice (Windows/Linux) and click Save.
  9. Next to the agent job task, click “+” to add a new task to the release pipeline and select the “Extract Files” task.
  10. Configure the task so the “Archive file patterns” match what we’ve produced in the build pipeline **/$(Build.BuildId).zipand set the destination folder to $(Build.DefaultWorkingDirectory)/$(Build.BuildId)
  11. Add a new task to the release pipeline and select “Azure CLI” from the list. Set the “Script Location” to “Inline” and input the following script:
    az storage blob upload-batch — account-name STORAGE_ACCOUNT_NAME account-key STORAGE_ACCOUNT_KEY — destination ‘$web’ — source ./
  12. Replacing “STORAGE_ACCOUNT_NAME” with the name of your storage account and “STORAGE_ACCOUNT_KEY” with “Key #1” that you made a copy of when you created the new storage account in the previous section.
  13. Set the working directory to $(System.DefaultWorkingDirectory)/$(Build.BuildId)/dist
  14. Save the pipeline and create a new release to manually trigger the pipeline. Once complete, you can visit the URL of your static site and see your deployment in all of it’s glory. The static website URL was given to you when you enabled the static website feature in your storage account, to obtain this URL again just navigate back to, select your storage account > static website.

You’ll notice that in the CLI script above, our destination is set to $web which is the storage blob that Azure automatically creates when you enable the static website feature in a storage account. Any object inside of this blob will be publicly accessible via the static website URL.

We would normally take advantage of pipeline variables and reference those in our CLI scripts instead of adding sensitive keys to the tasks directly. I felt they were out of scope of this article and are a self-explanatory topic if you’ve ever used any other CI/CD tool.


A full reference to the Azure DevOps YAML schema can be found here along with a catalog of tasks that also provide YAML snippets.

Build Pipeline:

trigger:- masterpool:vmImage: ‘Ubuntu-16.04’steps:- task: NodeTool@0inputs:versionSpec: ‘8.x’displayName: ‘Install Node.js’- script: npm installdisplayName: ‘npm install’- script: npm run builddisplayName: ‘npm run build’- task: ArchiveFiles@2inputs:rootFolderOrFile: ‘$(Build.SourcesDirectory)/dist’archiveType: ‘zip’archiveFile: ‘$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip’replaceExistingArchive: true- task: PublishBuildArtifacts@1

If this article has been useful to you, be sure to leave lots of claps! (you can leave up to 50!)



Matthew Leak

UK-based Technology Consultant working in London and Manchester.