TIL = Things, or Today I Learned

An experiment in Learning in Public.


Powershell on GitHub Actions - run Script as Admin on self-hosted Windows Runners

This came up when I wanted to run a powershell script with elevated Administrator privileges , sometimes on my dev latop and sometimes in a CI/CD process.

Running a script as an Administrator like that should alert you to proceed with caution.

As for CI/CD, I'm using GitHub Actions that run run on self-hosted runners on Windows. That means that I need somehow to see script's output in stdout.

The PowerShell Script

The script executes a command that requires admin rights. It's stored in the application's git repository.

# RunAsAdmin.ps1
if (Test-Path $LocalTestDir -PathType Container) {
  Write-Host "$LocalTestDir exists"
else {
  New-Item $LocalTestDir -ItemType Directory
Set-Content "$LocalTestDir\test.txt" 'Hello World'
Get-Content "$LocalTestDir\test.txt"

[System.IO.Directory]::GetAccessControl("\\.\pipe\docker_engine") | Format-Table

GitHub Actions workflow

The github action checks out the repository to access RunAsAdmin.ps1, executes the script, and checks the results.

name: Run as admin test
    - test-run-as-admin
  runs-on: [self-hosted, windows]
    - name: Checkout repo
      uses: actions/checkout@v2 # repo contains the RunAsAdmin.ps1
    - name: Run as Admin
      run: |
        echo "myWindowsID=$myWindowsID"
        $myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)
        echo "myWindowsPrincipal=$myWindowsPrincipal"
        New-Item -Path "$LocalTestDir\sysout.txt -ItemType File
        if ($myWindowsPrincipal.IsInRole($adminRole)) `
          echo "is admin" 
        } `
        else { `
          echo "is not admin"; `
          $passThruArgs = '-NoProfile -ExecutionPolicy Bypass -command', '&', "$env:GITHUB_WORKSPACE\RunAsAdmin.ps1", $arg, '*>', "`"$LocalTestDir\sysout.txt`""; `
          Start-Process powershell -Wait -Verb RunAs -ArgumentList $passThruArgs; `
          Get-Content "$LocalTestDir\sysout.txt"; `
        dir "C:\var"
    - name: check
      run: dir "C:\var"

With GitHub's ephemeral runners, the run environment and context, including the filesystem on which the job runs, disappears. The check step works because it's within the same job.

If you'd like to access C:\test\test.txt in another job, one option would be to upload it as an artifact so that it's accessible in another jobs.

Other useful references