GitHub Actions
A GitHub Actions run can ask GitHub for a short-lived token that says which repository, branch, and workflow it is. A service principal can trust that token. The run trades it for a platform token, and GitHub stores no platform secret at all.
1. Create the service principal
Section titled “1. Create the service principal”In the console, open IAM → App registrations → New application.
- Kind: Service principal (API access)
- Name: something that says where it runs, for example
shop-github
The console shows a client secret once. A GitHub workflow does not need it.
2. Give it a role on one service
Section titled “2. Give it a role on one service”Open the principal. Under Access, add Editor with the scope set to the one service the workflow deploys. Click Save access.
That is all the workflow can do. It cannot deploy another service, change the directory, or rename the tenant.
3. Trust the repository
Section titled “3. Trust the repository”Under Federated sign-in, keep the GitHub preset:
| Field | Example |
|---|---|
| Repository | acme/shop |
| Runs on | Branch |
| Branch | main |
The console shows the subject it will trust,
repo:acme/shop:ref:refs/heads/main. Click Trust.
The subject must match exactly. There are no wildcards. A run on another
branch, a pull request, or a fork gets a different subject and is refused.
To deploy from a GitHub environment instead, pick Environment. The
subject is then repo:acme/shop:environment:production, and the
environment’s protection rules apply.
The audience defaults to https://api.m12p.com, which is what the CLI asks
GitHub for.
4. Set the repository variables
Section titled “4. Set the repository variables”In GitHub, open Settings → Secrets and variables → Actions → Variables. None of these is secret:
| Variable | Value |
|---|---|
PLATFORM_TENANT | The tenant id, tnt_... |
PLATFORM_CLIENT_ID | The principal’s client id, client_... |
PLATFORM_SERVICE | The service id, service_... |
5. Deploy from the workflow
Section titled “5. Deploy from the workflow”permissions: contents: read id-token: write
jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 # ... build and push the images ... - name: Deploy env: PLATFORM_REST: https://api.m12p.com PLATFORM_TENANT: ${{ vars.PLATFORM_TENANT }} PLATFORM_CLIENT_ID: ${{ vars.PLATFORM_CLIENT_ID }} PLATFORM_FEDERATION: github run: | base=https://developers.m12p.com/downloads/cli curl -fsSLO "$base/platform-linux-amd64" && curl -fsSLO "$base/SHA256SUMS" grep ' platform-linux-amd64$' SHA256SUMS | sha256sum -c - chmod +x platform-linux-amd64 ./platform-linux-amd64 service deploy "${{ vars.PLATFORM_SERVICE }}" \ --compose docker-compose.paas.yml --waitid-token: write lets the job ask GitHub for its token. It does not give the
job write access to the repository.
--compose replaces the Compose file saved on the service with the one in
the repository, then deploys it. --wait follows the deployment. If it
fails, the step prints why and fails too.
What is checked
Section titled “What is checked”The platform takes a GitHub token only when all of this holds:
- The issuer is
https://token.actions.githubusercontent.com, which the platform trusts. - GitHub’s signature is valid. The keys come from GitHub over HTTPS.
- The token has not expired, allowing one minute of clock difference.
- The audience and the subject equal the federated credential’s.
- The client id is a service principal of that tenant.
Each exchange is written to the tenant’s audit log as iam.token.federated,
with the repository, ref, commit, workflow, run, and the GitHub user who
started it. The token itself is never stored.
Move off a deploy hook
Section titled “Move off a deploy hook”If the repository deployed through a deploy hook before:
- Push the workflow above and check that a run deploys.
- Delete the hook secret from the repository, for example
PAAS_WEBHOOK_URL. - Turn the hook off: the service’s Deployments tab, Turn off.