GitHub Actions
Let an approved workflow call Metalhost without storing a long-lived Metalhost API key in GitHub.
What it does
A job obtains signed identity from GitHub. Metalhost checks it against an approved connection and returns a token valid for 15 minutes. The token can act only in the approved project with permissions allowed by both the connection and its service account.
The job still runs on GitHub-hosted runners or runners you manage. GitHub sign-in to the dashboard is a separate feature; signing in does not authorize a workflow.
Connect a workflow
- Open the target project's Developers → Service accounts. Create or choose an active account. For the example below, give it
monitoring.read. - Open Developers → GitHub Actions and start verification for that account.
- Copy the temporary verification step into the exact workflow/job you intend to authorize. Save the supplied pairing code as the temporary
METALHOST_SETUP_CODEGitHub Actions secret. Use the displayed API origin, setup ID and audience. - Run that workflow from the intended branch, event and environment. Verification expires after 30 minutes. It proves identity but does not grant project access yet.
- Return to Metalhost and review the observed repository, owner, workflow, branch, event and environment. Approve only the required permissions.
- Remove the temporary verification step and pairing secret. Replace the step with the exchange example below. Copy the approved connection's trust name and audience into GitHub Actions variables.
Keep the same workflow file, event and branch used during verification. If an environment or reusable workflow was included, keep those too. Renaming the workflow, changing the triggering event, or moving branches can require a newly verified connection. Pull-request events are not supported; accepted events are push, workflow_dispatch and schedule, subject to the connection's exact event match.
Example: read your VM inventory
This complete workflow requires bash, curl and jq, provided by the selected GitHub-hosted Ubuntu image. Verify it using workflow_dispatch from the intended branch. It lists at most 50 VMs without querying metrics; nextPageToken signals more results. Repeat with pageToken to paginate.
name: Read Metalhost monitoring
on:
workflow_dispatch:
permissions: {}
jobs:
monitoring:
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
id-token: write
env:
# Copy these non-secret values from the approved Metalhost connection.
METALHOST_ENDPOINT: ${{ vars.METALHOST_ENDPOINT }}
METALHOST_TRUST: ${{ vars.METALHOST_TRUST }}
METALHOST_AUDIENCE: ${{ vars.METALHOST_AUDIENCE }}
steps:
- name: Exchange GitHub identity and read VM inventory
shell: bash
run: |
set -euo pipefail
set +x
: "${METALHOST_ENDPOINT:?Set the API origin for your enabled environment}"
: "${METALHOST_TRUST:?Set the approved trust name}"
: "${METALHOST_AUDIENCE:?Set the connection audience}"
case "$METALHOST_ENDPOINT" in
https://*) ;;
*) echo 'An HTTPS API origin is required' >&2; exit 1 ;;
esac
audience=$(jq -rn --arg value "$METALHOST_AUDIENCE" '$value|@uri')
assertion=$(curl --fail --silent --show-error --max-time 30 \
-H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=$audience" | jq -er '.value')
echo "::add-mask::$assertion"
response=$(jq -nc --arg trust "$METALHOST_TRUST" --arg assertion "$assertion" \
'{trustName:$trust,assertion:$assertion}' |
curl --fail --silent --show-error --max-time 30 \
-H 'Content-Type: application/json' -H 'Connect-Protocol-Version: 1' \
--data-binary @- "$METALHOST_ENDPOINT/aes.iam.v1.AutomationService/ExchangeGitHubToken")
token=$(jq -er '.accessToken' <<<"$response")
echo "::add-mask::$token"
project=$(jq -er '.projectName' <<<"$response")
unset assertion response
# Requires monitoring.read. This request does not create or change VMs.
jq -nc --arg project "$project" \
'{projectName:$project,pageSize:50,inventoryOnly:true}' |
curl --fail --silent --show-error --max-time 30 \
-H "Authorization: Bearer $token" \
-H 'Content-Type: application/json' -H 'Connect-Protocol-Version: 1' \
--data-binary @- "$METALHOST_ENDPOINT/aes.monitoring.v1.MonitoringService/ListVMMonitoring" |
jq '{vms:[.vms[]? | {name,displayName,state}],nextPageToken}'
unset token
Download workflow. Set METALHOST_ENDPOINT to the API origin, not the portal URL, without a trailing slash. Set METALHOST_TRUST to the full approved trust name and METALHOST_AUDIENCE exactly as shown by Metalhost. These identifiers are not secrets.
This uses the HTTP API directly, so no CLI helper is required. The September CLI branch also implements metalhost auth github and metalhost monitoring; these are absent from older CLI releases. Use a matching September build and check its --help before using the CLI alternative.
Other uses
- Read monitoring during a deployment and evaluate your own readiness criteria.
- Manage alert rules using
monitoring.write. - Provision or remove test VMs using the compute API and explicitly granted compute permissions. This is a separate, potentially billable operation; the authentication step does not do it for you.
Scoped automation does not provide guest SSH access, interactive consoles, identity administration or arbitrary administrative RPCs. A workflow that deploys software inside a VM still needs a separate, deliberate guest-access mechanism.
Secrets and failures
- Never enable shell tracing, print the exchange response, or put tokens into artifacts or
GITHUB_ENV. The example masks tokens and keeps them inside one step. - A job needing access after 15 minutes must request fresh GitHub proof and exchange again. There is no refresh token.
- A denied exchange usually means the observed identity, audience, event or service-account state does not match. Inspect the connection, not the token contents in logs.
- Protect the approved repository and branch. An attacker able to edit an authorized workflow can exercise its granted permissions.
- Disable or delete a connection to stop its access. Removing a temporary pairing secret alone does not revoke an approved connection.
The job's id-token: write permission allows it to request GitHub identity; Metalhost permissions still come from the approved connection. See GitHub's OIDC reference.