Featured image of post Static web site on Azure with Azure DevOps and Bicep

Static web site on Azure with Azure DevOps and Bicep

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

├── README.md
├── pipelines
│   └── azure-pipelines.yml
├── src
│   ├── cake.png
│   ├── error.html
│   └── index.html
└── templates
    ├── main.bicep
    ├── modules
    │   ├── cdn.bicep
    │   ├── scripts
    │   │   └── staticweb.sh
    │   └── storage-with-static-web.bicep
    └── parameters.dev.json

I have the following objectives for this sprint:

  • Create (or use existing) Microsoft Account and register in Azure;
  • Sign up with Azure DevOps, create an organization, setup a project;
  • Register domain and create a content for static web site;
  • Create and configure Azure Resources;
  • Configure source control (git), create pipelines, create and configure Azure resources and, eventually, introduce change process;
  • Review final architecture and pricing, resources list.

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

az ad sp create-for-rbac --name WeekendSprintsServicePrincipalName --role Contributor
Creating 'Contributor' role assignment under scope '/subscriptions/00000000-0000-0000-0000-000000000000'
The 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
{
  "appId": "00000000-0000-0000-0000-000000000000",
  "displayName": "WeekendSprintsServicePrincipalName",
  "name": "00000000-0000-0000-0000-000000000000",
  "password": "<redacted>",
  "tenant": "00000000-0000-0000-0000-000000000000"
}

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

trigger:
- main

pool:
  vmImage: ubuntu-latest

stages: 
  - stage: Validate
    jobs:
      - job: Validate
        variables:
          location: 'westeurope'
          
        steps:
        - task: AzureCLI@2
          inputs:
            azureSubscription: 'msdn'
            scriptType: 'bash'
            scriptLocation: 'inlineScript'
            inlineScript: |
              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
              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              

  - stage: Deploy_Infra
    jobs:
      - job: Deploy_Infra
        variables:
          location: 'westeurope'
        steps:
        - task: AzureCLI@2
          inputs:
            azureSubscription: 'msdn'
            scriptType: 'bash'
            scriptLocation: 'inlineScript'
            inlineScript: |
              # Create deployment
              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              

  - stage: Deploy_App
    jobs:
      - job: Deploy_App
        variables:
          location: 'westeurope'
        steps:
        - task: AzureCLI@2
          inputs:
            azureSubscription: 'msdn'
            scriptType: 'bash'
            scriptLocation: 'inlineScript'
            inlineScript: |
              # Upload files to storage (web-site content) from ./lab1-static-web-with-bicep/src folder and purge CDN cached content

              az storage blob upload-batch --account-name $STORAGE -s ./lab1-static-web-with-bicep/src -d '$web'
              az cdn endpoint purge -g $RG -n $CDNENDPOINT --profile-name $CDNPROFILE --no-wait --content-paths '/'

              # Output with hostName of the endpoint

              echo "Static website URL is  => `az cdn endpoint list -g $RG --profile-name $CDNPROFILE | jq -r ".[].hostName"`"              
  • What-if - before deploying the bicep file, I can review the changes
  • Validate - helps me to validate template

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:

  • Developer works on change in a separate branch and propose PR to main branch
  • Merged PR to main triggers the pipeline with single step to run shell script that has all the logic

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
Built with Hugo
Theme Stack designed by Jimmy