Aug. 6, 2025

Setting Up ALM for Power Platform with GitHub Actions

Setting Up ALM for Power Platform with GitHub Actions

This episode demystifies Power Platform ALM with GitHub Actions so you can see—and control—every step from source to prod. Learn why deployments fail (connector references, environment variables, and human-led imports), how to wire service principals and scoped secrets, and how to structure GitHub workflows (triggers, jobs, env-specific vaults) that validate, remap, and deploy solutions predictably. We cover source management with unpacked solutions, build-time checks, connector/variable remapping at deploy, and guardrails that stop silent breakages. If your Power Apps and flows “work in dev” but die in test or prod, this is your step-by-step playbook to ship reliable, auditable releases—without guesswork.

See every step. Control every secret. Ship the same app across dev → test → prod—on purpose.

What you’ll learn

  • Why Power Platform ALM breaks (hidden connector refs, env vars, drift, DLP)

  • How to structure GitHub Actions: triggers, jobs, environments, approvals

  • Secure identity & secrets: service principals, scoped env secrets, variable remapping

  • Build/test steps that catch issues early (solution validation, dependency checks)

  • A clean deploy pattern with rollback-ready artifacts

The ALM maze: what’s really different

  • Source ≠ code: Solutions bundle XML/JSON + hidden logic; changes aren’t visible until export.

  • Connectors are per-environment: References point to objects that don’t exist elsewhere by default.

  • Environment variables matter: Endpoints/keys must be swapped at deploy time, not baked in.

  • People-based deploys fail audits: Use service principals for consistency and traceability.


Core system: Source → Build → Test → Deploy (for Power Platform)

Source

  • Keep solutions in Git as unpacked (pac CLI) for diff-friendly PRs.

  • Commit both managed (release) and unmanaged (dev) exports; tag with solution version.

  • Store a /config folder per environment (env vars, connection ref mappings).

Build

  • Use GitHub Actions to pack/validate:

    • Unpack/pack with PAC CLI

    • Run Solution Checker (fail on criticals)

    • Generate dependency report (child flows, PCF, connection refs)

    • Publish artifacts: managed.zip + reports

Test

  • Spin up a test job against the target environment:

    • Verify env variables exist/are populated

    • Resolve connection refs to approved connectors

    • Optional “no-op” flow test runs (trigger test execution where safe)

Deploy

  • Import managed solution with service principal scoped to that environment.

  • Remap environment variables and connection references from /config/test|prod.

  • Post-deploy smoke checks (key flow run, app open, role access).


GitHub Actions: triggers, jobs, environments

Recommended triggers

  • push to feature/* → build & validate only

  • pull_request to main → full build + solution checker + artifact

  • workflow_dispatch on release/* → deploy to test

  • environment: production with required reviewers → deploy to prod

Job separation

  1. build: pack, validate, solution checker, publish artifacts

  2. prepare-env: fetch env config, map variables/refs

  3. deploy-test/prod: import managed, apply mappings, smoke checks

Environment protection

  • Use GitHub Environments with scoped secrets: DEV, TEST, PROD

  • Require approvals for PROD; restrict who can read those secrets

  • Never reuse secrets across envs; name them with env prefixes (e.g., PROD_DATAVERSE_URL)


Secrets, service principals, and connectors

Service principals

  • One app registration per tenant; one SP per environment (least privilege).

  • Grant Dataverse roles sized for ALM tasks; store client ID/secret per env as GitHub secrets.

  • Rotate secrets; prefer federated credentials if available.

Environment variables

  • Store endpoints, table names, feature flags; never hardcode.

  • Maintain /config/{env}/envvars.json; pipeline injects on import.

Connection references

  • Maintain /config/{env}/connections.json mapping solution refs → target connectors.

  • Validate existence & DLP compliance before import; fail fast if unresolved.


Guardrails that prevent “silent” breakage

  • Solution Checker gate (block on high/critical).

  • Dependency scan: ensure child flows/PCF/refs are included in the solution.

  • Config audit: ensure every required env var has a value in the target env.

  • DLP/Datasource check: block personal/unapproved connectors; enforce allow-lists.

  • Schema drift: compare Dataverse changes; require approval for destructive diffs.


Rollback & observability

Rollback

  • Always publish the previous managed.zip as an artifact; re-import on failure.

  • For severe issues, restore environment backup (Dataverse snapshot).

  • Practice restores in a sandbox; document timings and owners.

Observability

  • Log each step’s outputs (solution checker report, mapping summary, import result).

  • Emit deployment markers (run IDs, solution versions) to an audit table/dashboard.

  • Track KPIs: change failure rate, MTTR, % failures blocked pre-deploy.


Reference workflow outline (pseudo-YAML)

  • on: PR to main → build/validate; manual workflow_dispatch → deploy

  • jobs.build: checkout → pac unpack/pack → solution checker → publish artifacts

  • jobs.prepare-env: download artifacts → load /config/${{ env }} → validate mappings

  • jobs.deploy: use env-scoped secrets → pac auth (SP) → import managed → apply mappings → smoke tests


Common pitfalls (and fast fixes)

  • Works in dev, fails in test → missing connection ref mapping

    • Fix: add /config/{env}/connections.json; validation step blocks if unresolved.

  • Wrong endpoint in prod → env vars not swapped

    • Fix: map on import; forbid hardcoded endpoints via linter script.

  • Audit issues → human account used for deploy

    • Fix: switch to SPs; restrict env secrets with approvals.

  • Unpack/pack drift → only commit unpacked; rebuild managed in CI.

  • Half imports → use “stage for upgrade” and auto-rollback to last artifact on failure.


Quick-start checklist (this week)

  • Create SPs + GitHub envs (DEV/TEST/PROD) with scoped secrets

  • Unpack your solution into Git; add Solution Checker to PRs

  • Add /config/{env}/envvars.json and connections.json templates

  • Build job: pack → validate → publish managed.zip

  • Deploy jobs: import with SP, remap vars/refs, smoke-test, approvals for PROD

  • Save previous managed.zip as rollback artifact; test a rollback in sandbox