Skip to main content

GitHub Actions Quickstart

This guide shows you how to use PPDS ALM reusable workflows and composite actions in your GitHub Actions pipelines.

Prerequisites

  1. A GitHub repository with your Power Platform solution
  2. An Azure AD app registration for authentication
  3. GitHub repository secrets configured (see Authentication)

Quick Setup

1. Configure GitHub Environment

  1. Go to Settings > Environments > New environment
  2. Create environments for each target (e.g., Dev, QA, Prod)
  3. Add variables and secrets to each environment:

Variables:

VariableExample
POWERPLATFORM_ENVIRONMENT_URLhttps://myorg-qa.crm.dynamics.com/
POWERPLATFORM_TENANT_IDxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
POWERPLATFORM_CLIENT_IDxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Secrets:

SecretDescription
POWERPLATFORM_CLIENT_SECRETService principal client secret

2. Create Your First Workflow

Create .github/workflows/deploy-qa.yml:

name: Deploy to QA

on:
push:
branches: [develop]
workflow_dispatch:

jobs:
deploy:
uses: joshsmithxrm/ppds-alm/.github/workflows/solution-deploy.yml@v1
with:
solution-name: MySolution
solution-folder: solutions/MySolution/src
build-plugins: true
package-type: Managed
secrets: # Reusable workflow input - accepts both vars and secrets
environment-url: ${{ vars.POWERPLATFORM_ENVIRONMENT_URL }}
tenant-id: ${{ vars.POWERPLATFORM_TENANT_ID }}
client-id: ${{ vars.POWERPLATFORM_CLIENT_ID }}
client-secret: ${{ secrets.POWERPLATFORM_CLIENT_SECRET }} # Only this needs to be a secret

Available Workflows

Solution Workflows

WorkflowPurposeWhen to Use
solution-export.ymlExport from environmentNightly exports, on-demand sync
solution-import.ymlImport to environmentDirect import without build
solution-build.ymlBuild and packCI builds, artifact creation
solution-validate.ymlPR validationPull request checks
solution-deploy.ymlFull deploymentCD to QA/Prod

Plugin Workflows

WorkflowPurposeWhen to Use
plugin-deploy.ymlDeploy pluginsPlugin-only deployments
plugin-extract.ymlExtract registrationsGenerate registration file

Example Workflows

Nightly Export from Dev

name: Nightly Export

on:
schedule:
- cron: '0 2 * * *' # 2 AM UTC daily
workflow_dispatch:

jobs:
export:
uses: joshsmithxrm/ppds-alm/.github/workflows/solution-export.yml@v1
with:
solution-name: MySolution
solution-folder: solutions/MySolution/src
filter-noise: true
commit-changes: true
secrets:
environment-url: ${{ vars.POWERPLATFORM_ENVIRONMENT_URL }}
tenant-id: ${{ vars.POWERPLATFORM_TENANT_ID }}
client-id: ${{ vars.POWERPLATFORM_CLIENT_ID }}
client-secret: ${{ secrets.POWERPLATFORM_CLIENT_SECRET }}

PR Validation

name: PR Validation

on:
pull_request:
branches: [develop, main]

jobs:
validate:
uses: joshsmithxrm/ppds-alm/.github/workflows/solution-validate.yml@v1
with:
solution-name: MySolution
solution-folder: solutions/MySolution/src
build-code: true
run-tests: true
run-solution-checker: true
checker-fail-level: High
secrets:
environment-url: ${{ vars.POWERPLATFORM_ENVIRONMENT_URL }}
tenant-id: ${{ vars.POWERPLATFORM_TENANT_ID }}
client-id: ${{ vars.POWERPLATFORM_CLIENT_ID }}
client-secret: ${{ secrets.POWERPLATFORM_CLIENT_SECRET }}

Deploy to Production with Approval

name: Deploy to Production

on:
push:
branches: [main]
workflow_dispatch:

jobs:
deploy:
environment: Prod # Requires approval if configured
uses: joshsmithxrm/ppds-alm/.github/workflows/solution-deploy.yml@v1
with:
solution-name: MySolution
solution-folder: solutions/MySolution/src
build-plugins: true
package-type: Managed
skip-if-same-version: true
secrets:
environment-url: ${{ vars.POWERPLATFORM_ENVIRONMENT_URL }}
tenant-id: ${{ vars.POWERPLATFORM_TENANT_ID }}
client-id: ${{ vars.POWERPLATFORM_CLIENT_ID }}
client-secret: ${{ secrets.POWERPLATFORM_CLIENT_SECRET }}

Multi-Environment Pipeline

name: Release Pipeline

on:
push:
branches: [main]

jobs:
build:
uses: joshsmithxrm/ppds-alm/.github/workflows/solution-build.yml@v1
with:
solution-name: MySolution
solution-folder: solutions/MySolution/src
build-plugins: true
package-type: Managed

deploy-qa:
needs: build
environment: QA
uses: joshsmithxrm/ppds-alm/.github/workflows/solution-deploy.yml@v1
with:
solution-name: MySolution
solution-folder: solutions/MySolution/src
package-type: Managed
secrets:
environment-url: ${{ vars.POWERPLATFORM_ENVIRONMENT_URL }}
tenant-id: ${{ vars.POWERPLATFORM_TENANT_ID }}
client-id: ${{ vars.POWERPLATFORM_CLIENT_ID }}
client-secret: ${{ secrets.POWERPLATFORM_CLIENT_SECRET }}

deploy-prod:
needs: deploy-qa
environment: Prod
uses: joshsmithxrm/ppds-alm/.github/workflows/solution-deploy.yml@v1
with:
solution-name: MySolution
solution-folder: solutions/MySolution/src
package-type: Managed
secrets:
environment-url: ${{ vars.POWERPLATFORM_ENVIRONMENT_URL }}
tenant-id: ${{ vars.POWERPLATFORM_TENANT_ID }}
client-id: ${{ vars.POWERPLATFORM_CLIENT_ID }}
client-secret: ${{ secrets.POWERPLATFORM_CLIENT_SECRET }}

Using Composite Actions Directly

For more control, use composite actions directly in your workflows:

name: Custom Workflow

on:
workflow_dispatch:

jobs:
custom:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup PAC CLI
uses: joshsmithxrm/ppds-alm/.github/actions/setup-pac-cli@v1

- name: Authenticate
uses: joshsmithxrm/ppds-alm/.github/actions/pac-auth@v1
with:
environment-url: ${{ vars.POWERPLATFORM_ENVIRONMENT_URL }}
tenant-id: ${{ vars.POWERPLATFORM_TENANT_ID }}
client-id: ${{ vars.POWERPLATFORM_CLIENT_ID }}
client-secret: ${{ secrets.POWERPLATFORM_CLIENT_SECRET }}

- name: Export solution
uses: joshsmithxrm/ppds-alm/.github/actions/export-solution@v1
with:
solution-name: MySolution
output-folder: solutions/MySolution/src

- name: Analyze changes
id: analyze
uses: joshsmithxrm/ppds-alm/.github/actions/analyze-changes@v1
with:
solution-folder: solutions/MySolution/src

- name: Commit if real changes
if: steps.analyze.outputs.has-real-changes == 'true'
run: |
git config user.name "github-actions"
git config user.email "github-actions@github.com"
git add -A
git commit -m "chore: sync solution from Dev"
git push

Troubleshooting

"Cannot find reusable workflow"

  • Verify the repository path: joshsmithxrm/ppds-alm
  • Check the workflow path matches exactly
  • Ensure you're using a valid ref (@v1, @main, etc.)

Authentication Failed

  • Verify secrets are correctly configured
  • Ensure the app registration has proper permissions
  • Check the environment URL is correct and accessible

Import Skipped (Version Match)

This is expected behavior when skip-if-same-version: true (default). The target environment already has the same or newer version.

See Troubleshooting for more help.