Automating Sync from Github to Azure Runbooks With Github Actions

I started a side project on a Discord server where I have loads of automation plugged into specific channels for updates I want. After about the 5th runbook, things became a bit messy. I needed a solution that kept my github repo in sync with my Azure Automation accounts runbooks, but I also needed to be able to run code locally without hardcoding variables into the scripts. This is what I came up with:

Enterprise App Registrion To Github Repo
KKSB is my repo name
Add your github creds as a federated credential – Pick your branch/ organization on setup.
Things needed for the script:

Client ID of the app registration
Tenant ID of your Entra tenant
Subscription ID of your Azure subscription
Automation Account Name
Name Of The Resource Group The Account Lives In

sync-automation.yml
name: Sync Runbooks to Azure Automation

on:
  push:
    branches: [ "main" ]
    paths:
      - "runbooks/**.ps1"
      - ".github/workflows/sync-automation.yml"
  workflow_dispatch: {}

permissions:
  id-token: write
  contents: read

env:
  AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
  AZURE_TENANT_ID:       ${{ secrets.AZURE_TENANT_ID }}
  AZURE_CLIENT_ID:       ${{ secrets.AZURE_CLIENT_ID }}
  AUTOMATION_ACCOUNT:    ${{ vars.AUTOMATION_ACCOUNT }}
  RESOURCE_GROUP:        ${{ vars.AUTOMATION_RESOURCE_GROUP }}

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - name: Check out
        uses: actions/checkout@v4

      - name: Azure login (OIDC) + Az PowerShell
        uses: azure/login@v2
        with:
          client-id: ${{ env.AZURE_CLIENT_ID }}
          tenant-id: ${{ env.AZURE_TENANT_ID }}
          subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }}
          enable-AzPSSession: true

      - name: Install Az.Automation
        shell: pwsh
        run: |
          Install-Module Az.Accounts -Force -Scope CurrentUser
          Install-Module Az.Automation -Force -Scope CurrentUser

      - name: Verify Az context (PowerShell)
        shell: pwsh
        run: |
          $ctx = Get-AzContext
          if (-not $ctx) { throw "No Az context; PowerShell not logged in." }
          "Az Account: $($ctx.Account.Id)"
          "Subscription: $($ctx.Subscription.Id)"

      - name: Import + publish runbooks
        shell: pwsh
        run: |
          $rg  = "${{ env.RESOURCE_GROUP }}"
          $acc = "${{ env.AUTOMATION_ACCOUNT }}"
          if (-not $rg -or -not $acc) { throw "Missing RESOURCE_GROUP or AUTOMATION_ACCOUNT." }

          Write-Host "Current working directory: $(Get-Location)"
          $runbooks = Get-ChildItem "runbooks" -Filter *.ps1 -Recurse -File
          if (-not $runbooks) { Write-Host "No runbooks found under ./runbooks"; exit 0 }

          foreach ($rb in $runbooks) {
            $name = [IO.Path]::GetFileNameWithoutExtension($rb.Name)
            Write-Host "=== Importing/Updating runbook: $name from $($rb.FullName)"
            Import-AzAutomationRunbook `
              -ResourceGroupName $rg `
              -AutomationAccountName $acc `
              -Name $name `
              -Type PowerShell `
              -Path $rb.FullName `
              -LogProgress:$true -LogVerbose:$true `
              -Force
            Publish-AzAutomationRunbook -ResourceGroupName $rg -AutomationAccountName $acc -Name $name
            Write-Host "✅ Published: $name"
          }
      - name: Sync configs → Automation Variables (PROD)
        shell: pwsh
        run: |
          # Ensure modules exist (already installed above, but safe to re-run)
          Install-Module Az.Accounts -Force -Scope CurrentUser
          Install-Module Az.Automation -Force -Scope CurrentUser

          # Az context is already established via azure/login enable-AzPSSession: true
          $rg  = "${{ env.RESOURCE_GROUP }}"
          $acc = "${{ env.AUTOMATION_ACCOUNT }}"

          ./tools/Sync-ConfigsToAutomationVariables.ps1 `
            -ConfigFolderPath "./configs" `
            -ResourceGroupName $rg `
            -AutomationAccountName $acc `
            -AutomationPrefix "PROD"

For each of my runbooks I have a like-named JSON file that contains the variables. You can see in the last block, Github Actions will look at each of the config files, strip the variables and create them as automation variables in Azure. Once setup correctly, your commits will look like this:

And when we click into one of these and click into it again we get:

sample output from yaml file

It takes a few minutes to kick off but you can see it from the runbook section updating each runbook one-by-one

Leave a Comment

Your email address will not be published. Required fields are marked *