Power Apps Code Apps
A complete foundation for building enterprise-grade Code Apps with React, Fluent UI v9, and Power Platform. Use this template, run the wizard, and follow the intended sequence: plan first, prototype second, connect later.
πΊοΈ Template to Production β in 10 Steps
From clicking Use this template on GitHub to promoting through Power Platform Pipelines, every Foundations team follows the same proven path. One picture says it all.
The rest of this guide explains how β section by section.
Start Here
The README and the Guide describe the same workflow. Pick the format that matches how you want to onboard.
Start in the README
Use the concise text version if you want the shortest path through cloning, running the wizard, and understanding the repo structure.
Stay in the Guide
Use this page if you want the same procedure with richer context, step breakdowns, portal links, and operational cautions in one place.
Need the Full Delivery Path?
Use the prototype-first golden path when you want the end-to-end sequence from business planning through schema hardening and real providers.
What's Inside
The repo contains instruction files, helper scripts, and a setup wizard that work together.
14 Instruction Files β 4 Agents
Native guidance for GitHub Copilot, Claude Code, Cursor, and Codex. Each agent reads the same rules in its own format. The canonical source lives in .github/instructions/; Claude, Cursor, and Codex projections are generated from it.
Narrative-First Planning
Optional, but high leverage. If the app idea is still taking shape, this planning layer helps your coding agent decompose the business narrative, challenge gaps, and refine scope before schema and connectors harden.
Setup Wizard
One script, 9 steps, zero guesswork. Creates publisher and solution via API, sets up auth, scaffolds your app, adds a dedicated connector-binding pass, and deploys from the terminal.
Auth Scripts
1Password integration or .env.local fallback. Client secrets are encrypted at rest with AES-256-GCM. Service Principal auth means no browser popups β locally or in CI/CD.
Sync Foundations
Pull the latest instruction files, wizard updates, and security patches from the template repo with npm run sync:foundations. Your project code is never touched.
package.json and npm run won't work. Use bash scripts/sync-foundations.sh or node scripts/sync-foundations.mjs directly instead.
Every coding agent needs its instructions in a different format, but the underlying rules must be identical. Foundations solves this with a single canonical source β multiple generated projections model:
- Canonical source β
.github/instructions/*.instructions.md(Copilot-native format) plus the hand-authored rootAGENTS.md. - Generated projections β A manifest (
agent-guidance.config.json) maps each canonical file to its Claude (.claude/rules/), Cursor (.cursor/rules/), and Codex (nestedAGENTS.md) equivalents. - Drift prevention β
npm run guidance:checkfails if any projected file is missing or unmarked.npm run guidance:generateregenerates all projections.
The rule: edit the canonical .github/instructions/ files, then run npm run guidance:generate. Never edit projected files directly β they carry a "do not edit" marker and will be overwritten.
Required Tech Stack
Every Code App uses this exact stack. No substitutions without team lead approval.
| Layer | Choice | Version | Why |
|---|---|---|---|
| Language | TypeScript | 5.x | Type safety across codebase + connector models |
| UI Framework | React | 18.x | Microsoft-recommended; all official samples target React |
| Design System | Fluent UI v9 | @fluentui/react-components | Native Microsoft look; matches Power Platform chrome |
| Bundler | Vite | 5.x | Fast HMR; official templates use Vite |
| Server State | TanStack Query | v5 | Caching, deduplication, background refresh for connectors |
| Routing | React Router | v6 | Nested layouts, data loaders |
| Power Apps SDK | @microsoft/power-apps | ^1.0.3 | Connector access, auth context |
| CLI | PAC CLI | latest (not 2.3.2) | Scaffold, add data sources, deploy |
| Editor | VS Code / Cursor | Latest | Or any editor with coding-agent support |
| Coding Agent | Copilot / Claude / Cursor / Codex | β | Any LLM-backed agent reads the repo's native guidance |
Narrative-First Planning Layer
Use this when the business problem is still being worked out and you want the eventual app to be more than a fast scaffold.
Decompose the Narrative
Your coding agent starts from the user's freeform description, identifies goals, actors, workflows, outputs, constraints, and unknowns, then asks only the most useful follow-up questions.
00a-business-problem-decomposition.instructions.md
Refine the Scope
Your agent challenges for exception paths, validations, approvals, automation, Teams, Microsoft 365 outputs, reporting, governance, and Microsoft Copilot Studio placement decisions.
00b-scope-refinement-and-solution-shaping.instructions.md
Convert to Technical Planning
Once the scope is stable, your agent derives candidate entities, relationships, ownership patterns, and lifecycle states, then hands off into the Dataverse planning artifact workflow.
00c-solution-concept-to-dataverse-plan.instructions.md
Prototype Before Schema
Generate domain contracts and mock providers from the planning payload, build the UX in prototype mode, and capture what that prototype changes in the eventual schema.
00d-prototype-validation.instructions.md
β οΈ Enable Code Apps in Your Environment
This is a prerequisite. Without it, pac code push will fail with "does not allow this operation for this Code app."
pac code push will fail even with correct auth.
Steps
- Open β admin.powerplatform.microsoft.com
- Select your development environment
- Go to Settings β Features
- Find "Code components for canvas apps" β toggle ON
- Find "Allow publishing of canvas apps with code components" β toggle ON
- Click Save
- Wait ~1 minute for the setting to propagate
Auth for pac code push
pac code push requires a user auth profile β the BAP checkAccess API rejects SPN tokens. This is a one-time setup: create a user profile via device-code sign-in, and all subsequent pushes work silently. PAC CLI caches a refresh token that auto-renews (~90 days).
For CI/CD to test/production, use solution export/import instead of pac code push. Solution export and import both accept SPN auth and deploy the Code App, tables, connection references, and security roles as one atomic unit. See the deployment instructions for the full pipeline.
The Setup Wizard
This is the same quick-start flow described in the README. For non-trivial apps, pair it with the planning layer and prototype checkpoint before locking in schema and connectors.
- Recommended: Click "Use this template" on GitHub β create your repo β
git clone https://github.com/your-org/my-app.git - Quick explore:
npx degit martycarreras-psnl/PAppsCAFoundations my-app
Terminal wizard (the original):
cd my-app/wizard && npm install && node index.mjs
β¨ Browser wizard (new) β same flow, beautiful Fluent UI experience at http://127.0.0.1:5174:
cd my-app && npm run wizard:ux
Both wizards share .wizard-state.json, so you can switch between them at any time without losing progress. Steps that need interactive auth or long-running CLI sessions show a one-click "copy command" handoff in the browser version.
Works on Windows, macOS, and Linux.
The wizard saves your progress β quit with Ctrl+C and pick up where you left off. Reset with --reset.
Want a stronger business fit? After the wizard gives you a working foundation, use the narrative-first planning instructions and prototype checkpoint before finalizing Dataverse schema, connectors, approvals, or agent behavior.
β Do not git clone the template repo directly β that leaves origin pointing to PAppsCAFoundations instead of your own repo.
Steps 1β2
Check tools, name project & environments
Steps 3β4
App Registration & auth setup
Steps 5β6
Publisher & solution (auto via API)
Steps 7β9
Scaffold, bind connectors, build & deploy
All 9 Wizard Steps
Expand each step to see what happens and what you'll need.
The wizard checks for required tools and offers to install missing ones:
- Editor β VS Code (Install), Cursor, or any editor with coding-agent support
- Coding Agent β GitHub Copilot, Claude Code, Cursor, Codex, or another LLM-backed agent (see below)
- Node.js 20+ β Install
- Git β Install
- .NET SDK β Install
- PAC CLI β The wizard will install it automatically via
dotnet tool install -g Microsoft.PowerApps.CLI.Toolif missing - 1Password CLI (optional) β Install
pac code push. The wizard detects this and helps you downgrade.The wizard prompts you for your app name and environment URLs:
| Value | Example | Notes |
|---|---|---|
| App name | My Brain | Display name, any characters |
| Dev environment URL | https://org-dev.crm.dynamics.com | Required. Must have Dataverse enabled. |
| Test environment URL | https://org-test.crm.dynamics.com | Optional β press Enter to skip |
| Prod environment URL | https://org.crm.dynamics.com | Optional β press Enter to skip |
If you don't have a development environment yet, create one first:
- Open β admin.powerplatform.microsoft.com β Environments β + New
- Name it Your App - Dev, type Developer or Sandbox
- Toggle "Add Dataverse" to YES (required!)
- Copy the Environment URL after provisioning
This step collects your Azure App Registration credentials. If you use 1Password, the wizard detects it and tries to pull credentials automatically.
1Password Users (fastest path)
If the op CLI is detected, the wizard asks for your vault and item name, then reads tenant-id, app-id, and client-secret directly from 1Password. If all three are found, you skip all manual entry.
tenant-id(Text) β your Azure Directory IDapp-id(Text) β your App Registration Client IDclient-secret(Password) β your App Registration secretenv-dev(Text) β your Dev environment URLenv-test(Text, optional) β Test environment URLenv-prod(Text, optional) β Prod environment URL
If the item doesn't exist yet, the wizard will collect values manually and offer to create the 1Password item for you.
Manual Path (no 1Password, or partial credentials)
If 1Password isn't available, or some fields are missing, the wizard guides you through creating the App Registration:
A. Create Azure App Registration
- Open β Azure Portal β App registrations
- Click + New registration
- Name:
PowerApps-CodeApps-YourApp, Single tenant - Copy Application (client) ID and Directory (tenant) ID
- Go to Certificates & secrets β + New client secret
- Copy the secret value immediately β shown only once!
- Go to API permissions β + Add β Dataverse β user_impersonation
- Click Grant admin consent
B. Register as Application User
- Open β admin.powerplatform.microsoft.com
- Select environment β Settings β Users + permissions β Application users
- + New app user β search for your App Registration β assign System Administrator role
The wizard takes the credentials from Step 3 and handles everything:
- If you chose 1Password in Step 3, the storage mode and vault/item are already set β no repeated questions
- Otherwise, choose 1Password or .env.local for secret storage
- Writes credential files (
op://references or encrypted values) - Ensures
.env.localis in.gitignore - Creates repo-scoped PAC auth profiles for each environment instead of machine-global
Dev/Test/Prodnames - Runs
pac org whoto verify the selected profile matches the wizard target environment and should show your App Registration name, not a human user - Installs a pre-commit hook to prevent accidental secret leaks
pac commands work without any browser popup. SPN auth is used for Dataverse API calls, publisher/solution creation, and CI/CD (solution export/import). pac code commands (push, add-data-source) need a user profile β created once via the wizard (one-time sign-in, then silent). The wizard handles this automatically in the later connected-mode steps.The wizard queries Dataverse directly to find existing publishers or create a new one β no Maker Portal visit needed.
- Lists all existing publishers in the environment via Dataverse Web API
- Lets you select an existing publisher or create a new one
- If creating new: prompts for prefix, display name, and internal name
- Creates the publisher via API with all required properties
- Records the Choice value prefix automatically
| Value | Example | Rules |
|---|---|---|
| Publisher prefix | mybrn | 2β8 lowercase letters only. Cannot change later. |
| Publisher display name | My Brain Engineering | Human-readable |
| Publisher internal name | mybrainengineering | Lowercase, no spaces |
Like the publisher step, the wizard creates or selects a solution via Dataverse Web API β no Maker Portal visit needed.
- Lists all existing unmanaged solutions in the environment
- Lets you select an existing solution or create a new one
- If creating new: prompts for display name and unique name
- Creates the solution via API, bound to your publisher from Step 5
| Value | Example | Notes |
|---|---|---|
| Solution display name | My Brain | Shown in the Maker Portal solution list |
| Solution unique name | MyBrain | No spaces. Used in CLI commands and API calls. |
The wizard creates a complete, ready-to-develop project:
- Downloads the official starter template (or creates a minimal fallback)
- Installs all dependencies (React, Fluent UI v9, TanStack Query,
@microsoft/power-apps) - Writes
tsconfig.jsonwithmoduleResolution: "bundler" - Writes
vite.config.tswith port 3000,@/path alias, and relative base path for deploy - Creates the full folder structure (components, pages, hooks, utils, tests, etc.)
- Writes starter files:
main.tsx,App.tsxwith Fluent UI + TanStack Query providers - Writes
vitest.config.tswith jsdom, jest-dom matchers,@/alias, and coverage thresholds - Creates smoke test infrastructure:
tests/setup/setup.ts,tests/setup/test-utils.tsx(custom render with all providers), andsrc/App.test.tsx - Runs
npm run test:smokeautomatically to verify the scaffold is healthy before proceeding - Copies all instruction files and agent-native guidance (
.claude/rules/,.cursor/rules/, nestedAGENTS.md) from the foundations repo - Includes the narrative-first planning instructions so you can refine the business scope before hardening schema and connector choices
- Seeds prototype contracts, mock providers, and
dataverse/prototype-feedback.mdfrom the planning payload - Runs
pac code initto register the app in Power Platform - Defers connector setup by default so the team can prototype before binding real systems
- Initializes Git repo, detects or prompts for remote URL, makes initial commit
- Generates a project-specific README (replaces the template README)
vitest.config.ts, a test setup with provider wrappers, and src/App.test.tsx with three smoke tests. The wizard runs npm run test:smoke automatically during this step β if they pass, your scaffold is verified healthy. You can re-run anytime with npm run test:smoke or npm run test.
Connector & Data Source Setup
The initial scaffold no longer asks for connector IDs up front. The intended flow is to return later once the planning payload and prototype are stable enough to bind real systems.
# Terminal wizard: node wizard/index.mjs --from 8 # Browser wizard β navigate to Step 8 (Bind Connectors): npm run wizard:ux
pac code commands need a user auth profile (not SPN). The wizard detects SPN auth and automatically switches to a user profile before running pac code add-data-source. This user profile only needs to be created once β subsequent calls work silently.
When you do re-run connector setup, the wizard first tries pac connection list, filters to the selected connector API ID, and lets you choose from discovered connections. Manual Connection ID entry is only the fallback.
How to Get Connection IDs
You must create connections before adding them as data sources. See the Creating Connections section below for step-by-step instructions. Once they exist, the later wizard connector flow can often discover them for you.
This is the first connected-mode step. Run it only after the planning payload and prototype are stable enough that connector choices are no longer guesses.
- Presents a checklist of the seven most common connectors (Office 365 Users, SharePoint, Dataverse, SQL, Teams, Blob, Outlook)
- Then loops on "Add another connector by URL or apiId (blank to finish)" β paste any Maker Portal connection URL or a bare
shared_xxxapiId, including custom connectors and connectors not on the shortlist - Creates solution-level connection references for the connectors you actually chose
- Runs
pac connection listand filters matches to the selected connector API ID - Lets you select an existing connection when matches are found
- Falls back to manual Connection ID entry only when discovery does not find a usable match
- Adds the selected data sources to the Code App so generated services can be produced later
.../connections/shared_office365users/<GUID>/details and the wizard extracts both
the connector apiId and the connection GUID in one shot β no separate Connection-ID prompt for that connector.
Pasting just a bare GUID still works, and bare apiIds (e.g. shared_approvals) skip the URL entirely
and go straight to connection-ID resolution. Environment URLs are similarly tolerant β paste straight from PPAC,
with or without https://.
pac code add-data-source. The wizard switches from SPN automatically when needed. If the profile already exists, no sign-in is required.The wizard runs:
npm run buildβ verifiesdist/index.htmlexists- Checks
power.config.jsonforappIdto determine first vs. subsequent push - First push: Creates a user auth profile via browser sign-in (one-time)
- Subsequent pushes: Work silently β the cached refresh token auto-renews (~90 days)
- Offers to deploy through the verified PAC flow instead of trusting the active machine profile
Then displays a complete summary of everything that was set up and what to do next:
cd ../your-app npm run dev:local β prototype with mock providers npm run prototype:seed β refresh prototype assets after editing the planning payload npm run dev β connected mode once real providers exist node wizard/index.mjs --from 8 β bind real connectors when ready npm run deploy β deploy changes after the app has been registered
Creating Connections
Before moving from prototype mode into connected mode, create the actual connection instances in the Maker Portal.
A connection reference (created by the wizard in your solution) is a pointer that says "this app uses the Office 365 Users connector." The connection is the authenticated instance β the actual credential that makes it work. You need both.
Step-by-Step: Create a Connection
- Go to β make.powerapps.com and select your development environment
- In the left nav, click Connections (under Data, or directly at
make.powerapps.com/environments/<env-id>/connections) - Click + New connection
- Search for the connector (e.g. Office 365 Users) and click it
- Click Create and authenticate when prompted
- Once created, click the connection to open its details
- Copy the Connection ID from the browser URL β it's the GUID at the end:
https://make.powerapps.com/environments/xxx/connections/shared_office365users/160ffdfd-aff5-4740-86a8-47209c1c608c/details
Common Connectors
| Connector | API ID (for pac CLI) |
|---|---|
| Dataverse | dataverse (uses -t <table> instead of -c) |
| Office 365 Users | shared_office365users |
| SharePoint | shared_sharepointonline |
| Office 365 Outlook | shared_office365 |
| Microsoft Teams | shared_teams |
| SQL Server | shared_sql |
| Azure Blob Storage | shared_azureblob |
Adding Data Sources (After Getting Connection IDs)
Preferred path: rerun the wizard when you are ready to connect real data.
# Terminal wizard: node wizard/index.mjs --from 8 # Or use the browser wizard β navigate to Step 8: npm run wizard:ux
The wizard will create the solution-level connection references, try to discover existing environment connections with pac connection list, and let you select one when matches are found.
# Dataverse tables (no connection ID needed): pac code add-data-source -a dataverse -t myprefix_project # Non-Dataverse connectors (connection ID required): pac code add-data-source -a shared_office365users -c 160ffdfd-aff5-4740-86a8-47209c1c608c pac code add-data-source -a shared_sharepointonline -c <YOUR_CONNECTION_ID>
pac code commands). Create one once β it works silently after that:
pac auth create --name pp-myrepo-d-u-1a2b3c4d --environment https://org-dev.crm.dynamics.com --deviceCode pac auth select --name pp-myrepo-d-u-1a2b3c4d
Portal Quick Links
Portals you may need during setup (Step 3 App Registration is the main manual step).
Power Apps Maker Portal
View your solution, tables, and connections. Publishers and solutions are created automatically by the wizard.
Power Platform Admin Center
Create environments, register Application Users, manage settings.
Azure Portal β App Registrations
Create Service Principal, client secrets, API permissions.
Azure Portal β Entra ID
Manage tenant, users, enterprise applications, admin consent.
Local Development with Vite
Two modes: Prototype (mock providers, no Power Platform) and Connected (real connectors). Always start with Prototype mode.
Prototype Mode
Vite only. Domain contracts plus mock providers. No auth, no connections, no Power Platform needed. Build UI fast and let the UX challenge the data model.
npm run dev:local
Connected Mode
Vite + pac code run. Real connector calls through the Power Platform proxy.
npm run dev
When to Use Each Mode
| Phase | Command | What Runs | Data Source |
|---|---|---|---|
| Prototyping / UI | npm run dev:local | Vite only | Mock data |
| Refresh prototype assets | npm run prototype:seed | Node script | Planning payload |
| Integration | npm run dev | Vite + pac code run | Real connectors |
| Unit Tests | npm run test | Vitest | Mock data (MSW) |
| E2E Tests | npm run test:e2e | Vite + Playwright | Mock data |
| Production Build | npm run build | tsc + Vite build | N/A (static) |
| Deploy | npm run deploy | Build + pac code push | N/A |
Prototype Mode β How It Works
npm run dev:local runs VITE_USE_MOCK=true vite --port 3000. Vite serves your app at http://localhost:3000 with sub-second HMR β save a file, see it instantly. Refresh prototype contracts and mock providers anytime with npm run prototype:seed after editing dataverse/planning-payload.json.
PowerProvider context, auth context, connection consent β these require pac code run.Need the full sequence from planning payload through prototype review to real providers? Open the prototype-first golden path.
Mock Data Pattern
Every hook that calls a connector checks VITE_USE_MOCK to decide whether to return mock data or make a real call:
// src/hooks/useProjects.ts
import { useQuery } from '@tanstack/react-query';
import { mockProjects } from '@/mockData/projects';
import { ProjectService } from '@/generated/services/ProjectService';
const USE_MOCK = import.meta.env.DEV && import.meta.env.VITE_USE_MOCK === 'true';
export function useProjects() {
return useQuery({
queryKey: ['projects'],
queryFn: USE_MOCK
? () => Promise.resolve({ data: mockProjects })
: () => ProjectService.getAll(),
});
}
Connected Mode β Prerequisites
- PAC auth profiles created (
pac org whoshows org, no popup) - Data source added:
pac code add-data-source - At least one data source added with
pac code add-data-sourcesosrc/generated/exists power.config.jsonexists next topackage.json
Vite Configuration
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig(({ command }) => ({
base: command === 'build' ? './' : '/', // CRITICAL for Code Apps
plugins: [react()],
server: {
port: 3000, // Required by Power Apps SDK
strictPort: true, // Fail if 3000 occupied (don't silently use 3001)
open: true, // Auto-open browser
},
resolve: {
alias: { '@': path.resolve(__dirname, './src') },
},
build: { outDir: 'dist' },
}));
base: './' is required for production builds. Power Apps serves your bundle from a nested, non-root path. Without relative base, all asset URLs are absolute (/assets/index-abc.js) which 404 β blank screen.
Vite Environment Variables
Only variables prefixed with VITE_ are exposed to client code via import.meta.env. This is a security feature β Power Platform credentials (PP_*) are never leaked to the browser.
| Variable | Purpose | Set In |
|---|---|---|
VITE_USE_MOCK | Toggle mock data | dev:local script or .env.development |
VITE_APP_TITLE | App display name (optional) | .env or .env.development |
Troubleshooting
| Problem | Fix |
|---|---|
| Port 3000 already in use | lsof -ti:3000 | xargs kill -9 |
pac code run fails | Check pac org who, avoid PAC v2.3.2, confirm power.config.json |
| HMR not refreshing | Verify react() plugin in vite.config.ts, clear cache: rm -rf node_modules/.vite |
| Mock data shape mismatch | Re-run pac code add-data-source for the affected table or connector, then compare with src/generated/models/ |
See .github/instructions/01-scaffold.instructions.md for complete Vite config, mock data patterns, and mutation mocking.
Connectors
Code Apps cannot make direct HTTP calls. All data access goes through Power Platform connectors, but connector binding should happen after prototype validation.
connect-src 'none'. If a connector exists for the service, use it β no exceptions.Adding a Data Source
Recommended order: prototype first, then rerun the wizard from the dedicated connector step once the planning payload is stable.
# Terminal wizard: node wizard/index.mjs --from 8 # Or use the browser wizard β navigate to Step 8: npm run wizard:ux
# Dataverse (no connection ID needed): pac code add-data-source -a dataverse -t myprefix_project # Non-Dataverse connectors (connection ID required β see Creating Connections): pac code add-data-source -a shared_office365users -c <CONNECTION_ID>
pac code commands need a user auth profile (not SPN). Create the profile once, then all subsequent calls work silently. Switch to your repo-scoped user profile for the current environment before running them.Wrapping Generated Code with TanStack Query
// src/hooks/useProjects.ts
import { useQuery } from '@tanstack/react-query';
import { SqlService } from '@/generated/services/SqlService';
export function useProjects() {
return useQuery({
queryKey: ['projects'],
queryFn: () => SqlService.getProjects(),
staleTime: 5 * 60 * 1000, // Connector calls aren't free
});
}
src/generated/. They're auto-generated by the PAC CLI. If you need to extend a type, create a wrapper in src/types/.See .github/instructions/02-connectors.instructions.md for full patterns including Dataverse OData queries, pagination, and error handling.
Components
React + Fluent UI v9 patterns for consistent, accessible UI.
Key Principles
- Functional components only (no class components)
- Single responsibility β files under ~150 lines
- Fluent UI v9 (
@fluentui/react-components) for all controls - Style with
makeStyles+ design tokens β never raw CSS values - Local state (
useState) for UI; Server state (TanStack Query) for data - Colocate tests:
ProjectCard/ProjectCard.tsx+ProjectCard.test.tsx
Provider Stack (main.tsx)
<QueryClientProvider>
<FluentProvider theme={webLightTheme}>
<BrowserRouter>
<App />
</BrowserRouter>
</FluentProvider>
</QueryClientProvider>
See .github/instructions/03-components.instructions.md for layout patterns, data tables, error boundaries, and accessibility guidance.
Deployment & CI/CD
From local dev to production with GitHub Actions.
Flow
Feature Branch β PR β CI (lint + test + build) β Merge β Deploy to Dev
β
Deploy to Test β Deploy to Prod
Manual Deploy (Dev Only)
pac code push is rejected with SPN auth for both first and subsequent pushes. The wizard creates a user profile automatically. For CI/CD, see Enable Code Apps and the deployment instructions for workarounds.npm run deploy
GitHub Secrets Required
| Secret | Description |
|---|---|
PP_APP_ID | Application (client) ID |
PP_CLIENT_SECRET | Client secret value |
PP_TENANT_ID | Directory (tenant) ID |
Per-Environment Variable
| Variable | Description |
|---|---|
POWER_PLATFORM_URL | Environment URL (e.g. https://org-dev.crm.dynamics.com) |
See .github/instructions/04-deployment.instructions.md for CI/CD workflow YAML, solution management, and deployment settings files.
Testing
Vitest for units, Playwright for E2E, MSW for connector mocking.
What to Test
- β Component rendering with different states
- β User interactions (clicks, form submissions)
- β Custom hooks wrapping connectors
- β Utility functions with business logic
- β Generated files (
src/generated/) - β Fluent UI internals
See .github/instructions/05-testing.instructions.md for config files, MSW handler patterns, and CI integration.
Security
Auth is automatic. Your job: don't leak secrets, validate inputs, sanitize output.
.env.local.Required .gitignore Entries
.env.local .env.*.local .pac/ auth.json node_modules/ dist/ coverage/ .wizard-state.json
Input Validation
// Always validate user inputs
if (/<script/i.test(name)) return 'Invalid characters';
if (name.length > 200) return 'Max 200 characters';
// Never use dangerouslySetInnerHTML with user data
<Text>{project.description}</Text> // Safe β React escapes
See .github/instructions/06-security.instructions.md for error message guidelines, DLP handling, and code review checklist.
Dataverse Schema
Tables, columns, option sets β and the rules that prevent expensive mistakes.
Golden Rules
- Always global option sets β never inline choices
- Always use publisher prefix β
mybrn_ideastatus, notideastatus - Option values start at 100000000 β never 0 or 1
- Add, don't delete β removing a live option value breaks existing records silently
- Create inside the solution β never from the top-level Tables view
Naming Convention
β mybrn_ideastatus (prefix + descriptive, all lowercase) β mybrn_complexitylevel β IdeaStatus (no prefix β collides) β mybrn_IdeaStatus (camelCase β use all lowercase)
See .github/instructions/07-dataverse-schema.instructions.md for idempotent setup scripts, column creation patterns, and relationship modeling.
Copilot Studio Agent Integration
Add a conversational AI agent to your Code App using the Microsoft Copilot Studio connector.
CopilotStudioService handles everything.Setup Steps
- Create & publish your agent in β Copilot Studio
- Get the agent name from Channels β Web app β connection string (e.g.
cr3e1_customerSupportAgent) - Create a connection in make.powerapps.com β Connections β + New β "Microsoft Copilot Studio"
- Add the data source:
pac code add-data-source -a "shared_microsoftcopilotstudio" -c <connectionId>
- Import & call the generated service in your React code
Invoking the Agent
ExecuteCopilotAsyncV2. The other methods (ExecuteCopilot, ExecuteCopilotAsync) have known issues β fire-and-forget or 502 errors.import { CopilotStudioService } from '@/generated/services/CopilotStudioService';
const response = await CopilotStudioService.ExecuteCopilotAsyncV2({
message: "What is the status of my order?",
notificationUrl: "https://notificationurlplaceholder",
agentName: "cr3e1_customerSupportAgent",
});
if (response.data.completed) {
console.log(response.data.lastResponse);
}
Request Parameters
| Parameter | Required | Description |
|---|---|---|
message | Yes | User's message. Can be JSON string for structured data. |
notificationUrl | Yes | Always "https://notificationurlplaceholder" |
agentName | Yes | Published agent name (case-sensitive, includes publisher prefix) |
Response Structure
| Property | Type | Description |
|---|---|---|
responses | string[] | Array of response strings from the agent |
conversationId | string | Conversation ID for multi-turn tracking |
lastResponse | string | Most recent agent response |
completed | boolean | Whether the agent finished processing |
conversationId, ConversationId, or conversationID. Always use optional chaining or a safe accessor.Prototype Mode (Mock Data)
When running npm run dev:local, create a mock that simulates agent responses with realistic delays. The instruction file includes a complete mock implementation and shows how to wire it into useCopilotAgent with the VITE_USE_MOCK toggle.
See .github/instructions/08-copilot-studio.instructions.md for the complete hook, chat UI component, structured I/O patterns, and troubleshooting.
Naming Conventions
Quick reference for file, component, and Dataverse naming.
| Type | Convention | Example |
|---|---|---|
| Components | PascalCase folder + file | components/ProjectCard/ProjectCard.tsx |
| Hooks | camelCase + use prefix | hooks/useProjects.ts |
| Utils | camelCase | utils/formatDate.ts |
| Types | PascalCase | types/ProjectExtended.ts |
| Constants | SCREAMING_SNAKE in file | constants/routes.ts |
| Tests | Same name + .test | ProjectCard.test.tsx |
| Dataverse tables | prefix_tablename | mybrn_project |
| Option sets | prefix_descriptivename | mybrn_ideastatus |
Common Connector IDs
Use these when creating deployment settings files or connection references.
| Connector | Internal Name | ConnectorId |
|---|---|---|
| Office 365 Users | shared_office365users | /providers/Microsoft.PowerApps/apis/shared_office365users |
| Office 365 Outlook | shared_office365 | /providers/Microsoft.PowerApps/apis/shared_office365 |
| SharePoint | shared_sharepointonline | /providers/Microsoft.PowerApps/apis/shared_sharepointonline |
| Microsoft Dataverse | shared_commondataserviceforapps | /providers/Microsoft.PowerApps/apis/shared_commondataserviceforapps |
| SQL Server | shared_sql | /providers/Microsoft.PowerApps/apis/shared_sql |
| Microsoft Teams | shared_teams | /providers/Microsoft.PowerApps/apis/shared_teams |
| Azure Blob Storage | shared_azureblob | /providers/Microsoft.PowerApps/apis/shared_azureblob |
| HTTP with Entra ID | shared_webcontents | /providers/Microsoft.PowerApps/apis/shared_webcontents |
To find Connection IDs in the Power Apps Maker Portal, look at the URL after creating a connection:
https://make.powerapps.com/environments/xxx/connections/shared_office365users/CONNECTION_ID/details
What's Next
After the wizard finishes, here's your daily workflow and common commands.
Daily Development
| Command | What It Does |
|---|---|
npm run dev:local | Prototype with mock data β no auth or Power Platform connection needed |
npm run dev | Connected mode β runs Vite + pac code run concurrently |
npm run build | TypeScript check + Vite production build β dist/ |
npm run test | Run unit tests with Vitest |
npm run lint | ESLint check across src/ |
Deploying
| Command | What It Does |
|---|---|
pac code push | Upload your built app to Power Platform |
npm run deploy | Shortcut: build + push in one command |
pac code push requires Power Platform API access that service principal (SPN) auth lacks for first-push and pac code mutation flows. Use the repo-scoped interactive profile for that environment, then push:
pac auth create --name pp-myrepo-d-u-1a2b3c4d --environment <your-env-url> --deviceCode pac auth select --name pp-myrepo-d-u-1a2b3c4d pac code push
Connectors & Connection References
The wizard auto-creates connection references in your solution so they travel across environments. This now happens in an explicit later connector-binding pass, not during the initial scaffold.
| Action | How |
|---|---|
| Add connectors via wizard | node wizard/index.mjs --from 8 (runs the dedicated connector-binding step) |
| Add Dataverse table manually | pac code add-data-source -a dataverse -t <table_name> |
| Add other connector manually | pac code add-data-source -a <api_id> -c <conn_id> |
| Map references after import | Power Apps Maker Portal → Solutions → Connection References |
--apiId (-a) is always required for manual adds β there is no interactive picker in the CLI. The wizard compensates for this by discovering existing environment connections with pac connection list and letting you select one when possible. See the Connector IDs table above for common API IDs.
Re-running the Wizard
Both the terminal wizard and the browser wizard (npm run wizard:ux) share .wizard-state.json, so you can switch between them freely. The browser wizard lets you jump directly to any step from the step navigator.
| Command | What It Does |
|---|---|
node wizard/index.mjs | Resume from where you left off |
node wizard/index.mjs --from 7 | Re-run from scaffold onward, including the later connector and deploy steps |
node wizard/index.mjs --from 8 | Run the dedicated connector-binding pass and select real connections |
node wizard/index.mjs --reset | Start completely fresh |
npm run wizard:ux | Open the browser wizard β jump to any step from the step navigator |
Setting Up CI/CD
See .github/instructions/04-deployment.instructions.md for full details. You'll need:
- GitHub secrets:
PP_APP_ID,PP_CLIENT_SECRET,PP_TENANT_ID - Environment variable per target:
POWER_PLATFORM_URL