# #CloudGuruChallenge: Your resume in Azure

Azure Cloud Resume Challenge

 https://acloudguru.com/blog/engineering/cloudguruchallenge-your-resume-in-azure

The final output </br>
 https://www.resume.elston.co.in/

GitHub Link :
https://github.com/ElstonMisquitta88/cloudguru-azure-resume

<p><i>The main purpose of taking the challenge was that I am new to Azure and wanted to learn more  and work on Azure services.
This became quite a learning experience.
Trying out the servies and deploying on a live environment was fun.
</i>
</p>

I have divided the work done into the following steps:

1. Create a Static Website. 
2. Custom Domain for the Website. 
3. Workflow in Github for the static website. 
4. Create a Cosmos DB for saving the website visitor count.
5. Create a Function to Fetch/Update the visitor count.
6. Create a KeyValult to save the Cosmos DB connection string.
7. Workflow in Github for the Function.
8. Create a simple Unit test for the Function




> #### (Step 1) Create a Static Website 

- Create a Repository in GitHub.
![01_GitHub.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1618073476109/pGGCFOpm9.png)
 
- Used a website template from  https://templateflip.com/

- Added the files to the GitHub Repository.

- Installed GitHub Desktop to create a local clone copy.

- In Visual Studio via the Source Control Option - Push any changes in the files to GitHub Repository.

![02_VisualStudioCode.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1618074220131/6zQuNpe88.png)

- Create a Resource Group on Azure Portal Via CLI

Prior to this step you need to install Azure CLI or use Via Cloud Shell in Azure Portal

![03_CloudShel.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1618079199593/NmKpkakO3.png)
In CMD type (this is to login into azure)

```
az login
``` 

You will be redirected to a web page for Azure subscription authentication.

```
az group create --location   --name
az group create -l westindia -n rg_cg_resume
              
``` 
 
- Next step is to create a Storage Account


```
az storage account create --name --resource-group
az storage account create --name cgresume --resource-group rg_cg_resume --kind StorageV2 --access-tier hot --location westindia --sku Standard_LRS
``` 

- In Azure portal navigate to the storage account which you have just created.
In that search for Static Website option is the left hand side blade.


![04.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1618081572499/vicdBoIiy.png)

Enable the static Website and add the name of the default page to be used.
Click on Save.

![05.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1618081877958/V04Q4RliV.png)

After saving the Primary endpoint will be generated. You can use this link to view your website.

> #### (Step 2) Custom Domain for the Website 

- To assign a custom Domain we will need to create first a CDN profile.

![06_CDN.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1618122237309/ySgb68OZB.png)

- After creating the CDN Profile you will need to create a Endpoint which will point to the storage account holding the static website.


![07_CDN_Endpoint.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1618122422141/z0CGz1Gwa.png)


- Give a name for the endpoint.

- Origin Type - Storage static website

- Origin Hostname - Enter the Primary endpoint  which was generated from the storage static website(This will be available to select from the dropdown)

- After creation of the  endpoint, after sometime you will be able to view your website 
   via the Endpoint hostname. (*This will take some time*)

- Now we will need to assign your custom domain to the endpoint created.

- First create a CNAME record will your domain provider pointing to the Endpoint hostname.

- After that create a custom domain entry and save.

![08_CustomDomain.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1618124492417/7-Xw0VL0t.png)

- Next Step is to enable HTTPS for the domain.


![09_Https_enable.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1618141708298/NkdPBb9Ih.png)

At this point we have our basic website up and running with Https enabled.

> #### (Step 3) Workflow in Github for the static website 

- We will create a Workflow in GitHub to publish any changes checked in GitHub.

 https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-static-site-github-actions

- You need to execute this is Azure CLI

```
az ad sp create-for-rbac --name {myStaticSite} --role contributor --scopes /subscriptions/{subscription-id}/resourceGroups/{resource-group} --sdk-auth
``` 
- Replace the placeholders with your subscription ID and resource group name. The output is a JSON object with the role assignment credentials that provide access to your storage account similar to below. Copy this JSON object for later.


```
{
    "clientId": "<GUID>",
    "clientSecret": "<GUID>",
    "subscriptionId": "<GUID>",
    "tenantId": "<GUID>",
    (...)
  }
``` 

- Configure the GitHub secret

- In GitHub, browse your repository.

- Select Settings > Secrets > New secret.

- Paste the entire JSON output from the Azure CLI command into the secret's value field. Give the secret a name like AZURE_CREDENTIALS.

- Go to Actions for your GitHub repository.

- Select Set up your workflow yourself.

- The file will appear in the .github/workflows folder of your repository

```
name: Blob storage website CI

on:
    push:
        branches: [ main]
    pull_request:
        branches: [ main]

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

    - name: Upload to blob storage
      uses: azure/CLI@v1
      with:
        azcliversion: 2.0.72
        inlineScript: |
            az storage blob upload-batch --account-name <STORAGE_ACCOUNT_NAME> -d '$web' -s .
    - name: Purge CDN endpoint
      uses: azure/CLI@v1
      with:
        azcliversion: 2.0.72
        inlineScript: |
           az cdn endpoint purge --content-paths  "/*" --profile-name "CDN_PROFILE_NAME" --name "CDN_ENDPOINT" --resource-group "RESOURCE_GROUP"

  # Azure logout 
    - name: logout
      run: |
            az logout
``` 
- Review your deployment
- Go to Actions for your GitHub repository.
- Open the first result to see detailed logs of your workflow's run.

> #### (Step 4) Create a Cosmos DB for saving the website visitor count.

![cosmos.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1619369542099/2rxmCnGXZ.png)

> #### (Step 5) Create a Function to Fetch/Update the visitor count

- Create a HTTP Trigger Function via Visual Studio Code. 
In that we will be using Input / Output binding to fetch and update value from Cosmos DB.

```
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Net;
using System.Net.Http;
using System.Text;

namespace CustomCounter
{
     public class Counter
    {
        [JsonProperty(PropertyName = "id")]
        public string Id {get; set;}
        
        [JsonProperty(PropertyName = "index")]
        public string index {get; set;}


        [JsonProperty(PropertyName = "Count")]
        public int Count {get;set;}

    }
    public static class ResumeCounterTrigger
    {
        [FunctionName("GetResumeCounter")]
        public static HttpResponseMessage Run(
            [HttpTrigger(AuthorizationLevel.Anonymous , "get", Route = null)] HttpRequest req,
                     [CosmosDB(
                    databaseName:"CloudResume", 
                    collectionName: "tblCloudResume",
                    ConnectionStringSetting = "democosmos_DOCUMENTDB", 
                    Id = "One", 
                    PartitionKey = "RecordCount")] Counter Readcounter,  
                    [CosmosDB(
                        databaseName:"CloudResume",
                        collectionName: "tblCloudResume",
                        ConnectionStringSetting = "democosmos_DOCUMENTDB",
                        Id = "One", 
                        PartitionKey = "RecordCount")] out Counter updatedCounter,            
            ILogger log)
        {
                log.LogInformation("GetResumeCounter was triggered.");
                updatedCounter = Readcounter;
                updatedCounter.Count += 1;
           var jsonToReturn = JsonConvert.SerializeObject(updatedCounter);
           return new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent(jsonToReturn, Encoding.UTF8, "application/json")
            };
        }
    }
}

``` 
- Next a create a function App in the Azure Portal. Have selected consumption based function.
- Have enabled Application Insights for the same.
- You also have to enable CORS (use the URL of the static website) to use the function.

> #### (Step 6) Create a KeyValult to save the Cosmos DB connection string

- In order to prevent the Cosmos DB connection string in the function to be plain text we will be using KeyValult by creating a secret.

https://docs.microsoft.com/en-us/azure/key-vault/secrets/quick-create-portal

Additional steps to be done 

- Enable Managed Identify in Azure Functions
 https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview

![01_Function_Identity.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1619371224750/z4_L5vSmX.png)

- In Key vault add access policy for the created Identity

![02+keyvalult.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1619371323199/3q2WkfVMX.png)

-  Finally in the function, in Application Settings add the value for the Cosmos DB connection string.
 
https://docs.microsoft.com/en-us/azure/app-service/app-service-key-vault-references

![03_Functions.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1619376438651/m7J6ZluxE.png)

> #### (Step 7) Workflow in Github for the Function.
- We will create a workflow for publishing Function code to the Function app.

```
name: WebSiteFunctions

# Deploy push made from Functions folder.
on:
  push:
        branches: [ main ]
        paths:
        - 'Functions/**'
          

# CONFIGURATION
# For help, go to https://github.com/Azure/Actions
# 1. Paste the RBAC json into the following secret in your repository:
#   AZURE_RBAC_CREDENTIALS
# 2. Change these variables for your configuration:

env:
  AZURE_FUNCTIONAPP_NAME: <Function App Name>  
  AZURE_FUNCTIONAPP_PACKAGE_PATH: '<path to your web app project, defaults to the repository root>'  
  DOTNET_VERSION: '3.1'    # dotnet version

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

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

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

  - name: 'Run dotnet'
      shell: pwsh
      run: |
        pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/API'
        dotnet build --configuration Release --output ./output
        popd
    
    - name: 'Run Unit Test'
      shell: pwsh
      run: | 
        pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/Test'
        dotnet test
        
    - 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'

``` 

> #### (Step 8) Create a simple Unit test for the Function

https://madebygps.com/how-to-use-xunit-with-azure-functions/

https://docs.microsoft.com/en-us/azure/azure-functions/functions-test-a-function#c-in-visual-studio
 

