JAMStack Foundations



There are a few tools that you should have on your computer before beginning this project. Specific setup instructions for these pre-requisites are out of scope of this project.

Windows 10


Create Hugo Website Locally

Hugo Quick Start

Follow the instructions in the Hugo Quick Start guide to build your website. I’ve outlined a couple of changes to the instructions below. MacOS users can follow the Quick Start instructions exactly.

Download and install Hugo with the below steps:

PS C:\> mkdir Hugo
    Directory: C:\

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        3/15/2018   1:11 PM                Hugo

PS C:\> cd Hugo
PS C:\Hugo> wget https://github.com/gohugoio/hugo/releases/download/v0.37.1/hugo_0.37.1_Windows-64bit.zip -outfile hugo_install.zip
PS C:\Hugo> Expand-Archive hugo_install.zip"
PS C:\Hugo> set PATH=%PATH%;C:\Hugo\hugo_install

Close and open your PowerShell window to have the newly added path to Hugo active.

Create a parent directory and navigate to it before creating the site.

PS C:\> mkdir website

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        3/15/2018  12:40 PM                website
PS C:\> cd website
PS C:\website> hugo new site quickstart

Then ensure you are in the parent directory and initialize the local Git repository.

PS C:\website> ls

    Directory: C:\website

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        3/15/2018  12:45 PM                quickstart

PS C:\website> git init
Initialized empty Git repository in C:/website/.git/

The instructions show commands that are Linux/Mac OS. These mostly work in Windows Powershell leave off any ;\ as they aren’t necessary.

The instructions also contain the following line:

echo 'theme = "ananke"' >> config.toml

This inserts the text into the config.toml but the text in incorrectly encoded and instead looks like this:

t h e m e   =   " a n a n k e "

Instead run that line as follows:

echo 'theme = "ananke"' >> config.toml

This assumes that you’ve got Git bash for Windows installed.

Alternatively you can just edit the config.toml in your favorite text editor and add that line.

Create Private GitHub Repository

This step requires a GitHub developer account if you want to have your site source code private. If you don’t care then go ahead and create a public repository.

We already initialized a local Git repository so now we will link it to our GitHub Repository.

PS C:\website> git remote add origin git@github.com:user/testweb.git
PS C:\website> git add .
PS C:\website> git commit -m "First Commit"
PS C:\website> git push -u origin master

We have now successfully initialized and pushed our website source to GitHub

One item to add is a .gitignore file to ensure the public directory is not pushed to GitHub this directory contains the compiled website files from Hugo.

PS C:\website> echo "quickstart/public/" >> .gitignore

Create Azure Storage Account v2

We are going to be using Azure CLI 2.0 for this work. You should have already set this up from the pre-requisites section. I’m going to assume that you only have one subscription attached to your Microsoft account here.

az login
az group create --name "Website" --location "North Central US"
az storage account create --name "websitestor45" --resource-group "Website" --location "North Central US" --sku Standard_RAGRS --kind StorageV2 --https-only true --access-tier Hot
az storage container create --account-name "websitestor45" --name "website" --public-access container

Our redundant storage account has been created with a publicly available container for our website content.

Create Azure CDN Profile and Namespace

Now on to creating the CDN profile and a namespace linked to our storage account container.

az cdn profile create --resource-group "Website" --name website --sku Premium_Verizon

az cdn endpoint create --resource-group "Website" --name website454 --profile-name "website" --origin websitestor45.blob.core.windows.net --no-http --origin-path "/website" --query-string-caching NotSet --enable
-compression false

az cdn custom-domain create -resource-group "Website" --endpoint-name website --profile-name website -name website --hostname www.example.com

This gets the CDN setup with your custom domain. Next is to enable SSL.

NOTE: You must have your WHOIS contact info up to date. A Domain Verification e-mail will come from from Digicert on behalf of Verizon which you must be able to respond to in order for the certificate request to be completed.

az cdn custom-domain enable-https --endpoint-name website --name website --profile-name website --resource-group "Website"

The requests are only processed within typical North American business hours. So if you run this outside of those hours it won’t be processed until the next business day. Even within business hours it can be a few hours before you receive the domain validation e-mail.

After you have verified that you own the domain. It will take an additional 90 minutes to 6 hours to have the certificate propagate through the global CDN network.

Configure Azure Premium CDN Rules

Unfortunately at this time you will need to use a web based GUI to configure these.

Login to the Azure Portal with your credentials. Then select Resource Groups from the left bar, click on your Website resource group you created in earlier steps and then click on your CDN Profile that you also created earlier.

CDN Profile Screenshot

Click on the Manage button to have the CDN management portal open in a new tab.

Once the management portal opens go to the HTTP Large menu and select Rules Engine CDN Management Portal Main Screen

Click the Add New button and configure the below rules.

Add URL Rewrite Rule For ease of setup please copy the RegEx below:

Rule 1 : ((?:[^\?]*\/)?)($|\?.*)    $1index.html$2
Rule 2: ((?:[^\?]*\/)?[^\?\/.]+)($|\?.*)    $1/index.html$2

Save the rule and add another as below for caching rules. Add CDN Caching Rules Then another rule to bypass caching of the CMS configuration file. Bypass CDN Cache for CMS Config

Now to wait 90 minutes to 4 hours for rule propagation. The Pending XML will change to Active XML when the propagation of the rule is complete.

While we wait let’s move on to creating the VSTS CI/CD Pipeline

Create Visual Studio Online CI/CD Pipeline

Go to Visual Studio Online and activate your free Visual Studio Online tenant.

Once you are signed in, create a new Project. Go into the new Project.

Create New Build Select Build and Release and then select Builds. From there click new to start a new CI/CD pipeline.

Configure Phase 1 Select Process at the top. Set your build agent queue type. It should be set to Hosted.

GitHub Source Add Add your GitHub repository.

Add Hugo Build Click the + on Phase 1 and add/install the Hugo build extension.

Configure Hugo Build Configure the Hugo build task with the illustrated settings. Your source should be the folder in the GitHub repository that contains the website source files. The destination should be source_folder/public.

Upload to Blob Next add/install the AzureBlob File Copy build task. Select the correct subscription, the public folder from the earlier step as the source. Then enter the storage account name, the container that you setup in the previous steps. Also add the /SetContentType in Additional Arguments as by default all MIME content types get set to application/octet-stream which will not work. Now unfortunately not all file types are correctly set by the /SetContentType so we have to run a custom PowerShell script to catch those other file types.

Open PowerShell ISE on your computer and paste this code:

Make the appropriate changes to the code for your storage account. As the code above suggests DO NOT paste your storage account key in your script. I added a parameter in which I could pass the key through an additional argument. This is somewhat better though using Azure Key Vault is a much better idea. Here’s the additional code to add for the parameter. Another addition is for the CMS config.yml and Markdown files to have it’s content type set correctly.


#Add these two items to the SWITCH statement
        ".yml" { $ContentType = "text/yaml" }
        ".md" { $ContentType = "text/markdown" }

Delete the existing $StorageAccountKey variable declaration and save. Save the file in the root of your local git repository as fixContentType.ps1 and push the changes to your GitHub repository.

Set Content Type Advanced

Add and configure the Azure PowerShell script:FilePath build task as shown. Ensure you specify your storage account key in the script arguments area.

Save and Queue your new CI/CD pipeline.

Build Progress

Go into your build and monitor the build process. You can view the logs to see if there are any errors in the build process.

Ensure that the CDN rules are now active and you should be able to visit your custom domain at https://www.example.com and see your website.

Now let’s get CMS functionality activated.

Add Netlify CMS and Authentication Micro-service

GitHub Authentication Micro Service

First let’s create a GitHub OAuth application. Pick a unique Web App name. The callback URL will be https://webappname.azurewebsite.net/callback

Follow these Instructions to create the OAuth Application

You should have a Client ID and Client Secret. Save those for the next step.

Next let’s create a free Azure Web App.

NOTE: Replace gh-auth-service with the unique name you picked earlier. This forms the Web App URL as discussed earlier. Replace the OAUTH_CLIENT_ID and OAUTH_CLIENT_SECRET values with the values you obtained from creating the OAuth Application in GitHub.

az login
az appservice plan create --resource-group "Website" --name "FreePlan" --sku FREE
az webapp create --resource-group "Website" --plan "FreePlan" --name "gh-auth-service" --deployment-source-branch master --deployment-source-url https://github.com/vencax/netlify-cms-github-oauth-provider.git
az webapp config appsettings set --name "gh-auth-service" --resource-group "Website" --settings NODE_ENV=production OAUTH_CLIENT_ID=f432a9casdff1e4b79c57 OAUTH_CLIENT_SECRET=pampadympapampadympapampadympa
az webapp restart --name "gh-auth-service" --resource-group "Website"

Netlify CMS Installation and Configuration

In your local git repo add a folder called admin under the static folder. Your tree should be: root/website/static/admin

Create a new filed called index.html in this folder and paste the following code in it:

<!doctype html>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Content Manager</title>

  <!-- Include the styles for the Netlify CMS UI, after your own styles -->
  <link rel="stylesheet" href="https://unpkg.com/netlify-cms@^1.0.0/dist/cms.css" />

  <!-- Include the script that builds the page and powers Netlify CMS -->
  <script src="https://unpkg.com/netlify-cms@^1.0.0/dist/cms.js"></script>

Save this file. Next create another new file called config.yml. Paste the following into it:

  name: github
  repo: dude/website
  branch: master
  base_url: https://gh-auth-service.azurewebsites.net

publish_mode: editorial_workflow
media_folder: "quickstart/static/images/uploads"
public_folder: "/images/uploads"

  - name: "posts" # Used in routes, e.g., /admin/collections/blog
    label: "Posts" # Used in the UI
    folder: "quickstart/content/blog" # The path to the folder where the documents are stored
    create: true # Allow users to create new documents in this collection
    slug: "{{year}}-{{month}}-{{day}}-{{slug}}" # Filename template, e.g., YYYY-MM-DD-title.md
    fields: # The fields for each document, usually in front matter
      - {label: "Layout", name: "layout", widget: "hidden", default: "post"}
      - {label: "Title", name: "title", widget: "string"}
      - {label: "Publish Date", name: "date", widget: "datetime"}
      - {label: "Description", name: "description", widget: "text"}
      - {label: "Rating (scale of 1-5)", name: "rating", widget: "number"}
      - {label: "Body", name: "body", widget: "markdown"}

Ensure that you are customizing it with your repository information. Change the base_url setting to your authentication micro service URL.

Push the changes to the GitHub repository and wait 15 minutes for build pipeline to complete and the CDN cache to expire.

git add .
git commit -m "Netlify Deployment"
git push

Final Testing

Navigate to your custom domain CMS route https://www.example.com/admin/. A page with a “Login to GitHub” button should appear. Click it and your custom GitHub authentication micro service should launch a window that will prompt you to authorize it to use your GitHub credentials to login to the CMS. After that you should be taken into the CMS portal.

CMS Image

From there you can test the editorial workflow, creating new Posts etc. After Publishing a new post you should see the new content appear within 5-15 minutes on the main site.

Now there can be two simultaneous development streams. One for content creators, and one for developers extending the sites functionality and facilitating any UI enhancements. Content creators would use the CMS, and developers would be making use of local development and pushing those changes to the repository.