A Cloud Guru Azure Resume Challenge - My Journey

Introduction:

Cloud Technologies and DevOps is more than just theory—it's about putting your knowledge into action. With my recent decision of moving away from role based certifications in Microsoft to testing my skills out with projects, I took on the A Cloud Guru Cloud Resume Challenge.

As Gwyneth Peña-Siguenza says - The goal of this challenge is not to sharpen your web development skills, it is all about Azure. This was a good opportunity for me to test what I knew about Microsoft Azure in a real world project.

Come along with me as I share my journey through the Cloud Resume Challenge on Microsoft Azure. I'll talk about what worked, what didn't, and how this challenge helped me become better at using Azure.

You can visit https://www.akashmasand.com to see the final result.

Overview of the Cloud Resume Challenge:

The Cloud Resume Challenge is a practical test designed to assess and showcase one's proficiency in cloud technologies. It’s not just about creating a resume website; it’s a hands-on journey that assesses your ability to design, implement, and deploy a real-world application using cloud services. The individual taking the challenge can choose either AWS, Azure or GCP as their preferred cloud platform. In my scenario, I decided to choose Microsoft Azure as my preferred cloud platform.

The original Cloud Resume Challenge can be found here.

My Approach:

My approach to the Cloud Resume Challenge diverged from the traditional path, opting for a streamlined strategy. I centered my efforts around a specific tutorial video by Gwyneth Peña-Siguenza and the linked resources provided in its caption. This unconventional approach aimed to simplify the challenge's complexity by leveraging a singular, comprehensive source, allowing for a more focused and efficient journey through the Azure services involved.

Implementation on Microsoft Azure:

Setting up the environment:

The following tools are needed for the challenge:

An Azure Account: An Azure account is needed to deploy the website and the Azure Functions. Microsoft credits are also available for eligible users.

Azure CLI: This is the CLI tool to interact with your Azure Account. You can find more details on the same here.

Code Editor: I basically used the Visual studio code editor for writing my code. I also installed some extensions like the Azure Function Extension with the same that made creating functions and deploying functions to Azure from the code editor itself very easy.

Azure Function Core Tools: This is basically needed to develop Azure Functions locally on our machines.

Github Repository: Since, this is an entirely new project, I created a brand new repository for the same.

Architecture Design:

The website's hosting is powered by Azure Storage, coupled with Azure CDN to ensure rapid and dependable content delivery. To track visitor activity, I harnessed the capabilities of Azure Functions and Cosmos DB, enabling an efficient visitor counting functionality.

The Front End:

I am not a web developer myself and since my intention with this project was to learn Microsoft Azure rather than improving my web development skills, I decided to use the starter files that were provided by A Cloud Guru where I learned that the frontend of the website is based on the Ceevee template.

The JavaScript code to display the counter data is as follows:

window.addEventListener('DOMContentLoaded', (event) =>{
    getVisitCount();
})

const functionApiUrl = "<function API URL>";
const functionApi = '';

const getVisitCount = () => {
    let count = 30;
    fetch(functionApiUrl).then(response => {
        return response.json()
    }).then(response => {
        console.log("Website called function API");
        count = response.count;
        document.getElementById("counter").innerText = count;
    }).catch(function(error){
        console.log(error);
    });
    return count;
}

This is basically useful to display the counter value at - and this page has been viewed <counter value> times :)

The Backend:

First of all, I created a CosmosDB with a database named AzureResume and a container named Counter. Within the items, I added the following code:

{
    "id":"1",
    "count": 0
}

Please note that saving this will add a bunch of metadata as well.

The next part of this was creating an Azure Function and binding to the CosmosDB for that function.

The function code is as follows:

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Microsoft.Azure.Functions.Worker;
using System.Data.Common;
using System.Configuration;
using System.Net.Http;
using Microsoft.Azure.Cosmos;
using System.Text;

namespace Company.Function
{
    public static class GetResumeCounter
    {
        [FunctionName("GetReumeCounter")]
        public static HttpResponseMessage Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            [CosmosDB(databaseName:"AzureResume", collectionName: "Counter", ConnectionStringSetting = "AzureResumeConnectionString", Id = "1", PartitionKey = "1")] Counter counter,
            [CosmosDB(databaseName:"AzureResume", collectionName: "Counter", ConnectionStringSetting = "AzureResumeConnectionString", Id = "1", PartitionKey = "1")] out Counter updatedCounter,
            ILogger log)

        {
            log.LogInformation("GetResumeCounter was triggered.");

            updatedCounter = counter;
            updatedCounter.Count += 1;

            var jsonToReturn = JsonConvert.SerializeObject(counter);

            return new HttpResponseMessage(System.Net.HttpStatusCode.OK)
            {
                Content = new StringContent(jsonToReturn, Encoding.UTF8, "application/json")
            };
        }
    }
}

This can be tested out locally using the Function core tools.

Deploying to Azure:

The deployment of Azure Functions is very straightforward, thanks to the Azure Functions extension in Azure. I created an Azure Function in the Advanced Mode initially and then deployed it using the extension itself.

The next step was deploying the frontend to an Azure Blob Storage. This too, becomes extremely straightforward when using the Azure Storage Extension within the Visual Studio Code. I just right clicked on the frontend folder and then selected Deploy to static website via Azure Storage.

After this, I basically enabled an Azure CDN for the storage account. This helped me add a custom domain to my website and also enable HTTPS for my website. Moreover, the custom domain will need to be added to the CORS settings else, the resume counter will not work.

Deployment Workflow:

For the Deployment workflow, I used two workflows - main.yml for the frontend deployment and backend.main.yml for the backend deployment. Please note that I already created an Azure Service Principal and then basically added the output JSON as a secret named AZURE_CREDENTIAL. The Github workflows I created are as follows:

# backend.main.yml
name: deploy_backend

on:
  push:
        branches: [main] 
        paths:
        - 'backend/**' 

env:
  AZURE_FUNCTIONAPP_NAME: 'GetResumeCounterAkash'   # set this to your function app name on Azure
  AZURE_FUNCTIONAPP_PACKAGE_PATH: 'backend'       # set this to the path to your function app project, defaults to the repository root
  DOTNET_VERSION: '6.0.x'                   # set this to the dotnet version to use (e.g. '2.1.x', '3.1.x', '5.0.x')

jobs:
  build-and-deploy:
    runs-on: windows-latest
    environment: dev
    steps:
    - name: 'Checkout GitHub Action'
      uses: actions/checkout@v3

    - name: 'Login with Azure CLI'
      uses: azure/login@v1
      with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}

    - name: Setup DotNet ${{ env.DOTNET_VERSION }} Environment
      uses: actions/setup-dotnet@v3
      with:
        dotnet-version: ${{ env.DOTNET_VERSION }}

    - name: 'Resolve Project Dependencies Using Dotnet'
      shell: pwsh
      run: |
        pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/api'
        dotnet build --configuration Release --output ./output
        popd

    - name: 'Run unit tests'
      shell: pwsh
      run: |
        pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/tests'
        dotnet build --configuration Release --output ./output
        popd

    - name: 'Run Azure Functions Action'
      uses: Azure/functions-action@v1
      id: fa
      with:
        app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }}
        package: '${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/api/output'
#main.yml

name: deploy_frontend
# Deploys when push is made from the frontend folder

on:
  push:
        branches: [main] 
        paths:
        - 'frontend/**' 

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - uses: azure/login@v1
      with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}

    - name: Upload to blob storage
      uses: azure/CLI@v1
      with:
        inlineScript: |
            az storage blob upload-batch --account-name azureresumeacgakash --auth-mode key -d '$web' -s frontend/ --overwrite
    - name: Purge CDN endpoint
      uses: azure/CLI@v1
      with:
        inlineScript: |
           az cdn endpoint purge --content-paths  "/*" --profile-name "azureresumeakash" --name "azureresumeakash" --resource-group "azureresume-rg"

  # Azure logout
    - name: logout
      run: |
            az logout
      if: always()

The Result

You can visit the website at akashmasand.com. You'll be directed to a webpage resembling the following layout: