From 6291aa8c3b67ff00485a4b432f38e57d94cc7a8e Mon Sep 17 00:00:00 2001 From: Wes Haggard Date: Fri, 27 Feb 2026 13:25:44 -0800 Subject: [PATCH 1/5] Add action to use github app via github workflows --- eng/common/actions/login-to-github/action.yml | 92 +++++++++++++++++++ eng/common/scripts/login-to-github.ps1 | 19 +++- 2 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 eng/common/actions/login-to-github/action.yml diff --git a/eng/common/actions/login-to-github/action.yml b/eng/common/actions/login-to-github/action.yml new file mode 100644 index 0000000000..d222417c25 --- /dev/null +++ b/eng/common/actions/login-to-github/action.yml @@ -0,0 +1,92 @@ +# Login to GitHub - Composite Action +# +# Mints a GitHub App installation access token using Azure Key Vault signing. +# This action wraps eng/common/scripts/login-to-github.ps1 for use in GitHub +# Actions workflows. The same script is used by Azure DevOps pipelines via +# eng/common/pipelines/templates/steps/login-to-github.yml. +# +# IMPORTANT: This action requires Azure CLI to be pre-authenticated. +# You must call azure/login BEFORE this action in your workflow. +# This is because composite actions cannot call azure/login internally. +# +# Usage (single owner): +# jobs: +# my-job: +# permissions: +# id-token: write # Required for azure/login OIDC +# steps: +# # Step 1: Authenticate to Azure (required before this action) +# # azure-sdk-internal-AzureSDKEngKeyVault Secrets connection need to work with EngSys to add OIDC credentials +# - uses: azure/login@v2 +# with: +# client-id: 5786d1fb-187e-4ca9-9a81-ab89ea278986 +# tenant-id: 72f988bf-86f1-41af-91ab-2d7cd011db47 +# subscription-id: a18897a6-7e44-457d-9260-f2854c0aca42 +# +# # Step 2: Mint GitHub App token +# - uses: ./eng/common/actions/login-to-github +# with: +# token-owners: Azure +# +# # Step 3: Use the token (available as env var in all subsequent steps) +# - run: gh pr list --repo Azure/azure-sdk-tools +# env: +# GH_TOKEN: ${{ env.GH_TOKEN }} +# +# Usage (multiple owners): +# - uses: ./eng/common/actions/login-to-github +# with: +# token-owners: Azure,azure-sdk,MicrosoftDocs +# +# - run: gh pr list --repo Azure/azure-sdk-tools +# env: +# GH_TOKEN: ${{ env.GH_TOKEN_Azure }} +# +# Tokens are exported to GITHUB_ENV so all subsequent steps can reference +# them as ${{ env.GH_TOKEN }} (single owner) or ${{ env.GH_TOKEN_ }} +# (multiple owners). This matches the Azure DevOps behavior where tokens +# are set as pipeline variables. + +name: 'Login to GitHub' +description: 'Mint a GitHub App installation token via Azure Key Vault signing' + +inputs: + token-owners: + description: > + Comma-separated list of GitHub organizations or users for which to + obtain installation tokens (e.g. "Azure" or "Azure,azure-sdk"). + required: false + default: 'Azure' + variable-name-prefix: + description: > + Prefix for the exported variable name. With a single owner the + variable is named exactly this (default GH_TOKEN). With multiple + owners each variable is named _. + required: false + default: 'GH_TOKEN' + key-vault-name: + description: 'Azure Key Vault name containing the signing key' + required: false + default: 'azuresdkengkeyvault' + key-name: + description: 'Name of the RSA key in Key Vault' + required: false + default: 'azure-sdk-automation' + app-id: + description: 'GitHub App numeric ID' + required: false + default: '1086291' + +runs: + using: 'composite' + steps: + - shell: pwsh + run: | + $scriptPath = Join-Path "${{ github.action_path }}" ".." ".." "scripts" "login-to-github.ps1" + $owners = '${{ inputs.token-owners }}' -split ',' | ForEach-Object { $_.Trim() } + & $scriptPath ` + -KeyVaultName '${{ inputs.key-vault-name }}' ` + -KeyName '${{ inputs.key-name }}' ` + -GitHubAppId '${{ inputs.app-id }}' ` + -InstallationTokenOwners $owners ` + -VariableNamePrefix '${{ inputs.variable-name-prefix }}' diff --git a/eng/common/scripts/login-to-github.ps1 b/eng/common/scripts/login-to-github.ps1 index e911f31eb1..e5ff46dc2a 100644 --- a/eng/common/scripts/login-to-github.ps1 +++ b/eng/common/scripts/login-to-github.ps1 @@ -3,6 +3,9 @@ Mints a GitHub App installation access token using Azure Key Vault 'sign' (non-exportable key), and logs in the GitHub CLI by setting GH_TOKEN. + Works in both Azure DevOps pipelines and GitHub Actions workflows. + Requires Azure CLI to be pre-authenticated (via AzureCLI@2 in ADO, or azure/login in GH Actions). + .PARAMETER KeyVaultName Name of the Azure Key Vault containing the non-exportable RSA key. @@ -16,10 +19,13 @@ List of GitHub organizations or users for which to obtain installation tokens. .PARAMETER VariableNamePrefix - Name of the ADO variable to set when -SetPipelineVariable is used (default: GH_TOKEN). + Prefix for the exported variable name (default: GH_TOKEN). + With a single owner, exports as GH_TOKEN. With multiple owners, exports as GH_TOKEN_. .OUTPUTS - Writes minimal info to stdout. Token is placed in $env:GH_TOKEN if there is only one owner otherwise $env:GH_TOKEN_ for each owner. + Sets environment variables in the current process and exports them to the CI system: + - Azure DevOps: sets secret pipeline variables via ##vso logging commands + - GitHub Actions: writes to GITHUB_ENV / GITHUB_OUTPUT and masks the token #> [CmdletBinding()] @@ -167,12 +173,19 @@ foreach ($InstallationTokenOwner in $InstallationTokenOwners) # Export for gh CLI & git Write-Host "$variableName has been set in the current process." - # Optionally set an Azure DevOps secret variable (so later tasks can reuse it) + # Azure DevOps: set secret pipeline variable (so later tasks can reuse it) if ($null -ne $env:SYSTEM_TEAMPROJECTID) { Write-Host "##vso[task.setvariable variable=$variableName;issecret=true]$installationToken" Write-Host "Azure DevOps variable '$variableName' has been set (secret)." } + # GitHub Actions: mask the token and export to GITHUB_ENV + if ($env:GITHUB_ACTIONS -eq 'true') { + Write-Host "::add-mask::$installationToken" + Add-Content -Path $env:GITHUB_ENV -Value "$variableName=$installationToken" + Write-Host "GitHub Actions env variable '$variableName' has been exported." + } + try { Write-Host "`n--- gh auth status ---" $gh_token_value_before = $env:GH_TOKEN From dd9660dfb4b5020e445f96276d61ccc094538e58 Mon Sep 17 00:00:00 2001 From: Wes Haggard Date: Fri, 27 Feb 2026 13:32:03 -0800 Subject: [PATCH 2/5] Add note about environment --- eng/common/actions/login-to-github/action.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/eng/common/actions/login-to-github/action.yml b/eng/common/actions/login-to-github/action.yml index d222417c25..53e4c6bf3b 100644 --- a/eng/common/actions/login-to-github/action.yml +++ b/eng/common/actions/login-to-github/action.yml @@ -12,11 +12,14 @@ # Usage (single owner): # jobs: # my-job: +# # An environment is required for OIDC (federated credential) login. +# # Work with EngSys to configure the environment with the federated +# # credential for the AzureSDKEngKeyVault Secrets service connection. +# environment: AzureSDKEngKeyVault # permissions: # id-token: write # Required for azure/login OIDC # steps: # # Step 1: Authenticate to Azure (required before this action) -# # azure-sdk-internal-AzureSDKEngKeyVault Secrets connection need to work with EngSys to add OIDC credentials # - uses: azure/login@v2 # with: # client-id: 5786d1fb-187e-4ca9-9a81-ab89ea278986 From 1367e53f725e7d85fa21421c813b7c1e8fce5889 Mon Sep 17 00:00:00 2001 From: Wes Haggard Date: Fri, 27 Feb 2026 13:33:49 -0800 Subject: [PATCH 3/5] Use env variables to help prevent injection attacks --- eng/common/actions/login-to-github/action.yml | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/eng/common/actions/login-to-github/action.yml b/eng/common/actions/login-to-github/action.yml index 53e4c6bf3b..679be38280 100644 --- a/eng/common/actions/login-to-github/action.yml +++ b/eng/common/actions/login-to-github/action.yml @@ -84,12 +84,19 @@ runs: using: 'composite' steps: - shell: pwsh + env: + INPUT_TOKEN_OWNERS: ${{ inputs.token-owners }} + INPUT_VARIABLE_NAME_PREFIX: ${{ inputs.variable-name-prefix }} + INPUT_KEY_VAULT_NAME: ${{ inputs.key-vault-name }} + INPUT_KEY_NAME: ${{ inputs.key-name }} + INPUT_APP_ID: ${{ inputs.app-id }} + ACTION_PATH: ${{ github.action_path }} run: | - $scriptPath = Join-Path "${{ github.action_path }}" ".." ".." "scripts" "login-to-github.ps1" - $owners = '${{ inputs.token-owners }}' -split ',' | ForEach-Object { $_.Trim() } + $scriptPath = Join-Path $env:ACTION_PATH ".." ".." "scripts" "login-to-github.ps1" + $owners = $env:INPUT_TOKEN_OWNERS -split ',' | ForEach-Object { $_.Trim() } & $scriptPath ` - -KeyVaultName '${{ inputs.key-vault-name }}' ` - -KeyName '${{ inputs.key-name }}' ` - -GitHubAppId '${{ inputs.app-id }}' ` + -KeyVaultName $env:INPUT_KEY_VAULT_NAME ` + -KeyName $env:INPUT_KEY_NAME ` + -GitHubAppId $env:INPUT_APP_ID ` -InstallationTokenOwners $owners ` - -VariableNamePrefix '${{ inputs.variable-name-prefix }}' + -VariableNamePrefix $env:INPUT_VARIABLE_NAME_PREFIX From 49679538257231f116d6c8a73568a9d3488ff136 Mon Sep 17 00:00:00 2001 From: Wes Haggard Date: Fri, 27 Feb 2026 13:35:02 -0800 Subject: [PATCH 4/5] Remove note about outputs --- eng/common/scripts/login-to-github.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/common/scripts/login-to-github.ps1 b/eng/common/scripts/login-to-github.ps1 index e5ff46dc2a..0880611c4e 100644 --- a/eng/common/scripts/login-to-github.ps1 +++ b/eng/common/scripts/login-to-github.ps1 @@ -25,7 +25,7 @@ .OUTPUTS Sets environment variables in the current process and exports them to the CI system: - Azure DevOps: sets secret pipeline variables via ##vso logging commands - - GitHub Actions: writes to GITHUB_ENV / GITHUB_OUTPUT and masks the token + - GitHub Actions: writes to GITHUB_ENV and masks the token #> [CmdletBinding()] From 8cea02b81600e0a6ba20823b74fc469487dcf8a1 Mon Sep 17 00:00:00 2001 From: Wes Haggard Date: Fri, 27 Feb 2026 13:44:44 -0800 Subject: [PATCH 5/5] Add test workflow for login-to-github --- eng/common/scripts/login-to-github.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/common/scripts/login-to-github.ps1 b/eng/common/scripts/login-to-github.ps1 index 0880611c4e..129946a409 100644 --- a/eng/common/scripts/login-to-github.ps1 +++ b/eng/common/scripts/login-to-github.ps1 @@ -104,7 +104,7 @@ function New-GitHubAppJwt { --digest $Base64Value | ConvertFrom-Json if ($LASTEXITCODE -ne 0) { - throw "Failed to sign JWT with Azure Key Vault. Error: $SignResult" + throw "Failed to sign JWT with Azure Key Vault. Error: $($SignResultJson | ConvertTo-Json -Compress)" } if (!$SignResultJson.signature) {