diff --git a/.github/workflows/Action-Test-Prerelease.yml b/.github/workflows/Action-Test-Prerelease.yml index eb1c725..89122f0 100644 --- a/.github/workflows/Action-Test-Prerelease.yml +++ b/.github/workflows/Action-Test-Prerelease.yml @@ -12,6 +12,7 @@ concurrency: permissions: contents: read pull-requests: read + id-token: write jobs: ActionTest: diff --git a/.github/workflows/Action-Test.yml b/.github/workflows/Action-Test.yml index 77b6918..7aaed5b 100644 --- a/.github/workflows/Action-Test.yml +++ b/.github/workflows/Action-Test.yml @@ -15,6 +15,7 @@ concurrency: permissions: contents: read pull-requests: read + id-token: write jobs: ActionTest: diff --git a/.github/workflows/TestWorkflow.yml b/.github/workflows/TestWorkflow.yml index a0efac8..579c6cc 100644 --- a/.github/workflows/TestWorkflow.yml +++ b/.github/workflows/TestWorkflow.yml @@ -37,10 +37,14 @@ on: TEST_APP_ORG_PRIVATE_KEY: description: Private Key for the test GitHub App for the organization required: true + KEYVAULT_KEY_REFERENCE: + description: Azure KeyVault key reference URL for GitHub App authentication + required: true permissions: contents: read pull-requests: read + id-token: write jobs: ActionTestBasic: @@ -542,6 +546,104 @@ jobs: Get-GitHubConfig | Format-List | Out-String } + ActionTestWithKeyVaultKeyReference: + name: WithKeyVaultKeyReference + environment: azure + runs-on: ${{ inputs.runs-on }} + steps: + # Need to check out as part of the test, as its a local action + - name: Checkout repo + uses: actions/checkout@v4 + + # Login to Azure to enable KeyVault access + - name: Login to Azure + uses: azure/login@v2 + with: + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} + allow-no-subscriptions: true + + - name: Action-Test + uses: ./ + with: + ClientID: ${{ secrets.TEST_APP_ORG_CLIENT_ID }} + KeyVaultKeyReference: 'https://psmodule-test-vault.vault.azure.net/keys/psmodule-org-app/569ae34250e64adca6a2b2d159d454a5' + Prerelease: ${{ inputs.Prerelease }} + Script: | + LogGroup 'Context details' { + Get-GitHubContext | Select-Object * | Out-String + } + + LogGroup 'Get-GitHubApp' { + Get-GitHubApp | Format-List | Out-String + } + + LogGroup 'Get-GitHubAppInstallation' { + Get-GitHubAppInstallation | Format-Table -AutoSize | Out-String + } + + LogGroup 'Connect to all installations of the app' { + Connect-GitHubApp + } + + LogGroup 'Contexts' { + Get-GitHubContext -ListAvailable | Format-Table -AutoSize | Out-String + } + + LogGroup 'GitHubConfig' { + Get-GitHubConfig | Format-List | Out-String + } + + ActionTestWithKeyVaultKeyReferenceLatest: + name: WithKeyVaultKeyReferenceLatest + environment: azure + runs-on: ${{ inputs.runs-on }} + steps: + # Need to check out as part of the test, as its a local action + - name: Checkout repo + uses: actions/checkout@v4 + + # Login to Azure to enable KeyVault access + - name: Login to Azure + uses: azure/login@v2 + with: + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} + allow-no-subscriptions: true + + - name: Action-Test + uses: ./ + with: + ClientID: ${{ secrets.TEST_APP_ORG_CLIENT_ID }} + KeyVaultKeyReference: 'https://psmodule-test-vault.vault.azure.net/keys/psmodule-org-app/' + Prerelease: ${{ inputs.Prerelease }} + Script: | + LogGroup 'Context details' { + Get-GitHubContext | Select-Object * | Out-String + } + + LogGroup 'Get-GitHubApp' { + Get-GitHubApp | Format-List | Out-String + } + + LogGroup 'Get-GitHubAppInstallation' { + Get-GitHubAppInstallation | Format-Table -AutoSize | Out-String + } + + LogGroup 'Connect to all installations of the app' { + Connect-GitHubApp + } + + LogGroup 'Contexts' { + Get-GitHubContext -ListAvailable | Format-Table -AutoSize | Out-String + } + + LogGroup 'GitHubConfig' { + Get-GitHubConfig | Format-List | Out-String + } + ActionTestPreserveCredentialsFalse: name: PreserveCredentials False runs-on: ${{ inputs.runs-on }} diff --git a/README.md b/README.md index 28d87ad..d1adf3c 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ To get started with your own GitHub PowerShell based action, [create a new repos | `Token` | Log in using an Installation Access Token (IAT). | false | `${{ github.token }}` | | `ClientID` | Log in using a GitHub App, with the App's Client ID and Private Key. | false | | | `PrivateKey` | Log in using a GitHub App, with the App's Client ID and Private Key. | false | | +| `KeyVaultKeyReference` | Log in using a GitHub App, with the App's Client ID and KeyVault Key Reference. | false | | | `Debug` | Enable debug output for the whole action. | false | `'false'` | | `Verbose` | Enable verbose output for the whole action. | false | `'false'` | | `Version` | Specifies the exact version of the GitHub module to install. | false | | @@ -176,7 +177,35 @@ jobs: } ``` -#### Example 5: Using outputs from the script +#### Example 5: Run a GitHub PowerShell script with a GitHub App using a Client ID and KeyVault Key Reference + +Runs a script that uses the GitHub PowerShell module with a GitHub App authenticated via Azure KeyVault. This example retrieves the GitHub App details. + +> [!NOTE] +> 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. + +```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 + } +``` + +#### Example 6: Using outputs from the script Runs a script that uses the GitHub PowerShell module and outputs the result. @@ -201,7 +230,7 @@ Runs a script that uses the GitHub PowerShell module and outputs the result. Write-GitHubNotice -Message $result.Zen -Title 'GitHub Zen' ``` -#### Example 6: Run a script with credential cleanup +#### Example 7: Run a script with credential cleanup Runs a script with `PreserveCredentials` set to `false` to automatically disconnect GitHub credentials after execution. diff --git a/action.yml b/action.yml index eb5fa1f..a4614ec 100644 --- a/action.yml +++ b/action.yml @@ -23,6 +23,9 @@ inputs: PrivateKey: description: Log in using a GitHub App, using the App's Client ID and Private Key. required: false + KeyVaultKeyReference: + description: Log in using a GitHub App, using the App's Client ID and KeyVault Key Reference. + required: false Debug: description: Enable debug output for the whole action. required: false @@ -80,6 +83,7 @@ runs: PSMODULE_GITHUB_SCRIPT_INPUT_Token: ${{ inputs.Token }} PSMODULE_GITHUB_SCRIPT_INPUT_ClientID: ${{ inputs.ClientID }} PSMODULE_GITHUB_SCRIPT_INPUT_PrivateKey: ${{ inputs.PrivateKey }} + PSMODULE_GITHUB_SCRIPT_INPUT_KeyVaultKeyReference: ${{ inputs.KeyVaultKeyReference }} PSMODULE_GITHUB_SCRIPT_INPUT_Debug: ${{ inputs.Debug }} PSMODULE_GITHUB_SCRIPT_INPUT_Verbose: ${{ inputs.Verbose }} PSMODULE_GITHUB_SCRIPT_INPUT_Version: ${{ inputs.Version }} diff --git a/scripts/init.ps1 b/scripts/init.ps1 index 2cd7566..76663fd 100644 --- a/scripts/init.ps1 +++ b/scripts/init.ps1 @@ -78,15 +78,28 @@ process { $providedToken = -not [string]::IsNullOrEmpty($env:PSMODULE_GITHUB_SCRIPT_INPUT_Token) $providedClientID = -not [string]::IsNullOrEmpty($env:PSMODULE_GITHUB_SCRIPT_INPUT_ClientID) $providedPrivateKey = -not [string]::IsNullOrEmpty($env:PSMODULE_GITHUB_SCRIPT_INPUT_PrivateKey) + $providedKeyVaultKeyReference = -not [string]::IsNullOrEmpty($env:PSMODULE_GITHUB_SCRIPT_INPUT_KeyVaultKeyReference) + + # Validate mutual exclusion of PrivateKey and KeyVaultKeyReference + if ($providedPrivateKey -and $providedKeyVaultKeyReference) { + throw 'Only one of PrivateKey or KeyVaultKeyReference can be provided.' + } + + # Validate that if ClientID is provided, exactly one of PrivateKey or KeyVaultKeyReference is also provided + if ($providedClientID -and -not ($providedPrivateKey -or $providedKeyVaultKeyReference)) { + throw 'When ClientID is provided, either PrivateKey or KeyVaultKeyReference must also be provided.' + } + $moduleStatus = [pscustomobject]@{ - Name = $Name - Version = [string]::IsNullOrEmpty($Version) ? 'latest' : $Version - Prerelease = $Prerelease - 'Already installed' = $null -ne $alreadyInstalled - 'Already imported' = $null -ne $alreadyImported - 'Provided Token' = $providedToken - 'Provided ClientID' = $providedClientID - 'Provided PrivateKey' = $providedPrivateKey + Name = $Name + Version = [string]::IsNullOrEmpty($Version) ? 'latest' : $Version + Prerelease = $Prerelease + 'Already installed' = $null -ne $alreadyInstalled + 'Already imported' = $null -ne $alreadyImported + 'Provided Token' = $providedToken + 'Provided ClientID' = $providedClientID + 'Provided PrivateKey' = $providedPrivateKey + 'Provided KeyVaultKeyReference' = $providedKeyVaultKeyReference } if ($showInit) { Write-Output 'Module status:' @@ -101,6 +114,13 @@ process { Silent = (-not $showInit) } Connect-GitHub @params + } elseif ($providedClientID -and $providedKeyVaultKeyReference) { + $params = @{ + ClientID = $env:PSMODULE_GITHUB_SCRIPT_INPUT_ClientID + KeyVaultKeyReference = $env:PSMODULE_GITHUB_SCRIPT_INPUT_KeyVaultKeyReference + Silent = (-not $showInit) + } + Connect-GitHub @params } elseif ($providedToken) { $params = @{ Token = $env:PSMODULE_GITHUB_SCRIPT_INPUT_Token