Authentication
Each job authenticates with short-lived OIDC tokens issued by GitHub Actions for that run. The pull-request and merge contexts map to different identities:| Context | GitHub OIDC subject | AWS role | Synthetiq identity |
|---|---|---|---|
| Pull request | repo:<github-org>/<repo>:pull_request | Plan role (change-set verbs only) | None — generate doesn’t need one |
| Merge to main | repo:<github-org>/<repo>:ref:refs/heads/main | Apply role (full provisioning policy) | Service account holding infra:provision |
One-time setup
Create the AWS roles
Two IAM roles in the target account, trusting GitHub’s OIDC provider (
token.actions.githubusercontent.com), scoped to your repository:- synthetiq-infra-plan — trusted for
repo:<github-org>/<repo>:pull_request; policy fromsynthetiq infra permissions --stage generate - synthetiq-infra-apply — trusted for
repo:<github-org>/<repo>:ref:refs/heads/main; policy fromsynthetiq infra permissions --stage provision
Create the Synthetiq service account and trust
Find the role id for CI Provision Apply:Create the service account with that role:Create the OIDC trust so CI can authenticate as it:See Service Account.
The flow on every change
- Edit
_infra/synthetiq.yamlin a branch; open a PR. - The plan job runs
synthetiq infra generate: change sets are parked in your AWS account, the rendered CloudFormation template and changeset are committed to the branch, and a single PR comment (updated on later pushes) summarizes the impact — change counts by area and any replacements. The full template diff is in the PR’s Files changed. - Review and merge through your normal branch-protection process.
- The apply job runs
synthetiq infra provision, executing exactly the parked change set. If the stack moved since the plan, CloudFormation refuses it and the job fails asking for a re-plan. On the first provision, the job log ends with the three app DNS records to create. - Re-running with no config change is a green no-op — schedule the workflow for a free drift check.
For a second gate after merge, put the apply job behind a GitHub environment with required reviewers.
Staying current
Synthetiq publishes infrastructure updates as new CLI versions (all packages share one version number); an update can mean a diff to your stacks. An upgrade is just another pull request:- Pin
@synthetiq/cliin the infra repo (package.json+ lockfile, installed by both jobs). Never@latest— a floating version injects surprise diffs into unrelated PRs and lets the plan and apply jobs run different versions. - Upgrades arrive as version-bump PRs — point Renovate or Dependabot at the pin. Because
package.json/package-lock.jsonare in the workflow’s trigger paths, the bump alone runs the plan job on the new version, and the PR comment shows the exact diff that release causes in your account. - Review and merge like any other change; security patches are just fast merges.
- Same config + same version is a green no-op, so scheduled runs only go red for genuine drift.
provision refuses a changeset generated by a different CLI version (--allow-version-skew overrides).
Security model
- No stored credentials — AWS and Synthetiq access are per-run OIDC exchanges; tokens expire in minutes.
- Immediate revocation — delete the trust or demote the service account’s role and the next exchange fails.
- The AWS side is yours — your OIDC provider, roles, and trust policies; Synthetiq only specifies the policy contents.
- Fork PRs are inert — GitHub withholds OIDC tokens from fork pull requests.
- Audit trail — tokens minted through a trust carry the trust, issuer, and subject in their claims, so every CI action is attributable to the repository and ref that performed it.

