Most of the management I do in Azure is automated with powershell and put into a GitHub Actions workflow or an automation runbook. Most of them use the same modules; Az.Accounts, Az.Resources, Microsoft.Graph.Authentication, etc.

Previously I’ve used the action psmodulecache. This has the benefit of generating a cache at the end of the workflow run, if no cache already exists. This way, any consecutive runs will use the cache instead of re-downloading the modules. This made a great speed increase when I introduced it.
GitHub has a dedicated doc about caching workflow dependencies.

Lately I’ve been made aware of JustinGrote’s ModuleFast repository and it’s GitHub Action. So I decided to pit them against each other.

During Justin Grote’s session at PSConfEU 2024 he mentions ModuleFast:

At PSConfEU 2023 he had a session where he goes in-depth about ModuleFast:

Workflows for testing

Methods to download modules from PSGallery:

  • Install-Module-cmdlet
  • psmodulecache
  • ModuleFast
  • Install-PSResource-cmdlet

I created a workflow for each of the methods and ran the workflows 5 times. Then I changed the list of modules to download and it two more times.

Install-Module cmdlet

name: Install-Module-cmdlet
run-name: ${{ github.workflow }} [${{ github.ref_name }}]
on:
  workflow_dispatch:
jobs:
  install-and-list-modules:
    runs-on: ubuntu-22.04
    steps:
      - name: Install Modules
        shell: pwsh
        run: |
          Install-Module -Name Az       -RequiredVersion 12.1.0 -Repository PSGallery -Force
          Install-Module -Name dbatools -RequiredVersion 2.1.14 -Repository PSGallery -Force          
      - name: List Available Modules
        shell: pwsh
        run: |
          Get-Module -ListAvailable | Select-Object Version, Name | Sort-Object Name          

psmodulecache

name: psmodulecache
...
    steps:
      - name: Install and cache PowerShell modules
        uses: potatoqualitee/psmodulecache@v6.2
        with:
          modules-to-cache: Az:12.1.0, dbatools:2.1.14
...

ModuleFast

name: ModuleFast
...
    steps:
      - name: ⚡ ModuleFast with Specification
        uses: JustinGrote/ModuleFast-action@v0.0.1
        with:
          specification: |
            Az=12.1.0
            dbatools=2.1.14            
...

Install-PSResource cmdlet

name: Install-PSResource-cmdlet
...
    steps:
      - name: Install Modules
        shell: pwsh
        run: |
          Install-PSResource -TrustRepository -RequiredResource @{
            'Az'       = @{ version = '12.1.0' ; repository = 'PSGallery' }
            'dbatools' = @{ version = '2.1.14' ; repository = 'PSGallery' }
          }          
...

Caches and log

If a cache isn’t found during a run, it will be generated as a post step:

cache generated as post step
cache generated as post step

If a cache is found it will be restored and the post step will log the cache hit.

cache hit
cache hit logged as post step

After a workflow has generated a cache then details can be found in the Caches section of GitHub Actions.

GitHub caches
caches from all workflows

Speed results

Modules installed on test system:

  • PackageManagement (v1.4.8.1)
  • PowerShellGet (v2.2.5)
  • Microsoft.PowerShell.PSResourceGet (v1.0.4.1)

Run 1-5 was with Az=12.1.0 and dbatools=2.1.14.
Run 6-7 was with Az=12.1.0 and dbatools=2.1.14 and Microsoft.Graph=2.0.0.

Run Install-Module psmodulecache ModuleFast Install-PSResource
#1* 48s 74s* 30s* 29s
#2 79s 54s 19s 30s
#3 82s 40s 25s 28s
#4 49s 61s 32s 42s
#5 46s 36s 10s 29s
#6* 53s 104s* 31s* 83s
#7 88s 53s 17s 80s
Avg 63.57s (100%) 60.28s (94.8%) 23.43s (36.9%) 45.86s (72.14%)

Run 1* and 6* with psmodulecache and ModuleFast is marked to signal the first run with a given set of modules, thus requiring downloading the modules and saving the cache.
That means the cache hit ratio was 71.43% (5/7).

Conclusion

The time reduction from Install-Module to ModuleFast is 63.14% in this test! The sample size should have been bigger, but I’ll definitively change some of my CI to use ModuleFast.

I original did this post in august 2024, but I never got around to publishing it so here it comes in july 2025 instead.