A comprehensive deployment toolkit for applying custom templates, themes, and GCWeb (Government of Canada Web Experience Toolkit) compliance to Microsoft Power Pages sites. This engine automates the deployment of Bootstrap 3/4/5 compatible themes to Power Pages sites using the Enhanced Data Model.
- Power Pages Template Engine
The Power Pages Template Engine streamlines the deployment of custom themes to Microsoft Power Pages sites. It is specifically designed for:
- Government of Canada (GoC) departments requiring GCWeb/WET-BOEW compliance
- Organizations needing WCAG 2.0/2.1 Level AA accessible websites
- Developers deploying custom Bootstrap themes to Power Pages
- Enterprise teams requiring bilingual (English/French) support
The engine uses the Dataverse Web API to programmatically create and update:
- Web Templates (Liquid templates for headers, footers, layouts)
- Web Pages (site hierarchy and navigation)
- Web Files (CSS, JavaScript, images, fonts)
- Content Snippets (reusable multilingual content blocks)
- Page Templates (template configurations)
- ✅ Automated Deployment: Single script deploys entire theme structure
- ✅ Enhanced Data Model Support: Built for Power Pages' modern data architecture
- ✅ GCWeb Integration: Full support for Government of Canada Web Experience Toolkit
- ✅ Bilingual Support: English and French content snippet management
- ✅ WCAG Compliance: Designed for accessibility standards compliance
- ✅ Theme Flexibility: Support for Bootstrap 3, 4, and 5 themes
- ✅ Wizard Components: Multi-step form wizard templates included
- ✅ Idempotent Operations: Safe to run multiple times (creates or updates)
- ✅ File Upload via Power Automate: Optional cloud flow for file handling
┌─────────────────────────────────────────────────────────────────┐
│ Power Pages Template Engine │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Theme │ │ Deployment │ │ Dataverse │ │
│ │ Files │───▶│ Script │───▶│ Web API │ │
│ │ (ZIP) │ │ (Bash) │ │ (OAuth 2.0) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Liquid │ │ Content │ │ Power Pages │ │
│ │ Templates │ │ Snippets │ │ Website │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
| Software | Version | Purpose |
|---|---|---|
| macOS/Linux | Any | Script execution environment |
| curl | Latest | HTTP requests to Dataverse API |
| jq | Latest | JSON parsing and manipulation |
| python3 | 3.x | URL encoding and path manipulation |
| unzip | Latest | Theme package extraction |
# Install Homebrew if not already installed
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Install required tools
brew install jq curl python3sudo apt update
sudo apt install jq curl python3 unzip- Microsoft 365 tenant with Power Platform access
- Dataverse environment with Power Pages enabled
- Azure subscription (for App Registration)
- Power Platform Admin or Environment Admin role
- French language pack installed in Dataverse (for bilingual support)
Power Pages offers a 30-day free trial for exploring features:
- Visit powerpages.microsoft.com
- Select "Try it for free"
- Follow the guided setup process
Trial sites can be converted to production when ready.
Power Pages uses capacity-based licensing:
| Plan | Cost | Capacity |
|---|---|---|
| Authenticated Users | ~$200/month | 100 users per site |
| Anonymous Users | ~$75/month | 500 users per site |
Included Entitlements:
- Dynamics 365 Enterprise/Premium licenses: Unlimited Power Pages sites in the same environment
- Power Apps Premium: Unlimited Power Pages sites
- Power Apps per app: Single website use rights
For development and testing, use:
- Power Apps Developer Plan (free): Individual use environment
- Trial environments: 30-day standard or subscription-based trials
Before running the deployment script, you must create a blank Power Pages site:
- Navigate to make.powerpages.microsoft.com
- Select your target environment
- Click Create a site
- Choose Blank page template (critical for custom themes)
- Enter site name and web address
- Wait for site provisioning (typically 5-15 minutes)
Important: The site must use the Enhanced Data Model. New sites default to this model, but verify in the site settings.
If your environment doesn't have the Enhanced Data Model enabled:
- Go to admin.powerplatform.microsoft.com
- Select your environment
- Navigate to Resources → Power Pages sites
- Toggle Switch to enhanced data model to ON
- Wait for the Power Pages Core package to install
Verify Data Model:
- Open Power Platform Admin Center
- Go to Resources → Power Pages sites → Select your site → Manage
- Check the Data Model field in Site Details
Create an Azure App Registration for API authentication:
-
Go to portal.azure.com
-
Navigate to Azure Active Directory → App registrations
-
Click New registration
-
Configure:
- Name:
PowerPages-Template-Engine(or your preferred name) - Supported account types: Single tenant
- Redirect URI:
https://login.onmicrosoft.com
- Name:
-
After creation, note the:
- Application (client) ID
- Directory (tenant) ID
-
Create a client secret:
- Go to Certificates & secrets
- Click New client secret
- Set description and expiration
- Copy the secret value immediately (it won't be shown again)
-
Grant API permissions:
- Go to API permissions
- Add Dynamics CRM →
user_impersonation - Grant admin consent
The template engine uses a Power Automate cloud flow for file uploads:
-
Locate
FILE_UPLOAD_FLOW.zipin the repository -
Import into your Dataverse environment:
- Go to make.powerapps.com
- Navigate to Solutions → Import
- Upload and configure the solution
-
After import, open the cloud flow:
- Edit the flow
- Copy the HTTP POST URL from the trigger action
- Save this URL for your configuration file
Create a connection.json file with your environment details:
{
"clientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"tenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"crmInstance": "yourorg",
"redirectUri": "https://login.onmicrosoft.com",
"websiteId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"blobAddress": "https://yourstorageaccount.blob.core.windows.net/yourcontainer/",
"FlowURL": "https://prod-xx.westus.logic.azure.com:443/workflows/...",
"clientSecret": "your-client-secret-value",
"themeZipPath": "/path/to/your/theme.zip"
}Configuration Parameters:
| Parameter | Description | Example |
|---|---|---|
clientId |
Azure App Registration Client ID | a1b2c3d4-... |
tenantId |
Azure AD Tenant ID | e5f6g7h8-... |
crmInstance |
Dataverse environment name | contoso (from contoso.crm.dynamics.com) |
redirectUri |
OAuth redirect URI | https://login.onmicrosoft.com |
websiteId |
Power Pages Website GUID | Found in Power Pages Management app |
blobAddress |
Azure Blob Storage URL (optional) | https://storage.blob.core.windows.net/ |
FlowURL |
Power Automate trigger URL | From the imported cloud flow |
clientSecret |
Azure App client secret | Your generated secret |
themeZipPath |
Path to theme ZIP file | /Users/you/themes/gcweb.zip |
- Open Power Pages Management app (Model-driven app)
- Navigate to Websites
- Open your website record
- Copy the Website ID (GUID) from the URL or form
The deployment script contains additional variables you may need to modify:
# Language codes (Dataverse LCID values)
ENGLISH_LANGUAGE_CODE=1033
FRENCH_LANGUAGE_CODE=1036
# Template names
PAGE_TEMPLATE_NAME_NEW_HOME="CS-Home-WET"
WEB_TEMPLATE_HEADER="CS-header"
WEB_TEMPLATE_FOOTER="CS-footer"git clone https://github.com/Cloudstrucc/PowerPages-Template-Engine.git
cd PowerPages-Template-Engine# macOS
brew install jq curl python3
# Verify installations
jq --version
curl --version
python3 --version- Download GCWeb theme from GCWeb Releases
- Place the ZIP file in a known location
- Update
themeZipPathin your configuration
cd BuildGCWEB
chmod +x deploy.sh
./deploy.shWhen prompted:
- Enter
Yto provide a JSON configuration file - Enter the full path to your
connection.json - The script will authenticate and begin deployment
Interactive Mode:
If you prefer not to use a JSON file, enter N and provide values interactively.
The deployment script executes these steps in order:
- Extract Theme Files: Unzips the theme package
- Create Snippets: Deploys bilingual content snippets (EN/FR)
- Create File Snippets: Processes JS/CSS/HTML files as snippets
- Write Templates: Creates/updates Liquid web templates
- Update Home Page: Configures the home page template
- Write Hierarchy: Creates web pages and web files from folder structure
- Update Baseline Styles: Deploys required Power Pages CSS files
================================================
Power Pages Deployment Script
================================================
Acquiring authentication token...
Token acquired successfully
Retrieving configuration IDs...
Starting portal template installation...
Extracting theme files...
Creating snippets...
Writing templates...
Updating home page...
Writing hierarchy...
Updating baseline styles...
Portal template installation complete!
================================================
Deployment completed successfully!
Please go to Power Pages site and press Sync
================================================
BuildGCWEB/
├── deploy.sh # Main deployment script
├── files/
│ ├── liquid/
│ │ ├── contentsnippets/
│ │ │ └── snippets.json # Bilingual snippet definitions
│ │ └── webtemplates/ # Liquid template files
│ │ ├── CS-header.liquid
│ │ ├── CS-footer.liquid
│ │ ├── CS-Home-WET.liquid
│ │ ├── wizard-*.liquid # Multi-step form wizard templates
│ │ └── ...
│ ├── bootstrap.min.css # Bootstrap CSS
│ ├── custom-styles.css # Custom styling
│ ├── portalbasictheme.css # Power Pages base theme
│ ├── theme.css # GCWeb theme CSS
│ ├── favicon.ico # Site favicon
│ ├── logo-bw-contrast.png # Logo variants
│ ├── logo-invert.png
│ └── landscape.png
└── startbootstrap-*/ # Extracted theme folder
Contains reusable JavaScript libraries and utilities:
Libraries/
├── js/
│ ├── wet-boew.min.js # WET-BOEW core JavaScript
│ ├── gcweb.min.js # GCWeb theme JavaScript
│ └── custom/ # Custom scripts
└── css/
└── overrides/ # CSS override files
Key Liquid templates included:
| Template | Purpose |
|---|---|
CS-header.liquid |
Site header with GCWeb navigation |
CS-footer.liquid |
Site footer with required links |
CS-Home-WET.liquid |
Home page layout |
wizard-container.liquid |
Multi-step form wizard container |
wizard-step.liquid |
Individual wizard step template |
wizard-navigation.liquid |
Wizard progress indicator |
The template engine includes a multi-step form wizard system for complex data entry:
Wizard Features:
- Step-by-step navigation
- Progress indication
- Validation per step
- Session state management
- Accessible keyboard navigation
- Responsive design
Wizard Templates:
{% include 'wizard-container' steps: 5, form_id: 'application-form' %}
{% include 'wizard-step' number: 1, title: 'Personal Information' %}
<!-- Step 1 content -->
{% endwizardstep %}
{% include 'wizard-step' number: 2, title: 'Contact Details' %}
<!-- Step 2 content -->
{% endwizardstep %}
{% endwizardcontainer %}GCWeb is the official theme for Canada.ca, built on the Web Experience Toolkit (WET-BOEW):
- Official Canada.ca appearance: Mandatory for Government of Canada websites
- Accessibility built-in: WCAG 2.0/2.1 Level AA compliant
- Bilingual support: Full English/French language switching
- Responsive design: Works across all devices
- Standardized components: Consistent UI patterns
WET-BOEW provides:
- Accessible components (menus, forms, tables)
- Progressive enhancement
- Print-friendly layouts
- Multimedia accessibility features
The template engine is designed for WCAG 2.0/2.1 Level AA compliance:
| Criterion | Implementation |
|---|---|
| 1.1 Text Alternatives | Alt text on all images |
| 1.3 Adaptable | Semantic HTML structure |
| 1.4 Distinguishable | Color contrast, text resizing |
| 2.1 Keyboard Accessible | Full keyboard navigation |
| 2.4 Navigable | Skip links, focus indicators |
| 3.1 Readable | Language attributes |
| 4.1 Compatible | Valid HTML, ARIA labels |
Content snippets support English and French:
{
"site-title": [
"Government of Canada Portal",
"Portail du gouvernement du Canada"
],
"footer-terms": [
"Terms and Conditions",
"Avis"
]
}Language Switching:
The templates include automatic language toggle functionality following GCWeb standards.
After deployment completes:
- Go to make.powerpages.microsoft.com
- Open your site in the design studio
- Click Sync to refresh site components
- Preview changes in the studio
Power Pages supports multiple authentication providers. For production sites, configure external authentication:
Supported Providers:
- Azure AD B2C (recommended for external users)
- Microsoft Entra External ID (preview)
- OKTA
- Other OIDC/SAML 2.0 providers
Azure AD B2C is recommended for external user authentication:
- Go to portal.azure.com
- Search for "Azure AD B2C"
- Create a new Azure AD B2C tenant
-
In your B2C tenant, go to App registrations
-
Create New registration
-
Configure:
- Name:
PowerPages-Auth - Redirect URI:
https://yoursite.powerappsportals.com/signin-aad-b2c_1 - Supported account types: Accounts in this organizational directory only
- Name:
-
Enable tokens:
- Go to Authentication
- Enable Access tokens and ID tokens
Create sign-up/sign-in and password reset user flows:
- Go to User flows in your B2C tenant
- Create Sign up and sign in flow (B2C_1_SignUpSignIn)
- Create Password reset flow (B2C_1_ResetPassword)
- Configure user attributes as needed
- In Power Pages design studio, go to Security → Identity providers
- Select Azure Active Directory B2C → Configure
- Enter:
- Authority: Your B2C issuer URL (with
tfp) - Client ID: Application ID from B2C
- Redirect URI: Your site URL
- Authority: Your B2C issuer URL (with
Configure access control after deployment:
-
Open Power Pages Management app
-
Navigate to Security → Web Roles
-
Create roles:
- Anonymous Users: Public access
- Authenticated Users: Logged-in access
- Administrators: Full access
-
Assign Table Permissions to roles
-
Configure Page Permissions as needed
Content snippets are managed in snippets.json:
{
"snippet-name": [
"English content",
"French content"
]
}Adding New Snippets:
- Edit
files/liquid/contentsnippets/snippets.json - Add your snippet with both language versions
- Re-run the deployment script
Using Snippets in Templates:
{{ snippets['snippet-name'] }}File-Based Snippets:
Place .js, .css, or .html files in the contentsnippets folder. The script automatically creates snippets from these files with the filename (minus extension) as the snippet name.
Failed to acquire token. Response:
{"error": "invalid_client", ...}
Solution: Verify your clientId and clientSecret are correct. Ensure the app registration has proper Dynamics CRM permissions.
HOME_PAGE_ID: null
Solution: Verify the websiteId in your configuration. Open the website record in Power Pages Management to confirm the GUID.
French Language ID: null
Solution: Install the French language pack in your Dataverse environment:
- Go to Power Platform Admin Center
- Select your environment → Settings
- Languages → Enable French
HTTP 403 Forbidden
Solution: Ensure your Azure AD app has admin consent for Dynamics CRM permissions and that your user account has appropriate environment roles.
Enable debug output by examining stderr messages:
./deploy.sh 2>&1 | tee deployment.logTest API access manually:
curl -s -X GET "https://yourorg.api.crm3.dynamics.com/api/data/v9.2/mspp_websites" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Accept: application/json"Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
See developer-guidelines.md in the repository for coding standards and best practices.
This project is licensed under the MIT License - see the LICENSE file for details.
MIT License
Copyright (c) 2026 Frederick Pearson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- Government of Canada - GCWeb and WET-BOEW frameworks
- Microsoft - Power Pages platform and documentation
- Open Source Community - Bootstrap, Liquid templating
- Power Pages Documentation
- GCWeb Theme Documentation
- WET-BOEW Documentation
- Azure AD B2C Documentation
- wet-boew/GCWeb - Official GCWeb repository
- wet-boew/wet-boew - Web Experience Toolkit
- Power Platform CLI
Built with ❤️ by CloudStrucc Inc.