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.
- 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.
- 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
- 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
- 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:
- 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/.
- 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.
- 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.
- 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:
- 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.
- 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.
- 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).