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
/configfolder 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
pushtofeature/*→ build & validate onlypull_requesttomain→ full build + solution checker + artifactworkflow_dispatchonrelease/*→ deploy to testenvironment: productionwith required reviewers → deploy to prod
Job separation
build: pack, validate, solution checker, publish artifacts
prepare-env: fetch env config, map variables/refs
deploy-test/prod: import managed, apply mappings, smoke checks
Environment protection
Use GitHub Environments with scoped secrets:
DEV,TEST,PRODRequire approvals for
PROD; restrict who can read those secretsNever 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.jsonmapping 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; manualworkflow_dispatch→ deployjobs.build: checkout → pac unpack/pack → solution checker → publish artifactsjobs.prepare-env: download artifacts → load/config/${{ env }}→ validate mappingsjobs.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.jsonandconnections.jsontemplatesBuild 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
🚀 Want to be part of m365.fm?
Then stop just listening… and start showing up.
👉 Connect with me on LinkedIn and let’s make something happen:
- 🎙️ Be a podcast guest and share your story
- 🎧 Host your own episode (yes, seriously)
- 💡 Pitch topics the community actually wants to hear
- 🌍 Build your personal brand in the Microsoft 365 space
This isn’t just a podcast — it’s a platform for people who take action.
🔥 Most people wait. The best ones don’t.
👉 Connect with me on LinkedIn and send me a message:
"I want in"
Let’s build something awesome 👊








