# Set up Maester in GitHub

Source: https://maester.dev/docs/monitoring/github

# <IIcon icon="mdi:github" height="48" /> Set up Maester in GitHub

This guide will walk you through setting up Maester in GitHub and automate the running of tests using GitHub Actions.

## Why GitHub?

GitHub is the quickest and easiest way to get started with automating Maester. The [free tier](https://github.com/pricing) includes 2,000 minutes per month for private repositories which is more than enough to run your Maester tests daily.

## Set up a GitHub repository for Maester

### Pre-requisites

- If you are new to GitHub, create an account at [github.com](https://github.com/join)

## Create a new GitHub repository

The Maester tests ship inside the Maester PowerShell module, so the repository doesn't hold any tests. It only holds the GitHub Actions workflow that runs Maester. You can use a repository you already have, or create one:

- Open [https://github.com/new](https://github.com/new)
- Fill in the following fields:
  - **Repository name**: E.g. `maester-monitoring`
  - **Private**: Select this option to keep your workflow and results private
- Select **Create repository**

Later, you can add a `maester-config.json` to change settings (see [Configuration](../configuration/overview.md)) and a `custom` folder for your own tests (see [Writing custom tests](../writing-tests/index.mdx)). The workflows below pick them up when they are there.

## Set up the GitHub Actions workflow

There are many ways to authenticate with Microsoft Entra from GitHub Actions. We recommend using [**workload identity federation**](https://learn.microsoft.com/entra/workload-id/workload-identity-federation) as it is more secure, requires less maintenance and is the easiest to set up.

If you’re unable to use more advanced options like certificates stored in Azure Key Vault, which need an Azure subscription, there’s also guidance available for using client secrets.

- <IIcon icon="gravity-ui:nut-hex" height="18" /> **Workload identity federation** (recommended) uses OpenID Connect (OIDC) to authenticate with Microsoft Entra protected resources without using secrets.
- <IIcon icon="material-symbols:password" height="18" /> **Client secret** uses a secret to authenticate with Microsoft Entra protected resources.

<Tabs>
  <TabItem label="GitHub Action using Workload identity federation (recommended)">

This guide is based on [Use GitHub Actions to connect to Azure](https://learn.microsoft.com/azure/developer/github/connect-from-azure) and uses the maester GitHub action.

:::tip[PowerShell shortcut]

If you have the `Maester` PowerShell module and the [GitHub CLI](https://cli.github.com/) installed, the next four sections (create Entra app, grant permissions, add federated credentials, set GitHub secrets) can be completed in a single command from inside a local clone of the repository:

```powershell
Connect-Maester -Service Azure
New-MtMaesterApp -GitHubActions -SetGitHubSecrets
```

The cmdlet auto-detects the GitHub repository from the local `git remote`, creates the application, grants all required Graph permissions, adds the federated credential, and pushes `AZURE_CLIENT_ID` / `AZURE_TENANT_ID` to the repo's Actions secrets via `gh`.

The portal-based steps below remain fully supported.

:::

### Pre-requisites Workload identity federation

<CreateEntraApp/>

### Add federated credentials

- Select **Certificates & secrets**
- Select **Federated credentials**, select **Add credential**
- For **Federated credential scenario**, select **GitHub Actions deploying Azure resources**
- Fill in the following fields
  - **Organization**: Your GitHub organization name or GitHub username. E.g. `jasonf`
  - **Repository**: Your GitHub repository name (from the previous step). E.g. `maester-monitoring`
  - **Entity type**: `Branch`
  - **GitHub branch name**: `main`
  - **Credential details** > **Name**: E.g. `maester-devops`
- Select **Add**

### Add Entra tenant info to GitHub repo

- Open your Maester GitHub repository and go to **Settings**
- Select **Security** > **Secrets and variables** > **Actions**
- Add the secrets listed below by selecting **New repository secret**
- To look up these values you will need to use the Entra portal, open the application you created earlier and copy the following values from the **Overview** page:
  - Name: **AZURE_TENANT_ID**, Value: The Directory (tenant) ID of the Entra tenant
  - Name: **AZURE_CLIENT_ID**, Value: The Application (client) ID of the Entra application you created
- Save each secret by selecting **Add secret**.

<EnableGitHubActionsCreateWorkflow/>

```yaml
name: Run Maester 🔥

on:
  push:
    branches:
      - main

  schedule:
    # Daily at 7:30 UTC, change accordingly
    - cron: "30 7 * * *"

  # Allows to run this workflow manually from the Actions tab
  workflow_dispatch:

jobs:
  test:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read

    steps:
      - name: Run Maester 🔥
        id: maester
        # Set the action version to a specific version, to keep using that exact version.
        uses: maester365/maester-action@main
        with:
          tenant_id: ${{ secrets.AZURE_TENANT_ID }}
          client_id: ${{ secrets.AZURE_CLIENT_ID }}
          # Use Maester 3.0. Without this the action keeps installing Maester 2.x.
          maester_major_version: "3"
          # Check out this repository to use its maester-config.json and custom tests, if it has any
          include_private_tests: true
          include_exchange: false
          include_teams: false
          # Set a specific version of the powershell module here or 'latest' or 'preview'
          # check out https://www.powershellgallery.com/packages/Maester/
          maester_version: latest
          disable_telemetry: false
          step_summary: true

      - name: Write status 📃
        shell: bash
        run: |
          echo "The result of the test run is: ${{ steps.maester.outputs.result }}"
          echo "Total tests: ${{ steps.maester.outputs.tests_total }}"
          echo "Passed tests: ${{ steps.maester.outputs.tests_passed }}"
          echo "Failed tests: ${{ steps.maester.outputs.tests_failed }}"
          echo "Skipped tests: ${{ steps.maester.outputs.tests_skipped }}"
```

:::note
The `maester_major_version: "3"` input opts the action in to Maester 3.0, where the built-in tests run from the Maester module. Without it the action keeps installing Maester 2.x, so existing workflows are not changed. If you are moving an existing workflow to 3.0, see [Upgrading from 2.x](../upgrading-from-2x.md).
:::

  </TabItem>
  <TabItem label="Custom workflow using Workload identity federation">

This guide is based on [Use GitHub Actions to connect to Azure](https://learn.microsoft.com/azure/developer/github/connect-from-azure)

### Pre-requisites

- An Azure subscription is required for this method. This Azure subscription is required to set up workload identity federation authentication. No Azure resources will be created and there are no costs associated with it.
  - If you don't have an Azure subscription, you can create one by following [Create a Microsoft Customer Agreement subscription](https://learn.microsoft.com/azure/cost-management-billing/manage/create-subscription) or ask your Azure administrator to create one.

<CreateEntraApp/>

### Add federated credentials

- Select **Certificates & secrets**
- Select **Federated credentials**, select **Add credential**
- For **Federated credential scenario**, select **GitHub Actions deploying Azure resources**
- Fill in the following fields
  - **Organization**: Your GitHub organization name or GitHub username. E.g. `jasonf`
  - **Repository**: Your GitHub repository name (from the previous step). E.g. `maester-monitoring`
  - **Entity type**: `Branch`
  - **GitHub branch name**: `main`
  - **Credential details** > **Name**: E.g. `maester-devops`
- Select **Add**

### Create GitHub secrets

- Open your Maester GitHub repository and go to **Settings**
- Select **Security** > **Secrets and variables** > **Actions**
- Add the secrets listed below by selecting **New repository secret**
- To look up these values you will need to use the Entra portal, open the application you created earlier and copy the following values from the **Overview** page:
  - Name: **AZURE_TENANT_ID**, Value: The Directory (tenant) ID of the Entra tenant
  - Name: **AZURE_CLIENT_ID**, Value: The Application (client) ID of the Entra application you created
- Save each secret by selecting **Add secret**.

<EnableGitHubActionsCreateWorkflow/>

```yaml
name: Maester Daily Tests

on:
  push:
    branches: ["main"]
  # Run once a day at midnight
  schedule:
    - cron: "0 0 * * *"
  # Allows to run this workflow manually from the Actions tab
  workflow_dispatch:

permissions:
      id-token: write
      contents: read
      checks: write

jobs:
  run-maester-tests:
    name: Run Maester Tests
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Set current date as env variable
      run: echo "NOW=$(date +'%Y-%m-%d-T%H%M%S')" >> $GITHUB_ENV
    - name: 'Az CLI login'
      uses: azure/login@v2
      with:
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          allow-no-subscriptions: true
    - name: Run Maester
      uses: azure/powershell@v2
      with:
        inlineScript: |
          # Get Token
          $token = az account get-access-token --resource-type ms-graph

          # Connect to Microsoft Graph
          $accessToken = ($token | ConvertFrom-Json).accessToken | ConvertTo-SecureString -AsPlainText -Force
          Connect-MgGraph -AccessToken $accessToken

          # Install Maester (the built-in tests ship inside the module)
          Install-Module Maester -Force

          # Run Maester tests: -Path adds the custom tests and maester-config.json in this repository, if any
          $results = Invoke-Maester -Path . -Verbosity None -OutputFolder test-results -OutputFolderFileName "test-results" -PassThru

          # Add step summary
          $summary = Get-Content test-results/test-results.md
          Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value $summary

          # Flag status to GitHub - Uncomment the block below to fail the build if tests fail
          #if ($results.Result -ne 'Passed'){
          #  Write-Error "Status = $($results.Result): See Maester Test Report below for details."
          #}
        azPSVersion: "latest"

    - name: Archive Maester Html Report
      uses: actions/upload-artifact@v4
      if: always()
      with:
        name: maester-test-results-${{ env.NOW }}
        path: test-results
```

  </TabItem>
  <TabItem label="Custom workflow using Client secret">

:::caution
Client secret authentication is not recommended for long-term automation, and we recommend using workload identity federation when possible. Client secrets expire, require manual rotation and manual updates in your pipeline/repository variables.
:::

<CreateEntraApp/>

<CreateEntraClientSecret/>

### Create GitHub secrets

- Open your Maester GitHub repository and go to **Settings**
- Select **Security** > **Secrets and variables** > **Actions**
- Add the three secrets listed below by selecting **New repository secret**
- To look up these values you will need to use the Entra portal, open the application you created earlier and copy the following values from the **Overview** page:
  - Name: **AZURE_TENANT_ID**, Value: The Directory (tenant) ID of the Entra tenant
  - Name: **AZURE_CLIENT_ID**, Value: The Application (client) ID of the Entra application you created
  - Name: **AZURE_CLIENT_SECRET**, Value: The client secret you copied in the previous step
- Save each secret by selecting **Add secret**.

<EnableGitHubActionsCreateWorkflow/>

```yaml
name: Maester Daily Tests

on:
  push:
    branches: ["main"]
  # Run once a day at midnight
  schedule:
    - cron: "0 0 * * *"
  # Allows to run this workflow manually from the Actions tab
  workflow_dispatch:

permissions:
      id-token: write
      contents: read
      checks: write

jobs:
  run-maester-tests:
    name: Run Maester Tests
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Set current date as env variable
      run: echo "NOW=$(date +'%Y-%m-%d-T%H%M%S')" >> $GITHUB_ENV
    - name: Run Maester
      shell: pwsh
      env:
        TENANTID: ${{ secrets.AZURE_TENANT_ID }}
        CLIENTID: ${{ secrets.AZURE_CLIENT_ID }}
        CLIENTSECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
      run: |
        # Connect to Microsoft Graph
        $clientSecret = ConvertTo-SecureString -AsPlainText $env:CLIENTSECRET -Force
        [pscredential]$clientSecretCredential = New-Object System.Management.Automation.PSCredential($env:CLIENTID, $clientSecret)
        Connect-MgGraph -TenantId $env:TENANTID -ClientSecretCredential $clientSecretCredential

        # Install Maester (the built-in tests ship inside the module)
        Install-Module Maester -Force

        # Run Maester tests: -Path adds the custom tests and maester-config.json in this repository, if any
        $results = Invoke-Maester -Path . -Verbosity None -OutputFolder test-results -OutputFolderFileName "test-results" -PassThru

        # Add step summary
        $summary = Get-Content test-results/test-results.md
        Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value $summary

        # Flag status to GitHub - Uncomment the block below to fail the build if tests fail
        #if ($results.Result -ne 'Passed'){
        #  Write-Error "Status = $($results.Result): See Maester Test Report below for details."
        #}

    - name: Archive Maester Html Report
      uses: actions/upload-artifact@v4
      if: always()
      with:
        name: maester-test-results-${{ env.NOW }}
        path: test-results
```

:::tip
- If your custom tests include [Pester-format tests](../writing-tests/pester-format-tests.md) (`*.Tests.ps1`), add `Install-Module Pester -MinimumVersion 5.7.1 -Force` before `Invoke-Maester`. Pester is not needed for the built-in tests.
- To publish NUnit XML test results (for example with a test reporter action), add `-PesterConfiguration @{ TestResult = @{ Enabled = $true; OutputPath = 'test-results/test-results.xml'; OutputFormat = 'NUnitXml' } }` to `Invoke-Maester`, or set `Output.TestResult` in `maester-config.json`. Maester writes the file itself; Pester is not required.
- Use `-Product`, `-Feature`, `-Area`, `-Severity`, `-Suite`, `-TestId`, `-ExcludeTestId`, `-IncludePreview` or `-IncludeLongRunning` to choose which tests run, and `-DryRun` to see what would run.
:::

### Step-by-step video tutorial

<iframe width="686" height="386" src="https://www.youtube.com/embed/SzIxCQg6CWA" title="Maester Github Actions integration" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

  </TabItem>
  </Tabs>

## Manually running the Maester tests

To manually run the Maester tests workflow

- Open your Maester GitHub repository and go to **Actions**
- Select **Maester Daily Tests** from the left pane
- Select **Run workflow** drop-down from the right pane
- Select **Run workflow** button to start the workflow
- Select the running workflow to view the status

## Viewing test results

- Open your Maester GitHub repository and go to **Actions**
- Select a workflow run to view the results e.g. `Maester Daily Tests`

### Summary view

The summary view shows the status of the workflow run, the duration, and the number of tests that passed, failed, and were skipped.

![Screenshot of GitHub worklflow run summary Page](assets/github-summary-page.png)

### Maester report

The detailed Maester report can be downloaded by selecting the **maester-test-results...** file from the **Artifacts** section and opening the `test-results.html` page.

![Screenshot of the downloaded Maester report](assets/github-maester-report.png)

### Summary Maester report

A detailed summary of the Maester report can be viewed by scrolling down **Summary** page.

![Screenshot of Maester report in the GitHub Summary Page](assets/github-summary-details.png)

### Logs view

Select the **Run Maester Tests** job from **Jobs** in the left pane to view the raw logs.

![Screenshot of GitHub workflow logs page](assets/github-logs-page.png)

## Keeping your Maester tests up to date

The Maester team adds new tests over time. The built-in tests ship inside the Maester PowerShell module, so getting new tests only means using a newer module:

- The workflows above install the latest Maester release on every run (`maester_version: latest` for the action, `Install-Module Maester -Force` for the custom workflows), so new tests are picked up automatically.
- To control when you upgrade, pin a version instead, for example `maester_version: 3.0.0` or `Install-Module Maester -RequiredVersion 3.0.0 -Force`, and raise it when you are ready.

There is nothing to update in your repository: it holds only the workflow and, if you added them, your configuration and custom tests. If you set up this repository with Maester 2.x by copying the `maester-tests` repository, see [Upgrading from 2.x](../upgrading-from-2x.md) to clean it up.
