Our HR Onboarding Buddy works great when you hit F5: but the moment a second developer joins the project, “deploy it from my machine” stops being a strategy. CI/CD pipelines for declarative agents follow the same principles as any application: automate everything, keep environments consistent, and never let a manual step stand between a merged PR and production.
Why CI/CD for Declarative Agents?
Declarative agents are configuration, not compiled code: but that doesn’t mean they’re immune to deployment problems. A bad instruction edit, a mismatched environment variable, or a forgotten manifest update can break the agent just as badly as a null pointer in a backend service.
CI/CD gives you:
- Consistency: Every deployment follows the same steps, regardless of who triggers it
- Speed: Push to
main, agent updates in production. No manual zip uploads. - Auditability: Every change is traceable through git history and pipeline logs
- Environment isolation: Test in dev, validate in staging, deploy to production
The Microsoft 365 Agents Toolkit CLI (atk) makes this straightforward. The same commands you run locally (atk provision, atk deploy, atk publish) work identically in a pipeline.
The Lifecycle File: m365agents.yml
Every Agents Toolkit project includes a lifecycle file that defines what happens during provisioning and deployment. This file is named m365agents.yml.
Here’s what a typical lifecycle file looks like for the HR Onboarding Buddy:
version: v1.10
provision:
- uses: teamsApp/create
with:
name: HR Onboarding Buddy-${{TEAMSFX_ENV}}
writeToEnvironmentFile:
teamsAppId: TEAMS_APP_ID
- uses: teamsApp/zipAppPackage
with:
manifestPath: ./appPackage/manifest.json
outputZipPath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip
outputFolder: ./appPackage/build
- uses: teamsApp/update
with:
appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip
publish:
- uses: teamsApp/create
with:
name: HR Onboarding Buddy-${{TEAMSFX_ENV}}
writeToEnvironmentFile:
teamsAppId: TEAMS_APP_ID
- uses: teamsApp/zipAppPackage
with:
manifestPath: ./appPackage/manifest.json
outputZipPath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip
outputFolder: ./appPackage/build
- uses: teamsApp/publishAppPackage
with:
appPackagePath: ./appPackage/build/appPackage.${{TEAMSFX_ENV}}.zip
writeToEnvironmentFile:
publishedAppId: TEAMS_APP_PUBLISHED_APP_ID
Two stages:
- Provision: Registers the Microsoft 365 app and packages the manifest
- Publish: Creates the app, zips the package, and submits to your organization’s app catalog for admin approval
For pure declarative agents, there is no deploy stage. The provision and publish stages handle everything.
Setting Up GitHub Actions
The Agents Toolkit CLI is an npm package, so any CI runner with Node.js can execute it. Here’s a complete GitHub Actions workflow that provisions and publishes the HR Onboarding Buddy on every push to main:
name: Deploy Declarative Agent
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Install Agents Toolkit CLI
run: npm install -g @microsoft/m365agentstoolkit-cli
- name: Provision
run: atk provision --env production
env:
M365_ACCOUNT_NAME: ${{ secrets.M365_ACCOUNT_NAME }}
M365_ACCOUNT_PASSWORD: ${{ secrets.M365_ACCOUNT_PASSWORD }}
M365_TENANT_ID: ${{ secrets.M365_TENANT_ID }}
- name: Publish
run: atk publish --env production
env:
M365_ACCOUNT_NAME: ${{ secrets.M365_ACCOUNT_NAME }}
M365_ACCOUNT_PASSWORD: ${{ secrets.M365_ACCOUNT_PASSWORD }}
Each step maps to a lifecycle stage in m365agents.yml. The CLI reads the lifecycle file, resolves environment variables, and executes each action in order.
Store credentials in GitHub repository secrets: never in your YAML. The M365_TENANT_ID, M365_ACCOUNT_NAME, and M365_ACCOUNT_PASSWORD values should be configured under Settings > Secrets and variables > Actions.
Validating Before Deploying
Add a validation step before provisioning to catch manifest issues early:
- name: Validate manifest
run: atk validate --app-package-file-path ./appPackage/manifest.json
This catches schema errors, missing fields, and invalid references before the pipeline spends time provisioning resources.
Azure DevOps Pipelines
The same CLI commands work in Azure DevOps. The YAML syntax differs, but the logic is identical:
trigger:
branches:
include:
- main
pool:
vmImage: "ubuntu-latest"
steps:
- task: NodeTool@0
inputs:
versionSpec: "20.x"
- script: npm install -g @microsoft/m365agentstoolkit-cli
displayName: "Install ATK CLI"
- script: atk provision --env production
displayName: "Provision"
env:
M365_ACCOUNT_NAME: $(M365_ACCOUNT_NAME)
M365_ACCOUNT_PASSWORD: $(M365_ACCOUNT_PASSWORD)
M365_TENANT_ID: $(M365_TENANT_ID)
- script: atk publish --env production
displayName: "Publish"
env:
M365_ACCOUNT_NAME: $(M365_ACCOUNT_NAME)
M365_ACCOUNT_PASSWORD: $(M365_ACCOUNT_PASSWORD)
In Azure DevOps, store secrets using pipeline variables marked as secret, or link them from an Azure Key Vault variable group. Never hardcode credentials in pipeline YAML.
Multi-Environment Strategy
Most teams need at least two environments: dev for iteration and production for the real deployment. The Agents Toolkit handles this through environment files in the env/ folder.
env/
├── .env.dev # Dev tenant app IDs and settings
├── .env.dev.user # Dev credentials (git-ignored)
├── .env.production # Production tenant app IDs
└── .env.production.user # Production credentials (git-ignored)
Each environment gets its own Entra ID app registration and Microsoft 365 app ID. When you run atk provision --env dev, the CLI reads from .env.dev. When the pipeline runs atk provision --env production, it reads from .env.production. Same lifecycle file, different targets.
This means your dev agent and production agent are completely independent: different app registrations, different tenant configurations. A bad instruction change in dev doesn’t touch production until the PR merges and the production pipeline runs.
Best Practices
Validate the manifest first. Run atk validate as the first pipeline step. A malformed manifest.json will fail the whole pipeline: catch it fast.
Use branch-based triggers. Deploy to dev on every push to feature branches, but only promote to production on merges to main. This is standard CI/CD hygiene, and it applies to declarative agents just as much as any other project.
Pin the CLI version. Instead of always installing the latest CLI, pin a version to avoid surprise breaking changes:
npm install -g @microsoft/[email protected]
Review instruction changes in PRs. Your agent’s instructions file has more impact on user experience than most code changes. Treat instruction diffs with the same scrutiny you give API contracts.
Keep secrets out of environment files. The .env.*.user files are git-ignored by default: never commit them. In your pipeline, inject credentials through repository secrets (GitHub) or variable groups (Azure DevOps), not through checked-in files.
The Value of Automated Agent Deployments
Setting up a CI/CD pipeline for your declarative agent is a one-time investment that pays dividends on every release:
- No more “works on my machine”: Every deployment follows the same
atk provisionandatk publishsequence, executed identically by the pipeline runner on every merge. Human error in manual deployment steps is eliminated. - Full traceability: Every agent update is tied to a git commit, a pull request, and a pipeline run. You know exactly who changed what, when, and why.
- Safe iteration: Isolated dev and production environments mean you can experiment freely on a feature branch, validate in dev, and only promote to production when a PR merges. A bad instruction change in dev never touches production until you explicitly let it through.
- Fast releases: Push to
mainand the agent is provisioned and published automatically. No manual zip uploads, no Admin Center clicks, no waiting for someone with the right permissions to be online. - Scalable governance: As your agent estate grows, the same pipeline pattern applies to every new agent. Validation before provisioning, pinned CLI versions, and secrets in vault: these practices scale from one agent to fifty.
Treat your declarative agents like any other production software. The tooling is already there.
Resources
- Agents Toolkit CLI: Microsoft Learn
- Provision and Deploy with Agents Toolkit: Microsoft Learn
- Publish Agents Built with Agents Toolkit: Microsoft Learn
- Microsoft 365 Agents Toolkit Overview: Microsoft Learn
- Declarative Agents Overview: Microsoft Learn
- Microsoft 365 Copilot Extensibility: Microsoft Learn
Have questions or want to share what you’re building? Connect with me on LinkedIn.
Have questions or want to share what you're building? Connect with me on LinkedIn or check out more on The Manifest.