Skip to content
| Marketplace
Sign in
Visual Studio Code>Extension Packs>BCDR AssistantNew to Visual Studio Code? Get it now.
BCDR Assistant

BCDR Assistant

MSD- Solutions Engineering

|
43 installs
| (5) | Free
This is an internal Microsoft extension for Business Continuity and Disaster Recovery (BCDR) use.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

BCDR Assistant

Helps a BCDR Champ check assessment status and service hygiene, and prepare a service's DR plan (Minimal DRP) document without leaving VS Code. It detects your service's ServiceTree ID from the repo, checks the BCDR assessment due status, runs read-only hygiene / DHE data-quality pre-checks, and generates the pre-filled Minimal DRP .docx — optionally reusing the §7/§8 content from a previously approved plan.

Read-only and advisory: it reads Service Tree + BCDR Manager (Kusto) and writes a document. It never submits, approves, or mutates anything.

Features

  • Auto-detects the ServiceTree ID from repo markers (see below); falls back to a picker when none is found.
  • Assessment status — shows whether the service's BCDR assessment is due / due soon.
  • Data-quality pre-check — surfaces open BCDR hygiene issues and Service Tree DHE (Data Handling Exception) violations before you generate.
  • Production dependency overview - the service panel shows BCDR dependencies below the data-quality pre-checks, with counts for Critical, Mapped non-critical, and Missing mapping dependency/provider groups. Expandable lists show critical, non-critical and unmapped groups with resource types and regions. Each list starts collapsed with its count visible. Select a non-zero count link to expand and focus its corresponding list; zero counts are not links. Missing mappings keep a red border/background, with theme-adjusted text for readable contrast in light, dark and high-contrast themes. Missing or unrecognized classifications are labeled Missing mapping / classification, never assumed to be Critical. If nothing is mapped, the panel warns that the generated plan's resource and recovery sections may be empty. This advisory UI does not change the generated document, classify dependencies, or add a generation gate.
  • Generates the Minimal DRP .docx — pre-filled from Service Tree + BCDR Manager dependencies (the §2 Resiliency Overview auto-sorted into Platform Managed / Service Controlled-Configured / One Instance), saved in the extension's local storage.
  • Generated document actions — a dialog offers Save to OneDrive, Open, and Reveal and stays visible until you select an action or explicitly cancel. Feedback is offered in a separate modal after you respond and the selected action finishes. If you choose Save to OneDrive, it waits for the upload result, not for the OneDrive link notification to be dismissed. Local documents are not listed in the service panel; BCDR: Open Generated DR Plan remains available for recovery.
  • Pending document review - the generation dialog and service panel list what still needs attention: remaining placeholder fields, template/example text, service data and recovery-step review, fresh evidence, and choosing the applicable failback attestation while deleting the unused alternative. The placeholder count comes from the generated DOCX; the review tasks remain human confirmations. The panel shows the latest saved plan for the current service as a generation-time snapshot, retained after reopening. Later Word edits do not automatically update it.
  • Thumbs feedback - thumbs-up and thumbs-down buttons in the panel and the post-generation prompt save the choice in the local VS Code profile, queue a vote through existing usage telemetry, then open the Marketplace feedback page. The selected thumb stays highlighted after refresh and reload. Repeating the same vote does not queue another event; switching thumbs updates the existing vote. The feedback prompt appears after each newly saved document, including when a previous choice exists. Failed/cancelled generation and reopening an existing document do not trigger another prompt. A failed OneDrive upload still leaves the local document saved and eligible for feedback.
  • Prior-plan reuse (optional) — point it at a previously approved DR plan (SharePoint link or local .docx) to reuse the §7 recovery/validation and §8 failback steps. Fresh test evidence is always required and never reused.

Checking dependency mappings

Open BCDR: Check DR Assessment Status for the selected service. The BCDR dependencies section uses the same production dependency lookup as document generation; it requires access to the BCDR Manager Kusto database and VPN/corpnet where applicable. Counts represent dependency/provider groups, not individual Azure resource instances.

  • Nothing mapped: detected groups are NotMappedInBcdr. Map or classify them and confirm their criticality in the Dependencies tab in BCDR Manager, then choose Refresh dependencies before regenerating the plan. Until this is complete, the generated plan's resource and recovery sections may be empty.
  • Mapped, but no Critical dependencies: the panel distinguishes this from missing mappings. Confirm that the non-critical classifications are appropriate.
  • No production dependencies returned: this is a successful empty lookup, not proof that the service has no Azure resources. Check production subscription registration and dependency mappings.
  • Lookup unavailable: an access, sign-in, network or response error is shown instead of zero counts. Resolve it and refresh; a failed lookup is never presented as an empty resource inventory.

Opening the panel, selecting another service, or choosing Refresh dependencies loads fresh data. Refresh uses the existing panel-refresh flow, including the other pre-checks. Background status-bar checks do not perform this extra lookup. The overview uses no LLM and never changes BCDR Manager data. Keyboard focus stays on a named loading status while refreshing, then returns to the refresh button that started the request. Focus is not carried to another service, and subsequent user interaction cancels that focus restoration.

Commands

Open the Command Palette (⇧⌘P) and run:

Command What it does
BCDR: Generate DR Plan Document Generates the Minimal DRP .docx with Open, Reveal, and Save actions.
BCDR: Open Generated DR Plan Opens a saved local plan, including surviving temporary documents from older versions.
BCDR: Check DR Assessment Status Shows the assessment due status for the service.
BCDR: Select Service (ServiceTree ID) Manually set/override the ServiceTree ID.

ServiceTree ID detection

The extension auto-detects the ID from these files (highest confidence first):

File Field
agency.json / catalog.json .serviceTreeId
Ev2 ServiceModel.json .serviceMetadata.serviceIdentifier
es-metadata.yml / .yaml accountableOwners.service
*.bicep / ARM template a serviceTreeId-style tag
azure.yaml / azure.yml serviceTreeId:

If none is found (or the folder isn't a service repo), it asks you to pick the service.

Requirements

  • Python 3 — the extension shells out to shared Python scripts.
  • Sign-in — a Microsoft work-account sign-in (Accounts menu) or az login for the Kusto / Service Tree lookups.
  • The shared scripts/ folder — bundled with the plugin, not inside the .vsix. When installed from a .vsix, set bcdr.scriptsPath (see below).
  • Prior-plan reuse (optional) — Playwright (pip install playwright) + signed-in Edge to download a SharePoint plan. Sensitivity-labeled (MIP-encrypted) docs can't be parsed — save an unprotected copy and select it as a local .docx.

BCDR Manager MCP setup (optional, Windows)

The MCP server can retrieve a prior DR-plan URL that the Kusto export redacts. It requires Windows for MSAL's native token broker (WAM), Node.js, access to the internal AAET npm feed, and BCDR Manager API authorization. macOS/Linux continue to use Kusto. Contact bcdrmanagerdev@microsoft.com if API calls return 403.

Run these steps in PowerShell under your normal Windows account (no administrator session required). Keep credentials in your user-level npm configuration, never in the repository.

  1. Install the Azure Artifacts authentication helper, configure only the BCDR scope, and authenticate to the feed:
npm.cmd install --global vsts-npm-auth
npm.cmd config set '@bcdrmanager:registry' 'https://msazure.pkgs.visualstudio.com/_packaging/AzureAsiaEngineeringTeam/npm/registry/' --location=user
vsts-npm-auth -config "$env:USERPROFILE\.npmrc"

Use your configured corporate npm mirror where required; do not disable TLS verification if direct access to the public registry is blocked.

  1. Install the server in a stable directory. --save-exact pins the resolved version; rerun this command only when deliberately installing/upgrading the server:
npm.cmd install --prefix "$env:USERPROFILE\.bcdrmanager\runtime" --save-exact @bcdrmanager/mcp-server@latest
  1. Sign in to production through WAM. Complete any account/MFA prompts yourself:
node "$env:USERPROFILE\.bcdrmanager\runtime\node_modules\@bcdrmanager\mcp-server\dist\index.js" login prod
  1. For Copilot, the workspace MCP configuration launches the installed server with node. Run MCP: List Servers, select bcdr, and start it after reviewing the trust prompt. Use Show Output for diagnostics. After fixing an earlier extension lookup failure, run Developer: Reload Window to reset its session-level MCP fallback flag.

The extension also launches this same installed entry point directly. It never runs npx or installs packages during a lookup: feed credentials are required only for installation/upgrades, not reconnection. An npm E401 during installation means feed authentication needs refreshing; it does not establish whether API access is granted. A successful initialize/tool listing verifies the local server, while a successful read-only tool call is needed to verify API authorization.

For a custom install, set BCDR_MCP_SERVER_PATH in the extension process environment and update the Copilot configuration to the same entry point. BCDR_API_BASE_URL (or the Python script's --base-url) can select another environment; the WAM login environment must match. Copilot's env is separate from the extension's process environment. Production is the default. Avoid login --force unless intentionally clearing cached credentials.

The full upstream instructions are in the BCDR MCP Server README.

Settings

Setting Default Description
bcdr.autoRun true Auto-detect the service and check status on repo open / active-file change.
bcdr.dueSoonThresholdDays 30 Show a "due soon" warning when the assessment is due within this many days.
bcdr.pythonPath (auto) Path to the Python 3 interpreter. When empty, auto-detects py -3 / python / python3 (Windows) or python3 / python (macOS/Linux).
bcdr.scriptsPath "" Path to the shared scripts/ folder. Required for a .vsix install (the scripts aren't bundled); when empty, resolved relative to the install location.
bcdr.useBcdrManagerMcp true Try the pre-installed Windows BCDR Manager MCP for prior-plan URLs; fall back to Kusto when unavailable. See the setup steps above.

Privacy & telemetry

This extension collects anonymous usage telemetry to improve reliability, sent via @vscode/extension-telemetry. It honors your VS Code telemetry setting — set telemetry.telemetryLevel to off to turn off all collection.

  • Collected: which BCDR commands run and their step outcomes, coarse error categories, operation durations, a single AI token total per run with availability status and anonymous document-run IDs, and feedback votes (up/down) with their entry point (panel/generation), a random anonymous vote-record ID, and its revision.
  • Not collected: file contents, ServiceTree IDs / other GUIDs (redacted to [GUID_REDACTED]), access tokens, or personal data.

Data is handled per the Microsoft Privacy Statement.

Tokens per document-generation run

Each invocation of BCDR: Generate DR Plan Document has a distinct anonymous flowId (generation_ plus 32 random hexadecimal characters). This identifies a document-generation attempt, not a user or the longer-lived bcdr.sessionId. The final bcdr/generate-completed event carries the AI request totals for that run, including requests whose response could not be parsed or whose document failed to generate. It is emitted before the Open/Reveal/OneDrive dialog, so those actions do not extend the accounting window. serviceTreeToDocumentMs measures from the start of the Service Tree fetch to the document-generation outcome; it is absent for runs cancelled before that fetch.

Service Tree, dependency and prior-plan reads, and Python DOCX assembly do not call an LLM and add no LLM tokens. AI drafting is currently the only model call. Both input messages (instructions and fetched facts) and the full observed response are counted through the selected model's countTokens API. Counts are model-tokenizer estimates, not provider-reported or billable usage: hidden reasoning, provider prompt wrapping, caching, and provider-side retries are not exposed by this API. No prompt text, response text, or authentication tokens are included in telemetry.

Field Meaning
totalTokens One combined total for the entire document-generation run, including all input and output tokens. Present only when every request has complete counts and a complete response. Zero when no AI request ran.
tokenCountSource vscode-countTokens, or none when no AI request ran.
tokenCountStatus complete, unavailable, or not-used. Missing/failed counts are never converted to zero.

Counting failures do not prevent document generation. Input and output counting each have a two-second timeout. They are combined internally: no input/output breakdown or per-request token totals are emitted. Only the final bcdr/generate-completed event records totalTokens, avoiding double counting between step and completion events. Token totals are available in Output > BCDR Telemetry as totalTokens on the bcdr/generate-completed event. The service panel and feedback popup do not display token counts. Saved totals remain in the local generation history, and the hidden local-document list stays hidden. Remote delivery continues to honor the existing VS Code telemetry setting and ingestion controls.

Before enabling an ADX dashboard, have the 1DS/GDPR catalog owner review and register tokenCountSource, tokenCountStatus, totalTokens, and serviceTreeToDocumentMs on bcdr/generate-completed. Confirm that flowId is approved and retained on start, step, and completion events. Do not bypass catalog filtering; a local event or a new VSIX does not prove these fields were ingested. Old releases have no token measurements, and their exact historical usage cannot be reconstructed from document counts or prompt character counts.

After catalog approval and a fresh run of the updated extension, use this query for one row per attempt. It tolerates normalized property/measurement casing and deduplicates delivery by run ID; unknown counts remain null rather than zero.

RawEventsVSCodeExt
| where ClientTimestamp > ago(99d)
| where ExtensionName == "bcdr.bcdr-dr-plan-assistant"
| where EventName endswith "bcdr/generate-completed"
| project Timestamp = ClientTimestamp, Received = ServerTimestamp,
  RunId = coalesce(tostring(Properties["flowid"]), tostring(Properties["flowId"])),
  Success = tostring(Properties["success"]),
  TokenCountStatus = coalesce(tostring(Properties["tokencountstatus"]), tostring(Properties["tokenCountStatus"])),
  TotalTokens = tolong(coalesce(Measures["totaltokens"], Measures["totalTokens"]))
| where RunId startswith "generation_" and isnotempty(TokenCountStatus)
| summarize arg_max(Received, *) by RunId
| project Timestamp, RunId, Success, TotalTokens, TokenCountStatus
| order by Timestamp desc

For an aggregate token total, include only complete or not-used rows and report the number of unavailable attempts alongside it. Otherwise the total can hide missing usage. Add where Success == "true" only when intentionally excluding AI usage spent on unsuccessful document-generation attempts.

Services catered: ingestion requirements

The distinct-service metric needs the hashed Service Tree identifier emitted by serviceProps() in src/telemetry/instance.ts. This is not the number of events, users, sessions, or documents.

On 2026-09-25, the local BCDR Telemetry log contained bcdr.serviceHash on a precheck at 19:48:10.702Z. The matching row in cluster https://ddtelvscode.kusto.windows.net, database VSCodeExt, table RawEventsVSCodeExt lacked it. Its NovaProperties["GdprPropertyTags"] included dropped_unclassified_props and dropped_suppressed_props. This points to ingestion-side privacy/catalog filtering; the tags describe the event and do not identify which rule dropped this field.

Before this metric can work, the owner of the extension's 1DS/GDPR telemetry catalog and the VS Code telemetry ingestion pipeline needs to:

  1. Review and register the string property bcdr.serviceHash (normalized in ADX as bcdr.servicehash) for bcdr/precheck, bcdr/generate-started, and successful bcdr/generate-completed events. The full event prefix is bcdr.bcdr-dr-plan-assistant/.
  2. Approve the appropriate classification, purpose, and retention for this stable hashed service identifier, and confirm it is not suppressed by policy. Its shape is svc_ plus 16 hexadecimal characters; the raw Service Tree GUID is not sent. Hashing alone does not replace the privacy review.
  3. Deploy the approved catalog/mapping update for the existing ingestion route. Adding a local telemetry.json or rebuilding the VSIX alone does not update that server-side catalog. Do not rename the field to bypass classification or disable privacy controls.
  4. Run BCDR: Check DR Assessment Status and compare its fresh local telemetry event with the ingested row. Verify that the service-hash field survives before relying on the distinct-service dashboard tile.

Use this coverage check after the catalog update:

RawEventsVSCodeExt
| where ClientTimestamp > ago(1d)
| where ExtensionName == "bcdr.bcdr-dr-plan-assistant"
| where EventName endswith "bcdr/precheck"
   or EventName endswith "bcdr/generate-started"
| extend ServiceHash = coalesce(
   tostring(Properties["bcdr.servicehash"]),
   tostring(Properties["bcdr.serviceHash"])
)
| summarize Events = count(),
   EventsWithServiceHash = countif(isnotempty(ServiceHash)),
   RecordedServices = dcountif(ServiceHash, isnotempty(ServiceHash))

RecordedServices = 0 with EventsWithServiceHash = 0 means the metric lacks coverage, not that no services used the extension. Historical rows without a service identifier cannot yield a reliable distinct-service total on their own.

Feedback counts in Kusto

New or changed selections emit bcdr/feedback with Properties["vote"] set to up or down and Properties["source"] set to panel or generation. A random Properties["voteId"] identifies one logical vote in this local VS Code profile; Properties["revision"] increases when its choice changes. The existing extension telemetry pipeline carries these events into RawEventsVSCodeExt. The extension does not directly write to Kusto or require a database credential.

Use the latest revision per vote ID, not a raw row count. This collapses changed votes and duplicate deliveries. Latest votes received over the last 99 days:

RawEventsVSCodeExt
| where ClientTimestamp > ago(99d)
| where ExtensionName == "bcdr.bcdr-dr-plan-assistant"
| where EventName endswith "bcdr/feedback"
| extend Vote = tostring(Properties["vote"]),
         VoteId = tostring(Properties["voteId"]),
         Revision = tolong(Properties["revision"])
| where isnotempty(VoteId) and Revision > 0 and Vote in ("up", "down")
| summarize arg_max(Revision, Vote) by VoteId
| summarize ['Thumbs up'] = countif(Vote == "up"),
            ['Thumbs down'] = countif(Vote == "down")

Latest votes by the day of their last update (not historical daily snapshots):

RawEventsVSCodeExt
| where ClientTimestamp > ago(99d)
| where ExtensionName == "bcdr.bcdr-dr-plan-assistant"
| where EventName endswith "bcdr/feedback"
| extend Vote = tostring(Properties["vote"]),
         VoteId = tostring(Properties["voteId"]),
         Revision = tolong(Properties["revision"])
| where isnotempty(VoteId) and Revision > 0 and Vote in ("up", "down")
| summarize arg_max(Revision, Vote, ClientTimestamp) by VoteId
| summarize ['Thumbs up'] = countif(Vote == "up"),
            ['Thumbs down'] = countif(Vote == "down") by bin(ClientTimestamp, 1d)
| order by ClientTimestamp asc
| render columnchart

Counts represent logical votes received, not verified unique people. Separate profiles/devices, cleared extension storage, or simultaneous independent extension hosts creating their first vote can produce separate IDs; no signed-in identity or server-side uniqueness enforcement is used. Telemetry is append-only and delivery is best-effort, so the queries above provide deduplicated totals rather than an exactly-once guarantee for raw rows. Earlier events without a vote ID cannot be reliably deduplicated and are excluded.

The choice is saved locally even with usage telemetry disabled. Events require telemetry.telemetryLevel: all and may take time to appear after ingestion. A later explicit click can queue an unreported revision after telemetry is enabled or a send error is resolved; its ID and revision remain the same. The feedback page still opens for repeated choices, opt-out, or telemetry errors. Dismissal or Not now emits no vote and does not open the page. A thumbs selection does not submit a Marketplace review; written feedback remains the user's choice there.

Local VSIX updates

VS Code identifies updates by the extension version, not the VSIX filename. For a new local release, bump the patch version and build a version-named package with:

npm.cmd run package:next -- --skip-license

This updates package.json and package-lock.json together without creating a commit or tag. Run it once per new release. To retry a failed build without another version bump, use npm run package -- --skip-license. Azure Pipelines continues to assign its own version before calling normal packaging.

Marketplace publishing

The existing bcdr.bcdr-dr-plan-assistant listing has been released manually. Automated updates retain that publisher and extension identity; a new publisher is not needed. The automation identity still needs its own Contributor permission on the public bcdr Marketplace publisher.

azure-pipelines.yml includes the publish-only stage template .pipelines/publish.yml. publishToMarketplace defaults to true: a successful main run signs and publishes the tested bcdr-vsix artifact. Updates to main, including merged pull requests and direct pushes, trigger CI. batch: true groups changes arriving while a run is active, and pr: none disables PR-update runs. Manual runs remain available in Azure DevOps; set publishToMarketplace: false for a build-only run. Other branches cannot publish.

It follows the supplied public-release example's sequence: vsce generate-manifest, ESRP VSCodePublisherSign, and local signature/hash verification, then vsce publish --azure-credential --packagePath with the original VSIX. The bcdr listing was rejected when attaching a publisher signature because Marketplace currently permits that only for eligible first-party extensions. The upload therefore omits --manifestPath and --signaturePath; ESRP signing and signature verification remain mandatory CI checks, not a publisher signature attached to the Marketplace package. It consumes the validated bcdr-vsix artifact without rebuilding it. It keeps Node 22, pinned tools, locked dependencies, internal dependency feeds, and the existing 1ES security scans. Publish does not compile, test, bump the version, or package another VSIX. The original Build artifact remains available as bcdr-vsix; 1ES release jobs cannot declare artifact outputs. The detached manifest and signature are used for CI verification, not uploaded or exported as an artifact. Release jobs also cannot check out source. Build packages its already-installed, locked npm publishing tools into the separate bcdr-publish-tools artifact. Publish consumes that archive through job inputs and runs the CLI from the extracted tools directory, without a checkout or another dependency installation. Both the tools artifact and its packaging step are included only for main release builds.

Required one-time setup:

Parameter Required configuration
publishServiceConnection Defaults to SecurePublishPythonExtension_ServiceConnectionWithMI from the supplied example. Authorize it for this pipeline and grant its identity Contributor access on the public bcdr publisher. Private-feed access is not sufficient.
publishEnvironment Defaults to marketplace-publish. The environment must exist and allow this pipeline. Existing approval/check policies remain enforced; the YAML does not remove them.
esrp Uses the supplied MyHub-ESRP-PME connection, identity, vault, certificate, and CP-401405 detached-manifest VSCodePublisherSign operation. The signing owner must authorize their use by BCDR and this pipeline.

The example's connection names and signing values are populated, not proof of access. Both service connections must be present/shared in the pipeline's Azure DevOps project and authorized for it. Confirm public distribution approval for the bundled contents, license/EULA, notices, documentation, and telemetry disclosures before running a release. --skip-license during packaging does not waive that review. No PAT or client secret is required or stored here.

This release supports stable versions only. Reconcile the pipeline version counter with any manually published versions; changing its seed does not reset an existing counter. The old .pipelines/publish-public.yml draft is not linked or used by this release flow. The private publisher mirror and its pause flag do not apply to public Marketplace publishing.

See the official secure automated publishing guidance for managed identity federation and publisher membership. No PAT or client secret is required by this template.

Pipeline requirements

azure-pipelines.yml uses the Official 1ES template to test, version, package, and retain the self-contained VSIX as the bcdr-vsix artifact. The publish stage signs that same VSIX and uploads it to the existing public Marketplace listing. The extension publisher stays bcdr in package.json, independently of the Entra publishing identity.

The shared pool default, Azure-Pipelines-1ESPT-ExDShared, comes from the supplied pipeline example, together with its existing ubuntu-latest and windows-latest image aliases. Confirm that your Azure DevOps project/pipeline is authorized to use that pool; a name in another pipeline does not grant BCDR access.

CI and manually queued builds need usable pool defaults. ubuntu-latest and windows-latest are pool image aliases here, not a request for arbitrary Microsoft-hosted agents: confirm they exist in your 1ES Hosted Pool, including the Windows image required by source analysis. The tests accept configured defaults and check parameter wiring rather than requiring REPLACE_ values to remain forever.

The dependency-install step sets NPM_CONFIG_REGISTRY to the Microsoft mirror https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/. The lockfile uses Microsoft mirror tarball URLs, not direct registry.npmjs.org downloads, which can be blocked on the governed agent. Keep npm ci --include=optional and the checked-in lockfile; do not delete it, change integrity hashes, disable TLS validation, or use elevated permissions to work around a network FetchError. When updating dependencies, use the configured internal registry and run the pipeline tests, which reject public-registry URLs reintroduced into the lockfile.

The Python dependency step similarly sets PIP_INDEX_URL to https://packagefeedproxy.microsoft.io/pypi/simple/ for both runtime and CI requirements. Connection retries followed by No matching distribution found can indicate an unreachable index rather than a nonexistent package version. Keep TLS verification enabled and do not add a public extra index to bypass governed-agent restrictions. Fix dependency installation first when later xmlrunner or missing-test-result errors follow it.

Configuration preflight

.pipelines/validate_pipeline.py checks configuration before dependency installation and again immediately before npm version. It uses only the Python standard library and never modifies files, authenticates, or calls cloud services. It rejects blank/placeholder pool and image values, malformed or stale versions, and incomplete release configuration. Build-only runs do not require signing credentials. Successful main builds validate configured signing fields and the detached-manifest operation; PR and other-branch builds skip release settings. The runtime WIF, tenant, signature, artifact-integrity, and environment checks remain in place.

An invalid pool can prevent agent allocation before any script runs. Run this check locally before queuing the pipeline, using a JSON object with the parameter values you intend to submit. A build-only parameter file has this shape; replace every angle-bracket value with an actual pool/image name:

{
  "versionMajorMinor": "0.2",
  "versionPatchSeed": 35,
  "hostedPool": "<team-owned-1es-pool>",
  "linuxImage": "<linux-image-alias>",
  "windowsImage": "<windows-image-alias>",
  "publishToMarketplace": false
}
py -3 -B .pipelines/validate_pipeline.py --parameters "C:\path\team-pipeline-parameters.json" --version 0.2.35

For publishing, add the release parameters from the table above, including esrp. Do not put access tokens or secrets in this file. In CI, the same check reads PIPELINE_PARAMETERS and the resolved EXTENSION_VERSION; --ci also enforces the actual branch/build reason.

A pass validates the shape and consistency of the supplied values, not whether the pool/images exist, identities have access, approvals are configured, signing operations are approved. Those still require team confirmation and an authorized Azure DevOps run. The preflight does not query the Marketplace's highest published version.

To run the pipeline:

  1. Merge a pull request into main to trigger Build and Publish; direct pushes to main also trigger CI. PR updates alone do not trigger this YAML. For an explicit manual run, select Run pipeline in Azure DevOps and choose main. Use publishToMarketplace: true to publish, or false for a build-only run. Separately configured UI trigger overrides or branch-policy validation can affect triggering.
  2. Set versionMajorMinor and versionPatchSeed for the release series. Defaults start a fresh counter at 0.2.35, above the current source version 0.2.34. The counter is scoped to the pipeline and version base and advances on every newly queued run. Use a seed above all existing releases when creating a pipeline; changing the seed does not reset an existing counter. Preflight rejects a computed version that is equal to or older than the checked-in manifest before it can be overwritten. If an existing counter is behind, inspect its state and latest published versions; coordinate advancing that release series or choose an unused higher major/minor base. Do not assume increasing the seed will advance an already-running series. Keep the default fresh-counter version ahead of future checked-in manifest bumps. Version changes occur only in the build workspace, without commits or tags, and the same version is used during Publish.
  3. Authorize the publishing/signing connections and environment before the first release. Any environment approval still applies. The original Build artifact is retained as bcdr-vsix and uploads target the public Marketplace.

Python test results are published to the Azure DevOps Tests tab even when tests fail. The pipeline/YAML tests require the CI-only dependencies in .pipelines/requirements.txt; these are not bundled into the VSIX. GitHub Actions remains build-only.

Notes

  • Installed from a .vsix? The Python scripts/ live in the plugin, not the package — set bcdr.scriptsPath to …/bcdr-presubmit-validator/scripts.
  • If commands keep prompting for sign-in, run az login once (the CLI caches a long-lived token that the scripts reuse).
  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft