Skip to content

pax8labs/pax8-cta

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

300 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Pax8 Cross-Tenant Agents (CTA)

Multi-tenant Copilot Studio deployment tool for MSPs. Short name: Pax8 CTA.

CI License: Apache 2.0 npm version


Prerequisites: Node.js

Pax8 CTA runs on Node.js, including the no-install npx path shown below. Check your version first:

node --version

If it prints v20.12 or newer (CTA's declared engines floor), you're set — continue to the next section. If it prints an older version, or you see command not found, pick one of the install paths below.

  • Any OS, no extra tooling required (recommended default): download the Node.js LTS installer from https://nodejs.org and run it. Works the same way on macOS, Windows, and Linux.
  • Windows (built-in package manager): winget install OpenJS.NodeJS.LTS — winget ships with Windows 10 and 11, no setup needed.
  • macOS or Linux with Homebrew: brew install node — installs current Node, which is fine because it's past the v20.12 floor. (Homebrew is not native on Windows; use the installer or winget instead.)
  • No Node available, or a locked-down endpoint where you can't install runtimes: skip Node entirely and use the prebuilt Standalone Binaries under Installation — they're self-contained and need nothing else on the machine.

After any install path above, close the terminal, open a new one, and re-run node --version to confirm it now reports v20.12 or newer. The reopen step matters for both the installer and winget — they update PATH for new shells, not for shells that were already open. If something goes wrong, see the Troubleshooting section below.


Try it in 30 seconds (no install)

DEMO_MODE=true npx -y -p @pax8/cta pax8-cta tenants list

That flips on demo mode (mock data, no Azure/GDAP needed) and lists the sample tenant fleet. From there, npx -y -p @pax8/cta pax8-cta --help shows every command.


What It Does

Pax8 CTA exports Power Platform solutions (Copilot Studio agents) from a source Dataverse environment and imports them into customer tenants via GDAP (Granular Delegated Admin Privileges). It handles authentication, connection reference mapping, environment variables, staged rollouts, and rollback -- so you can deploy one agent to hundreds of customer environments in minutes.

Quick Start

npm install -g @pax8/cta
pax8-cta init                    # Set up credentials and config
pax8-cta validate                # Verify GDAP access to all tenants
pax8-cta export --solution "CustomerServiceAgent"
pax8-cta deploy --solution ./exports/CustomerServiceAgent_managed.zip --all

Time to first deployment: ~15 minutes (including GDAP setup). Time per subsequent deployment: ~2 minutes, whether it's 1 tenant or 100.


Prerequisites

1. GDAP Relationships (Per Customer)

What is GDAP? Microsoft's secure delegation model that lets you access customer tenants without storing their credentials.

Setup in Partner Center:

  1. Navigate to Customers > Select your customer
  2. Go to Account > Admin relationships
  3. Click "Request a delegated admin relationship"
  4. Configure:
    • Relationship name: Pax8 CTA Deployment Access
    • Duration: 2 years (default)
    • Roles to request: Power Platform Administrator (required)
  5. Send invitation link to customer
  6. Customer approves in their Microsoft 365 Admin Center
  7. Status changes to "Active"

Once active, Pax8 CTA automatically discovers this customer. No manual tenant configuration needed.

2. Azure AD App Registration (One-Time)

In your partner tenant's Azure Portal:

  1. Navigate to Azure Active Directory > App registrations > New registration
  2. Configure:
    • Name: Pax8 CTA Deployment Tool
    • Supported account types: Accounts in any organizational directory (Multitenant)
  3. After creation, note these values:
    • Application (client) ID
    • Directory (tenant) ID (your partner tenant ID)
  4. Create client secret:
    • Certificates & secrets > New client secret
    • Copy the value immediately (shown only once)
  5. Add API permissions:
    • API permissions > Add a permission
    • Dynamics CRM > Delegated permissions > user_impersonation
    • Click "Grant admin consent"
  6. Optional -- for GDAP discovery:
    • Add Microsoft Graph > Delegated > DelegatedAdminRelationship.Read.All
    • Add PowerApps Service > Delegated > User

3. Application User (Per Customer) -- Optional

Only needed if you encounter permission issues. Most MSPs with GDAP + Power Platform Admin role don't need this.

If required, in each customer's environment:

  1. Power Platform Admin Center > Environments > [Environment] > Settings
  2. Users + permissions > Application users > New app user
  3. Add your partner app registration
  4. Assign: System Administrator or Solution Import role

Installation

From npm (recommended)

npm install -g @pax8/cta

Standalone Binaries

Pre-built binaries are available for macOS (arm64, x64), Linux (arm64, x64), and Windows (x64). Download from the Releases page, or use the install scripts:

macOS / Linux:

curl -fsSL https://raw.githubusercontent.com/pax8labs/pax8-cta/main/install.sh | bash

Windows (PowerShell):

irm https://raw.githubusercontent.com/pax8labs/pax8-cta/main/install.ps1 | iex

Integrity and OS warnings. Each release publishes a .sha256 file next to every binary, and the install scripts above verify the SHA256 checksum before installing. The binaries are not yet code-signed or notarized, so on first run you may see a Gatekeeper warning on macOS ("cannot be opened because the developer cannot be verified") or a SmartScreen warning on Windows ("Windows protected your PC"). On macOS you can clear the quarantine attribute with xattr -d com.apple.quarantine /usr/local/bin/pax8-cta; on Windows, click "More info" then "Run anyway." Signed and notarized binaries are tracked separately and will land in a future release.

From Source

git clone https://github.com/pax8labs/pax8-cta.git
cd pax8-cta
npm install -g pnpm
pnpm install && pnpm build
pnpm cli    # Run the CLI

CLI Command Reference

Run pax8-cta with no arguments to enter interactive REPL mode, or use commands directly:

Command Description
pax8-cta deploy Deploy a solution to multiple tenants
pax8-cta export Export a solution from the source environment
pax8-cta import Import a solution into a single tenant
pax8-cta validate Verify GDAP access and connectivity for all tenants
pax8-cta analyze Analyze a solution package before deployment
pax8-cta tenants List, inspect, and manage customer tenants
pax8-cta solutions List solutions in the source environment
pax8-cta agents List agents and analyze drift across tenants
pax8-cta deployments View deployment history and status
pax8-cta auth Test authentication and token acquisition
pax8-cta setup Configure credentials interactively
pax8-cta init Initialize a new Pax8 CTA project with config files
pax8-cta status Show current configuration and connection status
pax8-cta telemetry View or change anonymous usage telemetry settings

Deploy

Deploy a solution to multiple tenants at once.

# Deploy to all enabled tenants
pax8-cta deploy --solution ./exports/CustomerServiceAgent_managed.zip --all

# Deploy to tenants with a specific tag
pax8-cta deploy --solution ./exports/CustomerServiceAgent_managed.zip --tag enterprise

# Deploy to multiple tag groups
pax8-cta deploy --solution ./exports/CustomerServiceAgent_managed.zip --tag enterprise --tag pilot

# Dry run (see what would be deployed without deploying)
pax8-cta deploy --solution ./exports/CustomerServiceAgent_managed.zip --all --dry-run

Export

Export a solution from your source environment.

# Export as managed (default)
pax8-cta export --solution "CustomerServiceAgent"

# Export as unmanaged
pax8-cta export --solution "CustomerServiceAgent" --unmanaged

Import

Import a solution into a single tenant (useful for testing).

pax8-cta import --solution ./exports/CustomerServiceAgent_managed.zip --tenant <tenant-id>

Validate

Check GDAP access and environment connectivity for all configured tenants.

pax8-cta validate

Analyze

Inspect a solution package to see components, dependencies, and connection references before deploying.

pax8-cta analyze --solution ./exports/CustomerServiceAgent_managed.zip

Tenants

Manage and inspect your customer tenant fleet.

# List all tenants
pax8-cta tenants list

# Filter by tag
pax8-cta tenants list --tag enterprise

# Inspect a specific tenant
pax8-cta tenants show <tenant-id>

# Run health check
pax8-cta tenants health <tenant-id>

Solutions

List solutions available in the source environment.

pax8-cta solutions list

Agents

View deployed agents and analyze drift across tenants.

# List agents
pax8-cta agents list

# Show agent details
pax8-cta agents show <agent-id>

# Analyze agent drift across tenants
pax8-cta agents drift

JSON Output

Most commands support --format json for scripting and automation:

pax8-cta tenants list --format json
pax8-cta deployments list --format json

Configuration

Fleet Configuration (config/tenants.yaml)

version: "2.0"

partner:
  tenantId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
  clientId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

source:
  tenantId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
  environmentUrl: "https://your-dev-org.crm.dynamics.com"

tenants:
  - name: "Contoso Corporation"
    tenantId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    environmentUrl: "https://contoso.crm.dynamics.com"
    tags: ["enterprise", "wave1"]
    enabled: true

Run pax8-cta init to generate this file interactively.

Connection Reference Mapping

Map source connection references to target connections for each tenant:

tenants:
  - name: "Contoso Corporation"
    tenantId: "..."
    environmentUrl: "..."
    connectionMappings:
      - sourceLogicalName: "cr_sharepoint_connection"
        targetConnectionId: "shared-sharepoint-contoso-xxx"
      - sourceLogicalName: "cr_outlook_connection"
        targetConnectionId: "shared-office365-contoso-xxx"

Environment Variables

Configure tenant-specific environment variable values:

tenants:
  - name: "Contoso Corporation"
    environmentVariables:
      - schemaName: "cr_SupportEmail"
        value: "support@contoso.com"
        type: "String"
      - schemaName: "cr_MaxRetries"
        value: 5
        type: "Number"

Deployment Waves

Deploy in stages with health checks between waves:

settings:
  waves:
    - name: "Pilot"
      order: 1
      tenants: ["wave1", "priority"]
      maxParallel: 2
      waitAfterCompletion: "5m"
      continueOnFailure: false

    - name: "Main Rollout"
      order: 2
      tenants: ["wave2"]
      maxParallel: 10
      continueOnFailure: true

Rollback Settings

Enable automatic snapshots and rollback:

settings:
  rollback:
    enabled: true
    keepVersions: 3
    autoRollbackOnFailure: false
    rollbackTimeout: "10m"

Webhook Notifications

Send notifications to external systems on deployment events:

settings:
  webhooks:
    - url: "https://hooks.slack.com/services/xxx"
      events:
        - deployment.started
        - deployment.completed
        - deployment.failed
        - tenant.failed
      secret: "${WEBHOOK_SECRET}"
      retries: 3

Environment Variables (Shell)

Variable Description Default
PARTNER_CLIENT_SECRET Azure AD app client secret Required
CONFIG_PATH Path to tenants.yaml ./config/tenants.yaml
DEMO_MODE Enable demo mode with mock data. Per-process override that takes precedence over the persistent demo on/demo off setting false
SNAPSHOTS_DIR Directory for rollback snapshots ./snapshots

Set credentials via pax8-cta setup or export them directly:

export PARTNER_CLIENT_SECRET="your-secret"

Security and Credentials

  • No credential storage -- Pax8 CTA uses GDAP delegation and never stores customer credentials
  • Token lifecycle -- Access tokens expire after 1 hour and are automatically refreshed
  • Least privilege -- Only requests minimum required API permissions
  • Audit logging -- All operations are logged with timestamps and outcomes
  • TLS required -- All API communication is over HTTPS

Testing

Pax8 CTA has comprehensive test coverage:

  • Core library: 511 tests covering auth, config, Dataverse client, and services
  • CLI: ~180 integration tests covering all commands, error handling, and edge cases
# Run all tests
pnpm test

# CLI tests only
pnpm --filter @pax8/cta test

# CLI tests with coverage
pnpm --filter @pax8/cta test:coverage

# Single test file
pnpm --filter @pax8/cta test -- --run src/__tests__/deploy.test.ts

# Core tests only
pnpm --filter @pax8/cta-core test

Tests run in demo mode by default (no Azure AD credentials or customer tenants required).


Architecture

pax8-cta
├── packages/
│   ├── cli/             # CLI application (Commander.js)
│   │   └── src/
│   │       ├── commands/     # All CLI commands
│   │       └── index.ts      # Entry point + REPL mode
│   │
│   └── core/            # Shared business logic
│       └── src/
│           ├── auth/         # GDAP + Azure AD token management
│           ├── config/       # YAML config schema (Zod validation)
│           ├── dataverse/    # Solution export/import, connection refs
│           └── services/     # Deployment, health, rollback, webhooks, waves
│
├── config/
│   └── tenants.yaml     # Fleet configuration
└── exports/             # Exported solution files
                    ┌──────────────┐
                    │  pax8-cta   │
                    │    CLI       │
                    └──────┬───────┘
                           │
                    ┌──────▼───────┐
                    │  @pax8/      │
                    │  cta-core    │
                    └──────┬───────┘
                           │
              ┌────────────┼────────────┐
              │            │            │
       ┌──────▼──┐  ┌──────▼──┐  ┌──────▼──┐
       │ Entra ID│  │Dataverse│  │  Graph   │
       │ (Auth)  │  │  (API)  │  │(Optional)│
       └─────────┘  └────┬────┘  └──────────┘
                         │
          ┌──────────────┼──────────────┐
          │              │              │
   ┌──────▼──────┐┌─────▼───────┐┌─────▼───────┐
   │ Customer A  ││ Customer B  ││ Customer C  │
   │  Dataverse  ││  Dataverse  ││  Dataverse  │
   └─────────────┘└─────────────┘└─────────────┘

How it works: Copilot Studio agents are stored in Microsoft Dataverse (the database behind Power Platform). Pax8 CTA uses the Dataverse API to export your agent as a "solution" from your development environment, then imports it into each customer's environment using GDAP for cross-tenant authentication.


Microsoft APIs Used

Dataverse Web API

  • Purpose: Solution import/export, agent management
  • Key endpoints: ExportSolution, ImportSolution, solutions, bots, connectionreferences, environmentvariabledefinitions
  • Auth: OAuth 2.0 with delegated permissions via GDAP
  • Rate limits: 6,000 requests per 5 minutes per user (handled with exponential backoff)
  • Docs: Dataverse Web API Reference

Microsoft Entra ID (Azure AD)

  • Purpose: Authentication, cross-tenant token acquisition
  • Permissions required: Dynamics CRM > user_impersonation (delegated)
  • Grant type: client_credentials with GDAP delegation
  • Docs: Microsoft identity platform

Microsoft Graph API (Optional)

Power Platform API

Authentication Flow

sequenceDiagram
    participant CLI as Pax8 CTA CLI
    participant Entra as Entra ID
    participant GDAP as Partner Center
    participant Tenant as Customer Tenant

    CLI->>Entra: Request token (client credentials)
    Entra->>GDAP: Verify GDAP relationship
    GDAP-->>Entra: GDAP approved
    Entra-->>CLI: Access token (customer tenant scope)
    CLI->>Tenant: Call Dataverse API
    Tenant-->>CLI: Solution deployed
Loading

API Rate Limits

API Limit Handling
Dataverse Web API 6,000 requests/5min per user Exponential backoff, request queuing
Graph API 12,000 requests/10sec Batching, rate limit detection
Entra ID Token 2,000 requests/min Token caching (1hr TTL)

Troubleshooting

command not found after installing Node

  • The terminal was open before the Node installer finished, so it still has the old PATH. Close that terminal and open a new one (or run exec $SHELL), then try the command again.
  • If a fresh shell still fails, run node --version in that new shell. If node --version reports v20.12 or newer but the command you actually wanted (such as npx or a globally installed binary) is still missing, that's a real PATH issue — check where your Node install put its bin directory and make sure it's on PATH.
  • If node --version also fails in a brand-new shell, the Node install didn't actually complete. Reinstall from https://nodejs.org and let the installer finish before opening a new terminal.

EACCES / permission denied on npm install -g

  • Don't reach for sudo. It papers over the install once but leaves root-owned files in the global npm tree that npm itself can't manage later, which creates worse problems on the next upgrade.
  • The simplest fix is to skip the global install entirely and use the no-install npx path documented near the top of this README.
  • If you do want a global install, reinstall Node via a version manager (such as nvm or fnm) so the global prefix lives inside your home directory. Global installs then no longer need root.

"Failed to acquire token"

  • Verify PARTNER_CLIENT_SECRET is set correctly
  • Check that the app registration has admin consent
  • Ensure the client secret hasn't expired

"No active GDAP relationship"

  • Verify GDAP relationship is approved in Partner Center
  • Check that the relationship includes Power Platform Administrator role
  • Run pax8-cta validate to check all tenants

"Import failed: missing dependencies"

  • Ensure all solution dependencies are installed in the target environment
  • Check that the solution was exported with "Add required objects"

"Rate limited"

  • Pax8 CTA automatically retries with exponential backoff
  • For large fleets, use deployment waves to stagger the rollout

"Connection reference not found"

  • Verify the connection reference logical name matches the source
  • Ensure the target connection ID exists in the customer environment
  • Check that connections are shared with the application user

Known Limitations

  1. make.powerapps.com does not support GDAP -- All operations must go through APIs
  2. Flows with owner-only triggers -- May require manual configuration in the target environment
  3. Cross-region deployments -- May experience higher latency (API calls traverse regions)

Telemetry

Pax8 CTA can collect anonymous usage telemetry to help improve the tool. Telemetry is disabled by default and must be explicitly opted into.

What's collected (when enabled): command names, flags used (names only, not values), success/failure, execution duration, CLI version, OS platform.

What's never collected: tenant IDs, solution names, file paths, environment URLs, error messages, or any personally identifiable information.

Opt in:

pax8-cta telemetry on

Opt out:

pax8-cta telemetry off
# or
export PAX8_CTA_TELEMETRY_DISABLED=1

Telemetry is also automatically disabled in CI environments (CI=true) and respects the DO_NOT_TRACK standard.


License

Apache 2.0 -- see LICENSE for details.

Contributing

We currently use a publish-only open source model and do not accept external pull requests yet. Please see CONTRIBUTING.md for the latest policy and feedback channels.

About

Ship your copilot agents to all your tenants! Multi-tenant Copilot Studio deployment automation for MSPs

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

5 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages