Automate Post Scheduling

Using Azure Function Apps to Automate Post Scheduling

Some waffle about burnout…

Ok, So firstly. Yes I’m alive. 🙌 Just you know how it goes… every one aims to try and blog/post some content every couple of weeks or months and then that old friend “technical burn out” arrives and you just don’t want to touch and computer, server or console after work. Yeah.. so that was me! But recently I was catching up with Lisa Lizaard08 and we were talking about blogging and domains and how does one get stared and what are the best ways to go around this and what options are out there for getting started. and while Lisa may end up using wordpress for now. I got my thinking about a project I attempted to start waaay back in February of this year. The Project you ask? trying to schedule posts for Hugo. Which lead to some interesting investigation and some failed attempts and some research.

[0] - The issues with Hugo

Now, look don’t get me wrong, Hugo for the most part. its pretty awesome in terms of static website content. If you’re wanting to a) host a blog, b) get wicked with markdown c) loose the will with css and scss files. then Hugo is defiantly the way to go here. and Since moving from WordPress to Hugo in December last year. I’ve loved the process, Not just from the technical side of learning and improving my markdown skills. but its helped me improve my Git, YAML, GitHub Action knowledge. So yes Hugo is pretty awesome.

BUT! Because its static, there is no native way of post scheduling, Something I’ve missed since moving away from WordPress. Now because of how you can use Hugo there about 100 and 1 different ways to resolve said issue.

[1] - Scoping of the Project

The scope is pretty straight forward, You want to blog a series of post. For example like the Learning Git and Github posts I did. But you cant’t automatically post at 10am every Monday for example. Yes I could stage all the PR’s and then every Monday for the next 5 weeks, Close them at around 10amish. - Yeah no this wont scale!

So we need something which can create/check a GitHub Pull Request, and then some time based trigger to close the PR at 10:00am on Monday and trigger the Azure Static Web App Rebuild and Publish. This means we need initially 2 files/script.

  • Script One - CreateGitHubPR
  • Script Two - MergeGitHubPR

I then also created two more scripts, Which will be explained in a bit!

  • Script Three - createNewPost.ps1
  • Script Four - PublishPost.ps1

[2] - Trying GitHub Actions

Ok, this one initially sense, As the code base for the BWC Production and Dev hugo sites is hosted in GitHub, Why not use GitHub Actions to run the automation?

Having looked around on the GitHub Market place, I found two possible options:

  • Option One - PR-Scheduler
    This looked really good initially from what I saw. and the Initial testing was going well.
    It used variables based on the Pull request to schedule and close. But you can only use it 4 times a month for the Free version, and the Personal usage package was around £5.00 a month. - I have nothing again paying for services, But I felt a GitHub Action should be able to cover this.

  • Option Two - merge-schedule Like Option One, this also used scheduled timezone formate. So I opted to try and use this in a GitHub action and started to deploy some YAML.

[3] - Git Actions & Timers

So this is where I found the first issue with GitHub Actions. If you want to trigger them to build like a static web app, Sure no issues. Close the PR and it builds and content its fired to Azure! 👌 BUT! If you want scheduled/cron like action to run say every 15 minutes to check if you need a post to go live? Yeah no. No dice. no bueno 🥲. What I found over a week of testing is that apparently I’m not the only one to have issues with GitHub Actions and time based triggers.

After some very annoying research, I found the following links which basically stalled the project dead in the water…

https://github.com/upptime/upptime/issues/42

https://github.com/orgs/community/discussions/27130

https://upptime.js.org/blog/2021/01/22/github-actions-schedule-not-working/

So what was the take away from these links I found? well the snippets speak for them selves reader…

source: https://upptime.js.org/blog/2021/01/22/github-actions-schedule-not-working

source: https://github.com/upptime/upptime/issues/42#issuecomment-837816094

[4] - Azure Functions

This got my thinking, As part of working for Intercept, I get an Visual Studio Enterprise - Azure Subscription and wanting to learn some more about Azure Functions, already knowing you can use them with HTTP and TimeTrigger functions, I set out Plan 2.0 and started playing around with an Azure Function. Now for those who are new to Azure and might not know what an Azure Function is, The Microsoft Learn page here Clickety Click will help you get up to speed 😀

The initial testing was working well, I’ve created a rough script which created the PR based on Commit from a local powershell file and started working on the time based trigger to check GitHub Pull requests every 15 minutes, initially and then based on the scheduling parameters of [time] and [date] close the pull request, and then I hit (at the time in February) a strange issue. which may not have caused an indirect pause in the blogging flow.

What was the issue you ask? Well let me tell you. So, having realised this now (in July) its pretty commonsense, But Simon of February clearly missed this!! When using Git, and you do git checkout -b my-new-post it clones the default repository, (yes I know this isn’t news…) you then publish the post and start on post two git checkout -b my-second-new-post and then scheduled this. What I noticed was that in the commit history there were two commit IDs for my-new-post and my-second-new-post.

Having realised eureka moment, I knew it was a fixable issue and set about creating a) a new blog post to celebrate this news and b) some infrastructure as code to help other hugo bloggers.

HugoScheduler Overview

So you’re using Hugo and you’re wanting to find a easy way to schedule and automate posts for your blog? Well Here is the solution. You’ll want to clone the bwc-hugo-post-scheduler-source-files repository and fork the bwc-hugo-post-scheduler-function-app for the function App CI/CD deployment

GitHub Repositories

bwc-hugo-post-scheduler-source-files

git clone https://github.com/builtwithcaffeine/bwc-hugo-post-scheduler-source-files.git

bwc-hugo-post-scheduler-function-app

git clone https://github.com/builtwithcaffeine/bwc-hugo-post-scheduler-function-app.git

Deploying Azure Function App

Before starting, Please ensure you have the following installed.

  • Microsoft Visual Studio Code
  • Microsoft Azure CLI (2.0.61)*
  • Az CLI Bicep (0.28.1)*
*versions at time of posting (July 2024)

Getting stated with the Function App, You’ll want to have cloned the bwc-hugo-post-scheduler-source-files repository. This contains the reference Bicep Files. Open Powershell and navigate to the folder and find the file deployHugoScheduler.ps1.
All this does create a user friendly wrapper to pass in some variables and user Azure CLI to initiate the bicep deployment. ✨

Deployment takes around 5 minutes, Grab a coffee ☕︎

Before you deploy the Bicep, You’ll need to edit the deployment variables:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// Resource Group Variables
param newResourceGroupName string = 'rg-hugo-scheduler-${environmentType}-${deployLocationShortCode}'

// Storage Account Variables
param newStorageAccountName string = 'sahugoscheduler${environmentType}${deployLocationShortCode}'

// Application Insights Variables
param newAppInsightsName string = 'ai-hugo-scheduler-${environmentType}-${deployLocationShortCode}'

// Log Analytics Variables
param newLogAnalyticsName string = 'la-hugo-scheduler-${environmentType}-${deployLocationShortCode}'

// App Service Plan Variables
param newAppServicePlanName string = 'asp-hugo-scheduler-${environmentType}-${deployLocationShortCode}'

// Azure Function Variables
param newFunctionAppName string = 'func-hugo-scheduler-${environmentType}-${deployLocationShortCode}'

Once you have updated these, you can execute a deployment using the following command:

1
.\deployHugoScheduler.ps1 -subscriptionId [subId] -deployLocation 'westeurope' -environmentType [dev] [prod]

The Bicep file will deploy the following resources:

  • Resource Group
  • Function App
  • Storage Account
  • Application Insights
  • Log Analytics WorkSpace

Function App Configuration

Now we have the infrastructure deploy, I’ll cover the two scripts which are required for the project. How you deploy this can be done in one of two ways. You could either fork and clone the Function App template and setup a CI/CD Pipeline or manually create the two Functions within the function app.

Once the Function is ready, You’ll need to create a GitHub Authentication Token to be able to create and merge the Pull requests from your Hugo Repository. GitHub Docs

I would recommend using a Fine-grained access token, As you can scope the access to the repository you’re working with.

Now you have created your GitHub Authentication Token, You need to the Environments Tab under the Azure Function, During the Bicep deployment an environment variable is pre-staged called: GITHUB_USER_TOKEN You can update this with the token and call it in the script as $Env:GITHUB_USER_TOKEN

The Permissions required for the Token are:

  • [REPOSITORY] - Read access to metadata
  • [REPOSITORY] - Read and Write access to code and pull requests

[Func] - CreateGitHubPR

The first function wants to be a HTTP Trigger. This Function will be called from the PublishNow.ps1 script, Which will be stored locally, This commits the post from your local machine to GitHub and creates a Pull Request based on a [time] and [date]. For Example. [Friday] at [10:00] and works in conjunction with the MergeGitHubPR function.

[Func] - MergeGitHubPR

The second function wants to be a TimerTrigger. This function runs every [insert-time-value-here] minutes/hours to check any opening/pending Pull requests for your blog repository and if they match it will add a commit message confirming that the [time] and [date]. otherwise it will add a comment advising that the [time] and/or [date] are not correct for the scheduling of said post and will recheck in the defined period of time.

Depending on how often you want to schedule your posts, Set this accordingly. For now I’m only scheduling posts on the hour. So have this set execute every hour.

If you need some extra help with reading cron schedules, This is a super helpful website - https://crontab.cronhub.io/

Function App - Source Control

If you want to use a source controlled deployment, You can fork this repository bwc-hugo-scheduler

Once you have forked the repository, From the function app you can configure CI/CD. From the function app under Deployment Center

From here you can choose your CI/CD Platform of choice and then configure:

Once the function app has completed an initial pull from the repo, Your function app should look something like this:

Post Scheduling Guide

createNewPost.ps1

The createNewPost.ps1 file, requires Git.Git and Hugo.Hugo.Extended to be installed on the local machine. The PowerShell file nicely wraps several steps into one executed line.
The file supports two parameters environmentName and newPost

  • environmentName
    The supported validators for this parameter are dev or prod. These values need to be updated before you can run and point to the local production and development hugo repositories.
  • newPost
    This is used for the post name folder under /content/posts/<post-name>.
    but is also used to create a git branch as post/<post-name>
1
.\createNewPost.ps1 -environmentName [dev]-[prod] -newPost [postname]

Under the hood what is happening?

  • create a git branch for the post: [post/«post-name»]
  • creates a folder under: [./content/posts/«post-name»]
  • creates an assets folder: [./content/posts/«post-name»/assets]
  • creates a branch checkout ready for the single post.

publishNow.ps1

Before running the Publish file, Open the file in your favorite IDE and edit line 110 and replace with the default (Function key) for the createGitHubPR function. This allows the user to automatically create a Pull request from the branch commit and post.

1
$azFuncUri = "<azure-function-url-here>"

The file supports two parameters publishDay and publishTime

  • publishDay
    The supported validator is well days of the week.

  • publishTime
    Currently this is defined on an hourly basis, you could update the variable to also include 15 minute or 30 minutes intervals. But I thought for now posting on an hourly basis would work fine. ALso remember to take this into the account for the MergeGitHubPR Function. As the timer trigger will need to be updated to reflect the additional time entries.

1
.\PublishNow.ps1 -publishDay <monday> -PublishTime <10:00>

Post Summary

Ok lets wrap this one up! What are the takes aways from this post? I fixed/recovered from my blogging burn out! (Thanks Lisa). I learn some Azure Function awesomeness and created two functions to resolve an issue I found with blogging with Hugo as there is no native support for post scheduling. but, but now there is! 😄
So for now peeps, #KeepBlogging and I’ll catch you in the next one! - Simon

PSA, If your curious - Lisa is also starting on the whole blogging train, You can find her site here: https://lisaslessons.cloud