Static web site on Azure with Azure DevOps and Bicep


Azure DevOps Infrastructure-as-code Azure Bicep 💪 Static Site


Table of contents:

In this sprint I’ll be setting up landing page (aka comming soon page or pre-launch page … static web site using Azure to be precise). The purpose of the exercise is to learn Microsoft’s capabilities around this topic. By the way, few years ago I wrote similar post for AWS services ;-)

All code from this sprint is here

 1├── README.md
 2├── pipelines
 3│   └── azure-pipelines.yml
 4├── src
 5│   ├── cake.png
 6│   ├── error.html
 7│   └── index.html
 8└── templates
 9    ├── main.bicep
10    ├── modules
11    │   ├── cdn.bicep
12    │   ├── scripts
13    │   │   └── staticweb.sh
14    │   └── storage-with-static-web.bicep
15    └── parameters.dev.json

I have the following objectives for this sprint:

That being said, the following diagram represents the end goal of the sprint.

Static web site with Azure and Azure DevOps

Create (or use existing) Microsoft Account and register in Azure #

Create (or use existing) Microsoft account (you might have one if you’ve used outlook, skype, xbox or any other Microsoft services). Use this link to register in Azure.

Sign up with Azure DevOps, create an organization, setup a project #

Use this link to sign up with Azure DevOps using Microsoft account that was used for Azure registration. Azure DevOps is available in the following regions. Consider to use the closest region to the users (if in doubt, check this or this tool to measure latency from you computer to the regions).

Azure DevOps Organization Azure DevOps Organization - available regions

Create a new project, make sure to use git as its version control. The rest can be left default or adjusted accordingly (up to you). While project’s visibility is self explanatory, it worth to visit this page to learn more about available process templates in Azure DevOps. See my example:

Azure DevOps - new project

Register domain and create a content for static web site #

I’ll leave domain registration (since there are multiple options to do it). And proceed with generation of static web site that consists of three files: index.html and error.html and fort.png (no CSS, no JS), let’s keep it super simple. All files are in /lab1-static-web-with-bicep/src folder.

Create and configure service principal #

Since I’d like to configure infrastructure-as-code (IaC), create and configure resources using Azure DevOps Pipelines, the first step is to generate Service Principal Name (SPN) that will be used by the pipelines to access Azure.

In my project settings (left bottom corner) I select “Service Connection” and configure new of type “Azure Resource Manager” using Service principal (automatic) and follow it’s steps. This will automatically create application in Azure AD tenant and store it’s credentials as service connection in my Azure DevOps organization’s project.

Azure DevOps - ARM service principal

… alternatively I can do this manually by generating SPN via the portal or using Azure CLI and pasting required parameters to the fields. For instance by going to Azure Portal > Azure Active Directory > Application Registration > Create new and follow the rest of the steps or by Azure CLI (ie there is builtin shell in Azure’s portal that can used to execute commands):

 1az ad sp create-for-rbac --name WeekendSprintsServicePrincipalName --role Contributor
 2Creating 'Contributor' role assignment under scope '/subscriptions/00000000-0000-0000-0000-000000000000'
 3The output includes credentials that you must protect. Be sure that you do not include these credentials in your code or check the credentials into your source control. For more information, see https://aka.ms/azadsp-cli
 4{
 5  "appId": "00000000-0000-0000-0000-000000000000",
 6  "displayName": "WeekendSprintsServicePrincipalName",
 7  "name": "00000000-0000-0000-0000-000000000000",
 8  "password": "<redacted>",
 9  "tenant": "00000000-0000-0000-0000-000000000000"
10}

This command generates new application in an AAD tenant (exactly as if we would do it via the portal as explained just before) and the output above is the credentials to use the SPN in service connection.

I am going to use Contributor role for my SPN, but later I could consider to change it’s scope, use other builtin roles or custom role with limited permissions to follow least privilege principle.

To provision resources in Azure I’ll use bicep 💪 template which is in templates folder. The template also calls deployment script (Azure CLI) that enables static web site on the storage account (since it’s not possible to define this param via bicep or ARM directly).

Bicep in Azure DevOps #

Now that we have everythin let’s put it in a way of unattended run (aka pipeline’s agent will do this for us).

 1trigger:
 2- main
 3
 4pool:
 5  vmImage: ubuntu-latest
 6
 7stages: 
 8  - stage: Validate
 9    jobs:
10      - job: Validate
11        variables:
12          location: 'westeurope'
13          
14        steps:
15        - task: AzureCLI@2
16          inputs:
17            azureSubscription: 'msdn'
18            scriptType: 'bash'
19            scriptLocation: 'inlineScript'
20            inlineScript: |
21              az deployment sub what-if -l $(location) --template-file ./lab1-static-web-with-bicep/templates/main.bicep --parameters ./lab1-static-web-with-bicep/templates/parameters.dev.json
22              az deployment sub validate -l $(location) --template-file ./lab1-static-web-with-bicep/templates/main.bicep --parameters ./lab1-static-web-with-bicep/templates/parameters.dev.json              
23
24  - stage: Deploy_Infra
25    jobs:
26      - job: Deploy_Infra
27        variables:
28          location: 'westeurope'
29        steps:
30        - task: AzureCLI@2
31          inputs:
32            azureSubscription: 'msdn'
33            scriptType: 'bash'
34            scriptLocation: 'inlineScript'
35            inlineScript: |
36              # Create deployment
37              az deployment sub create -l $(location) --template-file ./lab1-static-web-with-bicep/templates/main.bicep --parameters ./lab1-static-web-with-bicep/templates/parameters.dev.json              
38
39  - stage: Deploy_App
40    jobs:
41      - job: Deploy_App
42        variables:
43          location: 'westeurope'
44        steps:
45        - task: AzureCLI@2
46          inputs:
47            azureSubscription: 'msdn'
48            scriptType: 'bash'
49            scriptLocation: 'inlineScript'
50            inlineScript: |
51              # Upload files to storage (web-site content) from ./lab1-static-web-with-bicep/src folder and purge CDN cached content
52
53              az storage blob upload-batch --account-name $STORAGE -s ./lab1-static-web-with-bicep/src -d '$web'
54              az cdn endpoint purge -g $RG -n $CDNENDPOINT --profile-name $CDNPROFILE --no-wait --content-paths '/'
55
56              # Output with hostName of the endpoint
57
58              echo "Static website URL is  => `az cdn endpoint list -g $RG --profile-name $CDNPROFILE | jq -r ".[].hostName"`"              

Please note that the SPN from above (Visual Studio Subscribtion) must also have a role or permission that allows role assignmnet (I’ve added User Access Administrator, but it can also be an Owner). This is due to the part of the deployment script that assigns Storage Contributor role in managed identity on the storage scope (since we need to do service-properties update). This can also be placed outside of the bicep and run by the pipeline just fine, but I wanted to keep IAC in bicep.

Azure DevOps - multistage pipeline

This is now running good and visiting the below link we should be able to see the content.

Azure DevOps pipelines - static web with bicep

That being said for now the process is the following:

Later we will introduce additional controls (i.e. linked work item from the backlog, IAC template scan, static code analysis etc..). That’s it ✌️

Clean up #

Let’s clean up things:

az group delete -n <YOUR-RG>

Resources #

comments powered by Disqus