Update Ghost blogs and pages with PowerShell
In order to remove a bit of repetitive manual work from my schedule I wrote a little sync between my Scrum.org class schedule to this blog.
 
                    The samples provided by Ghost are in JavaScript, Curl and Python, all languages I'm not fluent in, so I set out to do the same from PowerShell or C#.
The hardest part turned out to be the code to create the correct JWT token to authenticate against Ghost. In their admin panel they provide a set of tokens, but it turns out you can't just use these tokens as-is.

The API key essentially makes up 2 parts:
- The Key Identifier, that's the part up to the :
- The Secret, that's the part after the :
In order to use this key, we first have to split it into its two parts:
$parts = $adminApiKey -split ":"
$id = $parts[0]
$secret = $parts[1]We then need to construct a JWT token from this key. I relied on the Posh-JWT package for that part:
Install-Module -Name JWT -force
$key = [Convert]::FromHexString($secret) # only works in PowerShell Core
$jwttoken = New-Jwt `
  -Header (
    @{
      "alg" = "HS256"
      "kid" = $id
      "typ" = "JWT"
    } | ConvertTo-Json
  ) `
  -PayloadJson (
    @{
      "exp" = ([DateTimeOffset](Get-Date).AddMinutes(2)).ToUnixTimeSeconds()
      "iat" = ([DateTimeOffset](Get-Date)).ToUnixTimeSeconds()
      "aud" = "/admin/"
    } | ConvertTo-Json) 
  -Secret $keyWith this token, you can then invoke the Ghost API.
$headers = @{
  Authorization  = "Ghost $jwttoken"
  "Content-Type" = "application/json"
}
$baseUri = "https://scrumbug.ghost.io/ghost/api"
        
$result = Invoke-RestMethod `
  -Uri "$baseUri/admin/pages/" `
  -Method GET `
  -Headers $headers     As I said, I used this code to automatically sync my Scrum.org classes to this blog. You can find the (private) Ghost update GitHub Action here. To keep my secrets secure I added a couple of extra lines of code to register my secrets with the runner:
Write-Output "::add-mask::$id"
...
Write-Output "::add-mask::$secret"
...
Write-Output "::add-mask::$key"
...
Write-Output "::add-mask::$jwttoken"End result? My classes are automatically updated 🎉:

 
                