Skip to content

Commit d7819cb

Browse files
🚀 Add KeyVaultKeyReference parameter for GitHub App authentication (#63)
This PR adds support for authenticating GitHub Apps using Azure KeyVault key references, enabling secure private key storage in Azure KeyVault instead of GitHub secrets. ## Changes Made ### 🔧 Core Implementation - **action.yml**: Added new `KeyVaultKeyReference` input parameter with proper description and environment variable mapping - **scripts/init.ps1**: - Added validation logic ensuring mutual exclusion between `PrivateKey` and `KeyVaultKeyReference` - Added requirement validation that `ClientID` must be provided with exactly one key parameter - Implemented new authentication path: `Connect-GitHub -ClientID <ClientID> -KeyVaultKeyReference <url>` - Updated module status reporting to include KeyVault key reference status ### 📚 Documentation - **README.md**: - Added `KeyVaultKeyReference` to inputs documentation table - Added comprehensive Example 5 demonstrating Azure KeyVault authentication workflow - Updated example numbering consistently - Included note about required `azure/login` action for KeyVault authentication ## Usage Example ```yaml jobs: Run-Script: runs-on: ubuntu-latest steps: - name: Login to Azure uses: azure/login@v1 with: creds: ${{ secrets.AZURE_CREDENTIALS }} - name: Run script uses: PSModule/GitHub-Script@v1 with: ClientID: ${{ secrets.CLIENT_ID }} KeyVaultKeyReference: ${{ secrets.KEYVAULT_KEY_REFERENCE }} Script: | LogGroup "Get-GitHubApp" { Get-GitHubApp } ``` ## Validation The implementation includes comprehensive validation: - ✅ Mutual exclusion: Only one of `PrivateKey` or `KeyVaultKeyReference` can be provided - ✅ Requirement validation: `ClientID` must be provided with exactly one key parameter - ✅ Error handling for invalid input combinations - ✅ PowerShell and YAML syntax validation - ✅ Integration testing with 6/6 test scenarios passing ## Authentication Flow The action now supports three authentication methods: 1. **Token**: `Token` parameter (existing) 2. **GitHub App with Private Key**: `ClientID` + `PrivateKey` (existing) 3. **GitHub App with KeyVault**: `ClientID` + `KeyVaultKeyReference` (new) Fixes #62. <!-- START COPILOT CODING AGENT TIPS --> --- 💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click [here](https://survey.alchemer.com/s3/8343779/Copilot-Coding-agent) to start the survey. --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: MariusStorhaug <[email protected]> Co-authored-by: Marius Storhaug <[email protected]>
1 parent cc12067 commit d7819cb

File tree

6 files changed

+167
-10
lines changed

6 files changed

+167
-10
lines changed

.github/workflows/Action-Test-Prerelease.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ concurrency:
1212
permissions:
1313
contents: read
1414
pull-requests: read
15+
id-token: write
1516

1617
jobs:
1718
ActionTest:

.github/workflows/Action-Test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ concurrency:
1515
permissions:
1616
contents: read
1717
pull-requests: read
18+
id-token: write
1819

1920
jobs:
2021
ActionTest:

.github/workflows/TestWorkflow.yml

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,14 @@ on:
3737
TEST_APP_ORG_PRIVATE_KEY:
3838
description: Private Key for the test GitHub App for the organization
3939
required: true
40+
KEYVAULT_KEY_REFERENCE:
41+
description: Azure KeyVault key reference URL for GitHub App authentication
42+
required: true
4043

4144
permissions:
4245
contents: read
4346
pull-requests: read
47+
id-token: write
4448

4549
jobs:
4650
ActionTestBasic:
@@ -542,6 +546,104 @@ jobs:
542546
Get-GitHubConfig | Format-List | Out-String
543547
}
544548
549+
ActionTestWithKeyVaultKeyReference:
550+
name: WithKeyVaultKeyReference
551+
environment: azure
552+
runs-on: ${{ inputs.runs-on }}
553+
steps:
554+
# Need to check out as part of the test, as its a local action
555+
- name: Checkout repo
556+
uses: actions/checkout@v4
557+
558+
# Login to Azure to enable KeyVault access
559+
- name: Login to Azure
560+
uses: azure/login@v2
561+
with:
562+
client-id: ${{ vars.AZURE_CLIENT_ID }}
563+
tenant-id: ${{ vars.AZURE_TENANT_ID }}
564+
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
565+
allow-no-subscriptions: true
566+
567+
- name: Action-Test
568+
uses: ./
569+
with:
570+
ClientID: ${{ secrets.TEST_APP_ORG_CLIENT_ID }}
571+
KeyVaultKeyReference: 'https://psmodule-test-vault.vault.azure.net/keys/psmodule-org-app/569ae34250e64adca6a2b2d159d454a5'
572+
Prerelease: ${{ inputs.Prerelease }}
573+
Script: |
574+
LogGroup 'Context details' {
575+
Get-GitHubContext | Select-Object * | Out-String
576+
}
577+
578+
LogGroup 'Get-GitHubApp' {
579+
Get-GitHubApp | Format-List | Out-String
580+
}
581+
582+
LogGroup 'Get-GitHubAppInstallation' {
583+
Get-GitHubAppInstallation | Format-Table -AutoSize | Out-String
584+
}
585+
586+
LogGroup 'Connect to all installations of the app' {
587+
Connect-GitHubApp
588+
}
589+
590+
LogGroup 'Contexts' {
591+
Get-GitHubContext -ListAvailable | Format-Table -AutoSize | Out-String
592+
}
593+
594+
LogGroup 'GitHubConfig' {
595+
Get-GitHubConfig | Format-List | Out-String
596+
}
597+
598+
ActionTestWithKeyVaultKeyReferenceLatest:
599+
name: WithKeyVaultKeyReferenceLatest
600+
environment: azure
601+
runs-on: ${{ inputs.runs-on }}
602+
steps:
603+
# Need to check out as part of the test, as its a local action
604+
- name: Checkout repo
605+
uses: actions/checkout@v4
606+
607+
# Login to Azure to enable KeyVault access
608+
- name: Login to Azure
609+
uses: azure/login@v2
610+
with:
611+
client-id: ${{ vars.AZURE_CLIENT_ID }}
612+
tenant-id: ${{ vars.AZURE_TENANT_ID }}
613+
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
614+
allow-no-subscriptions: true
615+
616+
- name: Action-Test
617+
uses: ./
618+
with:
619+
ClientID: ${{ secrets.TEST_APP_ORG_CLIENT_ID }}
620+
KeyVaultKeyReference: 'https://psmodule-test-vault.vault.azure.net/keys/psmodule-org-app/'
621+
Prerelease: ${{ inputs.Prerelease }}
622+
Script: |
623+
LogGroup 'Context details' {
624+
Get-GitHubContext | Select-Object * | Out-String
625+
}
626+
627+
LogGroup 'Get-GitHubApp' {
628+
Get-GitHubApp | Format-List | Out-String
629+
}
630+
631+
LogGroup 'Get-GitHubAppInstallation' {
632+
Get-GitHubAppInstallation | Format-Table -AutoSize | Out-String
633+
}
634+
635+
LogGroup 'Connect to all installations of the app' {
636+
Connect-GitHubApp
637+
}
638+
639+
LogGroup 'Contexts' {
640+
Get-GitHubContext -ListAvailable | Format-Table -AutoSize | Out-String
641+
}
642+
643+
LogGroup 'GitHubConfig' {
644+
Get-GitHubConfig | Format-List | Out-String
645+
}
646+
545647
ActionTestPreserveCredentialsFalse:
546648
name: PreserveCredentials False
547649
runs-on: ${{ inputs.runs-on }}

README.md

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ To get started with your own GitHub PowerShell based action, [create a new repos
1515
| `Token` | Log in using an Installation Access Token (IAT). | false | `${{ github.token }}` |
1616
| `ClientID` | Log in using a GitHub App, with the App's Client ID and Private Key. | false | |
1717
| `PrivateKey` | Log in using a GitHub App, with the App's Client ID and Private Key. | false | |
18+
| `KeyVaultKeyReference` | Log in using a GitHub App, with the App's Client ID and KeyVault Key Reference. | false | |
1819
| `Debug` | Enable debug output for the whole action. | false | `'false'` |
1920
| `Verbose` | Enable verbose output for the whole action. | false | `'false'` |
2021
| `Version` | Specifies the exact version of the GitHub module to install. | false | |
@@ -176,7 +177,35 @@ jobs:
176177
}
177178
```
178179

179-
#### Example 5: Using outputs from the script
180+
#### Example 5: Run a GitHub PowerShell script with a GitHub App using a Client ID and KeyVault Key Reference
181+
182+
Runs a script that uses the GitHub PowerShell module with a GitHub App authenticated via Azure KeyVault. This example retrieves the GitHub App details.
183+
184+
> [!NOTE]
185+
> This authentication method requires the `azure/login` action to authenticate with Azure first. The KeyVault Key Reference should be a URL pointing to the private key stored in Azure KeyVault.
186+
187+
```yaml
188+
jobs:
189+
Run-Script:
190+
runs-on: ubuntu-latest
191+
steps:
192+
- name: Login to Azure
193+
uses: azure/login@v1
194+
with:
195+
creds: ${{ secrets.AZURE_CREDENTIALS }}
196+
197+
- name: Run script
198+
uses: PSModule/GitHub-Script@v1
199+
with:
200+
ClientID: ${{ secrets.CLIENT_ID }}
201+
KeyVaultKeyReference: ${{ secrets.KEYVAULT_KEY_REFERENCE }}
202+
Script: |
203+
LogGroup "Get-GitHubApp" {
204+
Get-GitHubApp
205+
}
206+
```
207+
208+
#### Example 6: Using outputs from the script
180209

181210
Runs a script that uses the GitHub PowerShell module and outputs the result.
182211

@@ -201,7 +230,7 @@ Runs a script that uses the GitHub PowerShell module and outputs the result.
201230
Write-GitHubNotice -Message $result.Zen -Title 'GitHub Zen'
202231
```
203232

204-
#### Example 6: Run a script with credential cleanup
233+
#### Example 7: Run a script with credential cleanup
205234

206235
Runs a script with `PreserveCredentials` set to `false` to automatically disconnect GitHub credentials after execution.
207236

action.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ inputs:
2323
PrivateKey:
2424
description: Log in using a GitHub App, using the App's Client ID and Private Key.
2525
required: false
26+
KeyVaultKeyReference:
27+
description: Log in using a GitHub App, using the App's Client ID and KeyVault Key Reference.
28+
required: false
2629
Debug:
2730
description: Enable debug output for the whole action.
2831
required: false
@@ -80,6 +83,7 @@ runs:
8083
PSMODULE_GITHUB_SCRIPT_INPUT_Token: ${{ inputs.Token }}
8184
PSMODULE_GITHUB_SCRIPT_INPUT_ClientID: ${{ inputs.ClientID }}
8285
PSMODULE_GITHUB_SCRIPT_INPUT_PrivateKey: ${{ inputs.PrivateKey }}
86+
PSMODULE_GITHUB_SCRIPT_INPUT_KeyVaultKeyReference: ${{ inputs.KeyVaultKeyReference }}
8387
PSMODULE_GITHUB_SCRIPT_INPUT_Debug: ${{ inputs.Debug }}
8488
PSMODULE_GITHUB_SCRIPT_INPUT_Verbose: ${{ inputs.Verbose }}
8589
PSMODULE_GITHUB_SCRIPT_INPUT_Version: ${{ inputs.Version }}

scripts/init.ps1

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,28 @@ process {
7878
$providedToken = -not [string]::IsNullOrEmpty($env:PSMODULE_GITHUB_SCRIPT_INPUT_Token)
7979
$providedClientID = -not [string]::IsNullOrEmpty($env:PSMODULE_GITHUB_SCRIPT_INPUT_ClientID)
8080
$providedPrivateKey = -not [string]::IsNullOrEmpty($env:PSMODULE_GITHUB_SCRIPT_INPUT_PrivateKey)
81+
$providedKeyVaultKeyReference = -not [string]::IsNullOrEmpty($env:PSMODULE_GITHUB_SCRIPT_INPUT_KeyVaultKeyReference)
82+
83+
# Validate mutual exclusion of PrivateKey and KeyVaultKeyReference
84+
if ($providedPrivateKey -and $providedKeyVaultKeyReference) {
85+
throw 'Only one of PrivateKey or KeyVaultKeyReference can be provided.'
86+
}
87+
88+
# Validate that if ClientID is provided, exactly one of PrivateKey or KeyVaultKeyReference is also provided
89+
if ($providedClientID -and -not ($providedPrivateKey -or $providedKeyVaultKeyReference)) {
90+
throw 'When ClientID is provided, either PrivateKey or KeyVaultKeyReference must also be provided.'
91+
}
92+
8193
$moduleStatus = [pscustomobject]@{
82-
Name = $Name
83-
Version = [string]::IsNullOrEmpty($Version) ? 'latest' : $Version
84-
Prerelease = $Prerelease
85-
'Already installed' = $null -ne $alreadyInstalled
86-
'Already imported' = $null -ne $alreadyImported
87-
'Provided Token' = $providedToken
88-
'Provided ClientID' = $providedClientID
89-
'Provided PrivateKey' = $providedPrivateKey
94+
Name = $Name
95+
Version = [string]::IsNullOrEmpty($Version) ? 'latest' : $Version
96+
Prerelease = $Prerelease
97+
'Already installed' = $null -ne $alreadyInstalled
98+
'Already imported' = $null -ne $alreadyImported
99+
'Provided Token' = $providedToken
100+
'Provided ClientID' = $providedClientID
101+
'Provided PrivateKey' = $providedPrivateKey
102+
'Provided KeyVaultKeyReference' = $providedKeyVaultKeyReference
90103
}
91104
if ($showInit) {
92105
Write-Output 'Module status:'
@@ -101,6 +114,13 @@ process {
101114
Silent = (-not $showInit)
102115
}
103116
Connect-GitHub @params
117+
} elseif ($providedClientID -and $providedKeyVaultKeyReference) {
118+
$params = @{
119+
ClientID = $env:PSMODULE_GITHUB_SCRIPT_INPUT_ClientID
120+
KeyVaultKeyReference = $env:PSMODULE_GITHUB_SCRIPT_INPUT_KeyVaultKeyReference
121+
Silent = (-not $showInit)
122+
}
123+
Connect-GitHub @params
104124
} elseif ($providedToken) {
105125
$params = @{
106126
Token = $env:PSMODULE_GITHUB_SCRIPT_INPUT_Token

0 commit comments

Comments
 (0)