DEV Community

Mark O'Regan
Mark O'Regan

Posted on • Updated on

Mark O'Regan: CV Challenge

Finished site

Mark O'Regan's CV Challenge

The Requirements

The requirements of the challenge were as follows:

  • Get an Azure Foundation Certification
  • Front-end: Create a resume website
    • HTML, CSS, JavaScript
    • Host on Azure Static Sites
    • Use HTTPS for security
    • Custom domain name
    • Set Azure DNS
    • Use JavaScript to call Azure Counter Function
  • Back-end
    • Set up a Cosmos database to store the hit counter
    • Azure Function that serves as an API to connect web app to cosmos db.
    • Write tests that validate API tests
  • Infrastructure-as-code
    • Pulumi - Web site, azure function, cosmos db, DNS
    • Add source control for the site via Azure Devops
    • Implement automated CI/CD for the front-end
    • Implement automated CI/CD for the back-end
  • Write a blog post detailing the experience

Implementation

There are quite a few requirements listed above but they basically boils down to a 4 main areas, a front-end, a back-end, infrastructure as code and two CI/CD pipelines for publishing front and back-ends.

Image description

Front-end

I wanted to keep the front-end straight forward so grabbed the bootstrap album template and used it to create a basic CV site. I choose Azure Static Web App as the site host because it can be easily integrated with Azure Devops pipelines.

First up I will the infrasturcture as code that creates

The following segment of code is taken pulumi and will create a resource group called "cvfrontend". A static site is created first.


import pulumi
from pulumi_azure_native import resources
import pulumi_azure_native as azure_native
import pulumi_azure_native.documentdb as documentdb
import pulumi_azure_native.resources as resources
import pulumi_azure_native.storage as storage
# Create an Azure Resource Group
resource_group = resources.ResourceGroup('cvfrontend')
site_name = "cvsitefrontend"

static_site = azure_native.web.StaticSite("staticSite",
    branch="master",
    build_properties=azure_native.web.StaticSiteBuildPropertiesArgs(
        api_location="api",
        app_artifact_location="build",
        app_location="app",
    ),
    location="West Europe",
    name=site_name,
    repository_token="add your token",
    repository_url="add your git url to repo",
    resource_group_name=resource_group.name,
    sku=azure_native.web.SkuDescriptionArgs(
        name="Free",
        tier="Free",
    ))
Enter fullscreen mode Exit fullscreen mode

I registered a Domain name "cvoregan.com" using Azure Portal which created two resources in the image below. A domain name and DNS Zone associated with the domain.

Image description

Once these domain name resources were available I then wrote some pulumi code to create a CNAME record. The record points the static site host Url and bind it to www.cvoregan.com.

record_set = azure_native.network.RecordSet("cvStaticSiteRecordSet",
    cname_record=azure_native.network.CnameRecordArgs(
        cname=static_site.default_hostname,
    ),    
    record_type="CNAME",
    relative_record_set_name="www",
    resource_group_name=resource_group.name,
    ttl=3600,
    zone_name="cvoregan.com")

static_site_custom_domain = azure_native.web.StaticSiteCustomDomain("cvStaticSiteCustomDomain",
    domain_name="www.cvoregan.com",
    name=site_name,
    resource_group_name=resource_group.name)
Enter fullscreen mode Exit fullscreen mode

The site would have a Javascript call that retrieved and updated the counter and displayed

Where the front-end became more interesting to me was implementing the

# Create an Azure resource (Storage Account)
storage_account = storage.StorageAccount(
    "cvcosmostr",
    resource_group_name=resource_group.name,
    sku=storage.SkuArgs(
        name=storage.SkuName.STANDARD_LRS,
    ),
    kind=storage.Kind.STORAGE_V2)

# Cosmos DB Account
cosmosdb_account = documentdb.DatabaseAccount(
    "sqladminacc",
    resource_group_name=resource_group.name,
    database_account_offer_type=documentdb.DatabaseAccountOfferType.STANDARD,
    locations=[documentdb.LocationArgs(
        location_name=resource_group.location,
        failover_priority=0,
    )],
    consistency_policy=documentdb.ConsistencyPolicyArgs(
        default_consistency_level=documentdb.DefaultConsistencyLevel.SESSION,
    ))

# Cosmos DB Database
db = documentdb.SqlResourceSqlDatabase(
    "AzureCVDB",
    resource_group_name=resource_group.name,
    account_name=cosmosdb_account.name,
    resource=documentdb.SqlDatabaseResourceArgs(
        id="AzureCVDB",
    ))

# Cosmos DB SQL Container
db_container = documentdb.SqlResourceSqlContainer(
    "container",
    resource_group_name=resource_group.name,
    account_name=cosmosdb_account.name,
    database_name=db.name,
    resource=documentdb.SqlContainerResourceArgs(
        id="counter",
        partition_key=documentdb.ContainerPartitionKeyArgs(
            paths=["/count"],
            kind="Hash",
        )
    ))

account_keys = pulumi.Output.all(cosmosdb_account.name, resource_group.name).apply(
    lambda arg: documentdb.list_database_account_keys(account_name=arg[0], resource_group_name=arg[1]))
Enter fullscreen mode Exit fullscreen mode

Back-end resources
The back-end is an HTTP triggered Azure Functions with Cosmos DB input and output binding. The Function is triggered, it retrieves the CosmosDB item, adds 1 to it, and saves it and returns its value to the caller. CORS

Top comments (0)