Skip to content

Add trust-separated 1ES package and release-staging entry points - #28003

Draft
Justin Chung (jshigetomi) wants to merge 46 commits into
PowerShell:masterfrom
jshigetomi:pipeline/1es-consume-engineering-system
Draft

Justin Chung (jshigetomi) wants to merge 46 commits into
PowerShell:masterfrom
jshigetomi:pipeline/1es-consume-engineering-system

Conversation

@jshigetomi

@jshigetomi Justin Chung (jshigetomi) commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Completes the PowerShell-owned entry points for the 1ES migration while keeping reusable stages/jobs/steps in PowerShell-Engineering-System PR 41545.

This revision:

  • retains the canonical NonOfficial package entry point, .pipelines/1ES/NonOfficial/PowerShell-Packages-NonOfficial-1ES.yml;
  • removes only the redundant lightweight .pipelines/1ES/NonOfficial/PowerShell-Packages-NonOfficial.yml;
  • adds .pipelines/1ES/Official/PowerShell-Release-Official-1ES.yml as a non-publishing Official validation/staging counterpart;
  • corrects the Official package producer to PowerShell-Build-Official.yml (definition 4253); and
  • replaces per-SHA Engineering System pins with a protected-main consumption contract.

The product-owned support changes also widen prerelease/rebuild tag validation beyond two digits and exclude generated CodeSignSummary-*.md files from signed payload handling/source control. No legacy OneBranch pipeline YAML is modified.

Engineering System ref contract

Azure Pipelines supports a compile-time expression in resources.repositories[*].ref. It resolves the requested branch once, before external template expansion, and uses the resulting concrete commit for that run. A REST lookup from a job cannot choose the template version because jobs start after compilation.

NonOfficial entry points expose:

- name: PowerShellEngineeringSystemRef
  type: string
  default: refs/heads/main

and consume it with:

ref: ${{ parameters.PowerShellEngineeringSystemRef }}

This permits intentional pre-merge/canary previews while keeping the committed default on Engineering System main.

Official entry points expose no ref parameter and use a static ref: refs/heads/main. This deliberately avoids an arbitrary queue-time template-ref input in Official release infrastructure. The Engineering System main branch is treated as protected production supply-chain infrastructure, and changes there must remain compatible with all supported PowerShell servicing branches.

Entry points

Official:

  • .pipelines/1ES/Official/PowerShell-Coordinated_Packages-Official.yml
  • .pipelines/1ES/Official/PowerShell-Packages-Official.yml
  • .pipelines/1ES/Official/PowerShell-Release-Official-1ES.yml

NonOfficial:

  • .pipelines/1ES/NonOfficial/PowerShell-Coordinated_Packages-NonOfficial.yml
  • .pipelines/1ES/NonOfficial/PowerShell-Packages-NonOfficial-1ES.yml
  • .pipelines/1ES/NonOfficial/PowerShell-Release-NonOfficial.yml
  • .pipelines/1ES/NonOfficial/apiscan-gen-notice.yml

PowerShell retains parameters, pipeline/repository resources, 1ES extends configuration, SDL settings, product scripts/assets, and deliberate @self callbacks. The load-bearing alias remains PowerShellEngineeringSystem; jobs continue to use checkout: self.

Package and release-staging contract

Both release entry points reuse /templates/1ES/stages/PowerShell-Release-Staging-Stages.yml and the established PSPackagesOfficial alias:

  • NonOfficial source: PowerShell-Packages-NonOfficial-1ES (definition 4273).
  • Official source: PowerShell-Packages-Official-1ES (definition 4267).

The Official entry point extends v1/1ES.Official.PipelineTemplate.yml and disables CI, PR, and pipeline-resource triggers. A caller must explicitly select the package run. Both variants retain SDK, .NET global-tool, framework-dependent package validation, and WinGet-input staging only.

There are no public-release, publishing, submission, signing, or upload stages in this release-staging graph. The WinGet artifact remains inspection-only and marked DO-NOT-RELEASE.

Validation

  • All seven PowerShell entry points and all 27 Engineering System templates parse successfully.
  • Static checks confirm four NonOfficial entry points each define exactly one PowerShellEngineeringSystemRef, default it to refs/heads/main, and use ${{ parameters.PowerShellEngineeringSystemRef }}.
  • Static checks confirm all three Official entry points expose no such parameter and use exactly one static ref: refs/heads/main.
  • No full Engineering System SHA pin or stale repin instruction remains in the changed YAML/docs.
  • NonOfficial previews used PowerShell refs/heads/rebuild/v7.6.99-rebuild.4 at b54b0d4fcc28c023cf78736e0f42c923601c3bf9 and overrode the Engineering System ref to refs/heads/pipeline/1es-powershell-templates:
    • 4228 Apiscan-Gen-Notice-NonOfficial: run ID -1; stages APIScan, notice, SDLSources.
    • 4229 PowerShell-Build-NonOfficial: run ID -1; stages prep, macos, linux, windows, test_and_release_artifacts, SDLSources.
    • 4273 PowerShell-Packages-NonOfficial-1ES: run ID -1; source PowerShell-Build-NonOfficial; package stages preserved.
    • 4274 PowerShell-Release-NonOfficial-1ES: run ID -1; source PowerShell-Packages-NonOfficial-1ES; validateSdk, gbltool, fxdpackages, and stage_winget_release_inputs preserved.
  • The Engineering System feature branch was unchanged at 00bd012711cfa337944f685b26f1ee30cab57877 before and after the previews; each expanded YAML records the requested branch ref. Azure's preview response does not expose resources.repositories.*.version, so the branch-tip check plus successful expansion is the available preview-only SHA evidence.
  • Every expanded NonOfficial YAML has zero occurrences of EsrpCodeSigning@5, EsrpTestConnectionName, PowerShell-ESRP-Release, EsrpRelease, EsrpSigning-PowerShell, EsrpSigningTest-PowerShell, test KeyCodes CP-466277/CP-466279, and ProductionReadinessCheck.
  • The pre/post build list is unchanged; all previews returned ID -1, so no build was queued.
  • Official consumers were checked statically only. No Official pipeline was previewed or queued.

Merge order

  1. Merge Engineering System PR 41545 so the referenced templates exist on protected main.
  2. Re-preview the four NonOfficial definitions using their committed refs/heads/main default.
  3. Merge this PR.

Until step 1, the feature-branch parameter override is required for NonOfficial preview. This PR remains draft and must not merge first.

Justin Chung and others added 30 commits July 7, 2026 16:14
The PowerShell1ES pool was migrated to the 1ES-maintained MMS2022 image
(full name MMSWindows2022-1ESPT-v2) by an unmerged-but-deployed branch in
PsImageFactory (PR #38413, deployed ~4 months ago). It is 1ES PT-compliant
out of the box, so the previous ExDShared fallback is no longer needed.

Co-authored-by: Copilot <[email protected]>
…n from branch

- Remove unreferenced PoolNames variable group + LinuxContainerImage /
  WindowsContainerImage (zero references in the 1ES tree).
- Replace hardcoded sdl.apiscan.versionNumber "7.6" with a value derived
  from Build.SourceBranchName at template-expansion time. Fixes silent TSA
  mislabelling on 7.9+ branches.
- Keep ob_outputDirectory name intact (build.psm1 reads $env:OB_OUTPUTDIRECTORY).
- Document each decision inline with NB comments.

Co-authored-by: Copilot <[email protected]>
Phase A of the OneBranch -> 1ES Pipeline Templates migration for the
Coordinated_Packages pipeline. Builds and stubs signing end-to-end so the
scaffold can be validated in ADO before ESRP signing onboarding completes.

Files:
  .pipelines/1ES/PowerShell-Coordinated_Packages-NonOfficial.yml
    Entry pipeline. Extends v1/1ES.Unofficial.PipelineTemplate.yml.
    CodeQL enabled unconditionally (compiled + enabledOnNonDefaultBranches
    + tsaEnabled) so every branch -- including rebuild/* shipping
    branches and migration test branches -- gets coverage. Branch-gated
    CodeQL would miss rebuild/* releases and block validating the SDL
    injection plumbing without cutting a real release.
    Global SDL paths use $(Build.SourcesDirectory)\.config\... (NO
    \PowerShell\ segment) because the auto-injected SDL Sources Analysis
    job runs without the stage-repo helper.

  .pipelines/1ES/templates/stages/PowerShell-Coordinated_Packages-Stages.yml
    5-stage layout: prep / macos / linux / windows / test_and_release_artifacts.

  .pipelines/1ES/templates/stage-repo-under-PowerShell.yml
    NEW helper. Copy-Items repo into a \PowerShell\ subfolder so existing
    scripts that hardcode $(Build.SourcesDirectory)\PowerShell\... keep
    working. Replaces OneBranch cloneToOfficialPath.yml (which used
    `git clone` -- not viable in 1ES PT where source IS the parent of
    dest). Hardened: ErrorAction=Stop, excludes .git, post-copy sanity
    check for build.psm1 / .config/tsaoptions.json / .config/suppress.json.

  .pipelines/1ES/templates/mac.yml
    macOS build (Azure Pipelines + macOS-latest + isCustom:true) plus
    Windows sign job (PowerShell1ES + MMS2022). Apple sign + verify
    stubbed. codeSignValidation disabled on sign job while signing is
    stubbed -- 1ES PT's separate codeSignValidation SDL check fails
    regardless of Update-PSSignedBuildFolder's tolerance for unsigned
    files in Unofficial mode. Belt-and-suspenders ##vso[artifact.upload]
    fallback added because templateContext.outputs on custom pools is
    untested in this codebase.

  .pipelines/1ES/templates/linux.yml
    Linux build (PowerShell1ES + PSMMSAzureLinux3.0-Secure) plus Windows
    sign job. Per-job CodeQL3000Init/Finalize tasks dropped -- 1ES PT
    injects them automatically from sdl.codeql config.

  .pipelines/1ES/templates/windows-hosted-build.yml
    Single Windows job (PowerShell1ES + MMS2022) that builds and signs
    in place. 3 signing stubs (obj 1P, nupkg, obp-file-signing dispatch).
    Per-job CodeQL3000 tasks dropped.

  .pipelines/1ES/templates/testartifacts.yml
    Win + nonwin test package staging. Win job passes $(PowerShellRoot)
    -- NOT $(RepoRoot) -- to insert-nuget-config-azfeed.yml so the
    private-feed nuget.config lands in the staged repo, not the un-staged
    root that $(RepoRoot) still points to after the stage-repo helper runs.

  .pipelines/1ES/templates/obp-file-signing.yml
    1P + 3P signing stubbed. Folder-prep and Update-PSSignedBuildFolder
    invocation preserved. Header lists the 4 ESRP KeyCodes needed for
    unstub (CP-230012, CP-231522, CP-401405, CP-401337-Apple).

Reuses these OneBranch templates unchanged:
  .pipelines/templates/variables/PowerShell-Coordinated_Packages-Variables.yml
  .pipelines/templates/SetVersionVariables.yml
  .pipelines/templates/set-reporoot.yml
  .pipelines/templates/insert-nuget-config-azfeed.yml
  .pipelines/templates/install-dotnet.yml
  .pipelines/templates/rebuild-branch-check.yml
  .pipelines/templates/step/finalize.yml

Phase B (real ESRP signing) starts once ESRP keycode onboarding lands.
Phase C forks this Unofficial scaffold to an Official sibling.

Co-authored-by: Copilot <[email protected]>
Build #684757 (the first ADO build of the new 1ES scaffold) was cancelled
at the prep stage with:

  Remote machine provider issue: Failed to request agent.
  Exception Image PSMMSAzureLinux3.0-Secure doesn't exist in pool PowerShell1ES

Root cause: PowerShell1ES (the team-owned 1ES managed pool, id 108, 35 agents)
only carries MMS2022 (Windows Server 2022). It does NOT have any Linux image
deployed. The original scaffold tentatively assumed PSMMSAzureLinux3.0-Secure
was present based on a partial read of the CloudTest.png inventory; build
#684757 disproved that assumption.

Fix:
* prep/SetVars (Stages.yml)       -> PowerShell1ES + MMS2022 + os: windows
  SetVars only runs pwsh to compute version variables; no Linux-specific
  tooling is required. Using the proven Windows pairing from the apiscan POC
  also avoids gating prep on a second pool authorization on first run.

* linux.yml build job             -> Azure-Pipelines-1ESPT-ExDShared
                                     + ubuntu-latest + os: linux
* testartifacts.yml nonwin job    -> same

Azure-Pipelines-1ESPT-ExDShared is the E+D org's shared 1ES PT-managed
Linux pool (24 D2ads_v5 agents, 8 GiB RAM, 75 GiB temp storage, Ubuntu 22.04
LTS with the full Microsoft-hosted dev tooling). It is the documented
standard fallback per the 1ES PT migration guide.

If/when a team-owned Linux image lands on PowerShell1ES (would need a
PsImageFactory PR similar to the unmerged addAzureCLI PR that put MMS2022
there), swap the pool: block in 2 places: templates/linux.yml build job and
templates/testartifacts.yml nonwin job.

Also updates the POOL/IMAGE STRATEGY comment in the entry pipeline and
drops the now-resolved "Confirm Linux pool/image mapping" TODO.

All 4 modified files validated with ConvertFrom-Yaml; no stale pairing
references remain in code (only in comments documenting the lesson).

Co-authored-by: Copilot <[email protected]>
After inspecting the PsImageFactory repo, PowerShell1ES.bicep already
declares PSMMSUbuntu22.04-Secure (and PSMMSUbuntu20.04-Secure) in its
images[] array. Build #684757 failed for PSMMSAzureLinux3.0-Secure
specifically because that image has its own bicep but was never plumbed
into the pool resource.

Swap Linux jobs back to the team-owned PowerShell1ES pool using Ubuntu
22.04 so we're not dependent on the external E+D shared pool. If the
next build fails the same way (Ubuntu 22 declared in bicep but
deploy.ps1 was never run to push it to Azure), fall back to
Azure-Pipelines-1ESPT-ExDShared + ubuntu-latest, or open a #38412-style
PsImageFactory PR to plumb in PSMMSAzureLinux3.0-Secure.

Affected:
- .pipelines/1ES/templates/linux.yml (build job)
- .pipelines/1ES/templates/testartifacts.yml (nonwin job)
- .pipelines/1ES/PowerShell-Coordinated_Packages-NonOfficial.yml
  (POOL/IMAGE STRATEGY comment block)

Co-authored-by: Copilot <[email protected]>
Cluster 1 (Linux pool): revert linux.yml + testartifacts.yml to
Azure-Pipelines-1ESPT-ExDShared + ubuntu-latest. PSMMSUbuntu22.04-Secure
is declared in PowerShell1ES.bicep and deployed, but the image lacks
the 1es-pt-prerequisites artifact required by 1ES PT
(https://aka.ms/1espt/image-prerequisites). E+D shared images ship the
artifact by default. Long-term: PsImageFactory PR to add the artifact
to PSMMS* images. Updated header comments to capture the lesson.

Cluster 2 (Switch-PSNugetConfig): add 1ES-specific
.pipelines/1ES/templates/insert-nuget-config-azfeed.yml that resets
`$global:LASTEXITCODE = 0` after the call. Switch-PSNugetConfig ->
New-NugetConfigFile runs `git update-index --skip-worktree` against
the regenerated nuget.config. The staged repo (Copy-Item without .git)
isn't a git workspace, so git fails benignly and leaves $LASTEXITCODE
non-zero. pwsh 7.4+ propagates that to the process exit code -> ADO
task fails despite Switch-PSNugetConfig actually completing. The
`skip-worktree` is purely defensive against accidental commits, which
is moot on a Copy-Item'd staging folder. Repointed 5 callsites
(linux.yml, mac.yml, windows-hosted-build.yml, testartifacts.yml win +
nonwin) to the 1ES wrapper. apiscan POC left on the shared template
because its staging path differs.

Cluster 3 (Linux nuget capture path): downstream of cluster 1; auto-
resolved by the pool revert.

Cluster 4 (mac.yml BuildArchitecture): line 119 referenced
$(BuildArchitecture) on the BUILD job, but only the SIGN job declared
the variable. Changed to template parameter substitution
(${{ parameters.buildArchitecture }}) at compile time. Cleaner than
mirroring the variable on the build job.

All 6 modified YAML files validated with ConvertFrom-Yaml.
Playbook updated with Gotchas PowerShell#4, PowerShell#5, PowerShell#6.

Co-authored-by: Copilot <[email protected]>
Per team policy: PowerShell is in the C+AI org and must NOT use pools
owned by other orgs (e.g., E+D's Azure-Pipelines-1ESPT-ExDShared). The
previous commit used that shared pool as a workaround for the missing
1es-pt-prerequisites artifact on PSMMSUbuntu22.04-Secure — that was a
governance violation regardless of the convenience.

Reverted linux.yml + testartifacts.yml + entry-pipeline comment block
back to PowerShell1ES + PSMMSUbuntu22.04-Secure + os: linux. Updated
all comments to:
  (a) State the governance rule explicitly (team-owned pools only).
  (b) Document the BLOCKER: PSMMSUbuntu22.04-Secure image build lacks
      the 1es-pt-prerequisites artifact required by 1ES PT
      (https://aka.ms/1espt/image-prerequisites). Build #685762 was
      rejected on this basis.
  (c) State the RESOLUTION path: PsImageFactory PR to bake the
      artifact into the PSMMSUbuntu22.04-Secure image build, then
      redeploy. Linux jobs of this pipeline cannot pass end-to-end
      until that work lands.

Other fixes from the previous commit (nuget LASTEXITCODE wrapper for
Windows + mac BuildArchitecture parameter substitution) are RETAINED.

Co-authored-by: Copilot <[email protected]>
PsImageFactory rebuilt PSMMSUbuntu22.04-Secure with the linux-1es-pt-prerequisites-v2 artifact on 2026-06-09, unblocking the Linux jobs that were failing build #685762 with Using an image without 1es-pt-prerequisites artifact is not allowed.

Co-authored-by: Copilot <[email protected]>
… NonOfficial)

Bug PowerShell#1 (Linux Stage-repo): YAML boolean `cleanFirst` parameter rendered as
literal `True` in the pwsh inline script, causing `if (True -and ...)`
which fails on Linux. Switched to quoted string comparison
`if ('${{ parameters.cleanFirst }}' -ieq 'True')` so the value is
unambiguously a string regardless of platform pwsh parser.

Bug PowerShell#2 (Windows wix nuget restore): the staged repo at
`$(Build.SourcesDirectory)\PowerShell` was created via `Copy-Item`
without `.git`. Downstream `build.psm1` calls `git clean -fdX` which
treats every file as untracked-and-ignored and wipes `tools/wix/nuget.config`
(matches `.gitignore:110` pattern `nuget.config`). That file declares the
`dotnet-eng` feed that hosts `Microsoft.Signed.Wix`, so dotnet restore
fails with NU1101. Switched the staging helper to `git worktree add --detach
$dst HEAD` so the staged path is a real git workspace; tracked files are
preserved by `git clean -fdX` as designed. Added the lifecycle dance
(`worktree remove --force` -> conditional `Remove-Item` -> unconditional
`worktree prune` -> `worktree add`) so prior aborted runs do not leave
orphaned worktree registrations blocking the next run. Also added
`tools/wix/nuget.config` to the required-files post-check as a canary for
this whole class of bug.

Bug PowerShell#3 (macOS Capture VSS* flake): hosted `macOS-latest` pwsh occasionally
throws `Call to 'procargs' failed with errno 5` during startup - a libproc
race independent of our scripts. Added `retryCountOnTaskFailure: 2` AND
`continueOnError: true` to the diagnostic Capture VSS* task: retry to
maximise odds of getting useful output, continueOnError as a safety net so
a hosted flake never fails an otherwise-green build on a non-load-bearing
diagnostic step.

Also removed the obsolete `$global:LASTEXITCODE = 0` workaround from
`insert-nuget-config-azfeed.yml` - it was masking the Bug PowerShell#2 root cause.
With the staged path now being a real git workspace, `git update-index
--skip-worktree` succeeds normally and no reset is needed.

Co-authored-by: Copilot <[email protected]>
… NonOfficial)

Build #686027 ran end-to-end for the first time after the Linux image and
git-worktree fixes from b58362c8. It surfaced two new failure clusters,
both unrelated to the prior fixes:

Cluster A/B/D: ~100K StyleCop/CodeAnalysis errors across Linux build,
Windows BUILD, Windows Symbols, and Linux TestArtifacts. All four failures
were the same root cause: Roslyn was loading TWO .globalconfig files
(parent at $(Build.SourcesDirectory)/.globalconfig from 1ES PT checkout: self,
plus child at $(Build.SourcesDirectory)/PowerShell/.globalconfig from our
git-worktree staging step). Per Roslyn semantics, dual global-analyzer
configs with conflicting keys UNSET those keys and emit a
MultipleGlobalAnalyzerKeys warning. With every SA*/CA* suppression unset,
all rules fired and TreatWarningsAsErrors escalated them.

Fix: in stage-repo-under-PowerShell.yml, after git worktree add succeeds,
rename the parent .globalconfig to .globalconfig.disabled-by-stage-repo so
Roslyn no longer auto-discovers it. Add a defensive walk-up check from a
sample project to assert exactly one .globalconfig remains visible and that
it is the worktree copy. Add .globalconfig to the required-files canary list.

This bug does NOT manifest under OneBranch because OneBranch's checkout
layout puts source and staged repo as siblings, not parent/child. It is
specific to the 1ES PT "checkout: self at $(Build.SourcesDirectory) + stage
into a subdirectory" pattern. Other auto-discovered files were audited:
.editorconfig has root=true (walk-up stops), no Directory.Build/Packages.*
exist in the repo, nuget.config uses <clear/> at the worktree root.

Cluster C: macOS BUILD published `macosBinResults-${arch}` artifact twice
(once via mid-job `##vso[artifact.upload]`, once via end-of-job
PublishPipelineArtifact@1 auto-injected from templateContext.outputs).
The second registration failed with `Artifact macosBinResults-x64 already
exists for build 686027`, marking the whole job as failed. The mid-job
upload was added as a "belt-and-suspenders fallback" under the (incorrect)
assumption that ADO tolerates duplicate artifact names; it does not.

Fix: remove the mid-job `##vso[artifact.upload]` from mac.yml. Keep
templateContext.outputs.pipelineArtifact as the single publish mechanism.

Playbook updated with Gotchas PowerShell#11 (dual .globalconfig conflict) and
PowerShell#12 (duplicate artifact registration).

Co-authored-by: Copilot <[email protected]>
Build #686043 demonstrated both prior fixes (parent .globalconfig rename and
mac.yml artifact de-duplication) worked: macOS and Windows jobs all passed,
SDL succeeded, and the SA*/CA* analyzer firehose was gone. But all 9 Linux
build jobs (and Linux test artifacts) failed in the new defensive walk-up
assertion at stage-repo-under-PowerShell.yml:183 with:

  Get-Item: Could not find item /mnt/vss/_work/1/s/PowerShell/.globalconfig

even though the previous line's Test-Path returned True for the same path.

Root cause: on Linux, .NET marks any file with a leading "." as Hidden in
FileSystemInfo.Attributes (POSIX convention). PowerShell's FileSystemProvider
filters those out of Get-Item/Get-ChildItem by default; -Force disables the
filter. Test-Path doesn't consult Attributes, so it always saw the dotfile.
macOS .NET does NOT flag dotfiles as Hidden, which is why macOS passed the
same code path.

Fix:
- Get-Item (Join-Path $dst '.globalconfig') ->
  Get-Item -LiteralPath (Join-Path $dst '.globalconfig') -Force
  (Adopts rubber-duck recommendation: -LiteralPath also avoids wildcard
  ambiguity for paths containing [ ] *.)
- Cosmetic: displayName changed from "$(Build.SourcesDirectory)\PowerShell"
  to "$(Build.SourcesDirectory)/PowerShell" so the Linux log no longer
  reads "Stage repo into /mnt/vss/_work/1/s\PowerShell" (mixed separators).

Playbook updated with Gotcha PowerShell#13 (Linux dotfile Get-Item behaviour) and a
new decision-tree row for "Get-Item: Could not find item /...<dotfile>".

Co-authored-by: Copilot <[email protected]>
Build #686056 ran 24 of 25 jobs successfully — the dotfile Get-Item fix
from cdb732234 worked perfectly. The only failure was the auto-injected
"Guardian: CredScan (Binary)" SDL task in the Linux "Build non-windows
test artifacts" job, which failed with:

  [ERROR] [Invalid Argument] SuppressionsPath: Invalid file path list.
  File not found - /mnt/vss/_work/1/s\.config\suppress.csk
  GuardianErrorExitCodeException: credscan completed with exit code 24

Root cause: the entry pipeline's PIPELINE-LEVEL sdl block uses Windows
backslash paths ("$(Build.SourcesDirectory)\.config\suppress.json"). On
Windows .NET, backslash AND forward slash both work as path separators,
so the auto-injected SDL Sources Analysis (self) job — running on a
Windows-default agent — passed. On Linux .NET, backslash is a LITERAL
character, not a separator. The path resolves to
"/mnt/vss/_work/1/s\.config\suppress.json" which the Guardian CLI's
file-existence check rejects.

The Linux BUILD jobs passed because linux.yml:64-66 already overrides
sdl.tsa.configFile + sdl.credscan.suppressionsFile with forward-slash
paths under PowerShellRoot. The Linux SIGN jobs and macOS SIGN jobs
also had backslash overrides BUT passed by luck — 1ES PT does not
inject Binary Analysis CredScan into sign-only jobs (they get
CodeSignValidation only). The non-windows testartifacts job is the one
job that (a) runs on os: linux AND (b) gets BinaryAnalysis CredScan
injected AND (c) did not override the inherited backslash paths.

Fix:
- testartifacts.yml non-win job: add per-job sdl.tsa.configFile +
  sdl.credscan.suppressionsFile overrides with forward-slash paths under
  $(Build.SourcesDirectory)/PowerShell/.config/ (REQUIRED fix —
  unblocks the failing job).
- linux.yml sign job (lines 185, 187): convert backslashes to forward
  slashes (DEFENSIVE — passing today only because CredScan isn't
  injected into sign jobs, would break if 1ES PT injection rules change).
- mac.yml sign job (lines 153, 155): same defensive cleanup.

NOT changed:
- Pipeline-level (entry pipeline) sdl paths — still backslash, working
  fine for SDL Sources Analysis (self) which runs on Windows. Switching
  them to forward slashes is also safe (Windows accepts both), but
  out of scope; not changing what's working.
- Windows-only job sdl paths (windows-hosted-build.yml,
  testartifacts.yml win job) — backslash is fine on Windows.

Validated all 3 files via ConvertFrom-Yaml.

Playbook (<session>/files/onebranch-to-1es-migration.md) — added
Gotcha PowerShell#14 (SDL paths on Linux/macOS) with watch-list of all
templateContext.sdl.* overrides and a decision-tree row.

Co-authored-by: Copilot <[email protected]>
…eal-task gate)

Wire MS-Sign Test KeyCodes (CP-466277 for 1P, CP-466279 for 3P) into the 1ES
Unofficial pipeline so the EsrpCodeSigning@5 task path can be exercised before
full AME-onboarded PROD signing is available.

Design: sibling-condition gate using the runtime variable EsrpTestConnectionName.
Each modified signing site has two sibling steps:
  - Existing Write-Host stub, condition: eq(variables['EsrpTestConnectionName'], '')
  - New EsrpCodeSigning@5 task, condition: ne(variables['EsrpTestConnectionName'], '')

In ADO expression syntax, variables['UndefinedVar'] evaluates to empty string,
so the default behavior (no variable group attached) preserves the green
baseline -- only stubs run. Attaching a variable group named
EsrpSigningTest-PowerShell with 6 keys (EsrpTestConnectionName,
EsrpTestAppRegClientId, EsrpTestAppRegTenantId, EsrpTestAuthAkvName,
EsrpTestAuthCertName, EsrpTestAuthSignCertName) flips every modified site to
real signing.

Sites modified (3):
  - .pipelines/1ES/templates/obp-file-signing.yml site 1: 1P Authenticode, CP-466277
  - .pipelines/1ES/templates/obp-file-signing.yml site 2: 3P Authenticode, CP-466279
  - .pipelines/1ES/templates/windows-hosted-build.yml obj files: 1P Authenticode, CP-466277

Sites that STAY STUBBED (2):
  - windows-hosted-build.yml nupkg files: no NuGet-compatible test KeyCode
    available (NuGetSign operation cannot reuse Authenticode codes)
  - mac.yml Apple Mach-O CodeSign: no Apple test KeyCode available
    (MacAppDeveloperSign operation, entirely different cert chain)

Header comment updates:
  - PowerShell-Coordinated_Packages-NonOfficial.yml entry pipeline: documents
    the 6 variable-group keys, KeyCode mapping table, PROD swap path, and the
    predicate caveat (test-signed files round-trip through both 1P and 3P paths
    in obp-file-signing.yml -- by design, do not fix)
  - obp-file-signing.yml: hybrid stub/test-signing mode documentation
  - windows-hosted-build.yml: per-site mode documentation

All 3 modified YAML files validated via ConvertFrom-Yaml.
Safe to push to the green rebuild/v7.9.99-rebuild.9 branch: default condition
runs stubs (current green baseline preserved); test signing is opt-in by
attaching the variable group at queue time.

Co-authored-by: Copilot <[email protected]>
The 5 production EsrpCodeSigning@5 blocks referenced AuthCertName:
$(EsrpProdAuthCertName) - a separate authentication cert that was never
provisioned. Our ESRP setup uses the ESRP-Signing-Identity managed identity
over the PowerShell-ESRP-Release WIF connection, with only the request-signing
cert (esrp-auth-sign) in pscoresignprod-kv.

Replace AuthCertName with UseMSIAuthentication: true across all Official
signing sites (1P CP-230012, 3P CP-231522, NuGet CP-401405, Apple
CP-401337-Apple) so the MI's AAD token proves identity to ESRP. Drop
EsrpProdAuthCertName from the Official entry-file variable-group doc.

Test/stub blocks (EsrpTest*) unchanged.

Co-authored-by: Copilot <[email protected]>
Use newline-delimited minimatch patterns so ESRP signs all selected files, and temporarily disable the new Roslyn ruleset that promotes existing style findings to build errors.

Co-authored-by: Copilot <[email protected]>

Copilot-Session: a5624bc7-4810-4c5f-9e68-c0148555a63a
Skip generated CodeSignSummary markdown files when restoring signed binaries and correctly classify files that match either accepted Microsoft issuer family.

Co-authored-by: Copilot <[email protected]>

Copilot-Session: a5624bc7-4810-4c5f-9e68-c0148555a63a
Remove the two-digit cap from release tag validation across build, packaging, and release tooling so iterative preview branches such as preview.100 are supported.

Co-authored-by: Copilot <[email protected]>

Copilot-Session: a5624bc7-4810-4c5f-9e68-c0148555a63a
Co-authored-by: Copilot <[email protected]>

Copilot-Session: d718a0aa-c021-4a5d-b655-da1e9fbc37dd
Co-authored-by: Copilot <[email protected]>

Copilot-Session: d718a0aa-c021-4a5d-b655-da1e9fbc37dd
Co-authored-by: Copilot <[email protected]>

Copilot-Session: d718a0aa-c021-4a5d-b655-da1e9fbc37dd
Co-authored-by: Copilot <[email protected]>

Copilot-Session: d718a0aa-c021-4a5d-b655-da1e9fbc37dd
Co-authored-by: Copilot <[email protected]>

Copilot-Session: d718a0aa-c021-4a5d-b655-da1e9fbc37dd
Co-authored-by: Copilot <[email protected]>

Copilot-Session: d718a0aa-c021-4a5d-b655-da1e9fbc37dd
Co-authored-by: Copilot <[email protected]>

Copilot-Session: d718a0aa-c021-4a5d-b655-da1e9fbc37dd
Move the reusable 1ES stage/job/step template graph out of this repository and
consume it from PowerShellCore/PowerShell-Engineering-System, so the product
repository keeps only thin entry points plus the product-specific configuration
and assets that must travel with a product source revision.

Removed (now hosted in the engineering system):
  .pipelines/1ES/templates/**  (21 files)

Kept here (unchanged):
  .pipelines/1ES/*.yml                    thin Official/NonOfficial entry points
  .pipelines/templates/**                 OneBranch-owned helpers, still shared
  .pipelines/NonOfficial/**, .pipelines/*.yml   legacy OneBranch, untouched
  build.psm1, tools/**, .config/**        product scripts and assets

Entry points repointed (6 references across 5 files):
  /.pipelines/1ES/templates/stages/PowerShell-Coordinated_Packages-Stages.yml@self
    -> /templates/1ES/stages/PowerShell-Coordinated_Packages-Stages.yml@PowerShellEngineeringSystem
  /.pipelines/1ES/templates/stages/PowerShell-Packages-Stages.yml@self
    -> /templates/1ES/stages/PowerShell-Packages-Stages.yml@PowerShellEngineeringSystem
  /.pipelines/1ES/templates/compliance/apiscan.yml@self
    -> /templates/1ES/compliance/apiscan.yml@PowerShellEngineeringSystem
  /.pipelines/1ES/templates/compliance/generateNotice.yml@self
    -> /templates/1ES/compliance/generateNotice.yml@PowerShellEngineeringSystem

Each entry point gains a PowerShellEngineeringSystem repository resource pinned
to an immutable full commit SHA rather than a moving branch ref, so a later
template change cannot retroactively alter a shipped build. The alias is load
bearing: engineering-system templates reference siblings as
@PowerShellEngineeringSystem, and Azure DevOps resolves repository aliases from
the consuming pipeline's resources block.

Behaviour is intended to be identical. The resource is used for compile-time
template resolution only and is never checked out, so jobs still run
`checkout: self` with the product repository at $(Build.SourcesDirectory) and
every runtime script path, staged worktree path, service connection, signing
site and artifact name is unchanged. Templates that call back into helpers
co-owned with the still-live OneBranch pipelines keep using @self.

No legacy OneBranch pipeline file is modified.

Co-authored-by: Copilot App <[email protected]>
Copilot-Session: 8a10ef4e-c1c5-49aa-910d-e19959c89392
Justin Chung and others added 13 commits September 8, 2026 12:34
Advance the `PowerShellEngineeringSystem` repository resource in all five 1ES
entry points from 8641371 to 284827d.

284827d removes the ESRP test-signing branches from the NonOfficial expansion
of obp-file-signing.yml and windows-hosted-build.yml. Those branches referenced
$(EsrpTestConnectionName) from the EsrpSigningTest-PowerShell variable group,
which is not provisioned, and failed 1ES compile-time validation in NonOfficial
run 716713. NonOfficial now emits unconditional Write-Host stubs and unsigned
artifacts; production signing is unchanged and remains compile-time guarded to
OfficialBuild: true.

Still pinned to a branch-head commit on the in-review engineering system PR
(mscodehub PR 41545). This MUST be repinned to a durable post-merge SHA or tag
before this PR completes.

Co-authored-by: Copilot App <[email protected]>
Copilot-Session: 8a10ef4e-c1c5-49aa-910d-e19959c89392
Advance the `PowerShellEngineeringSystem` repository resource in all five 1ES
entry points from 284827d to 8089380.

8089380 is a comment/displayName-only change in mac.yml: the Apple CodeSign
NonOfficial stub is renamed from "ESRP onboarding pending" to "NonOfficial
builds are unsigned" to match the other stubs, and the Developer ID
verification comment now records that the step is stubbed in both Official and
NonOfficial. No task, service connection or KeyCode changes.

Still pinned to a branch-head commit on the in-review engineering system PR
(mscodehub PR 41545). This MUST be repinned to a durable post-merge SHA or tag
before this PR completes.

Co-authored-by: Copilot App <[email protected]>
Copilot-Session: 8a10ef4e-c1c5-49aa-910d-e19959c89392
Create a distinct package entry point backed by the NonOfficial coordinated build and centralized Engineering System templates.

Co-authored-by: Copilot App <[email protected]>

Copilot-Session: 8a10ef4e-c1c5-49aa-910d-e19959c89392
Rename .pipelines/1ES/PowerShell-WinGet-Release-Staging-NonOfficial.yml
to .pipelines/1ES/PowerShell-Release-NonOfficial.yml so the top-level
1ES entry point follows the same broad NonOfficial release naming
already established by the legacy OneBranch
.pipelines/NonOfficial/PowerShell-Release-NonOfficial.yml pipeline.

Update the pipeline run name from the WinGet-specific
winget-stage-$(Build.BuildId) to the generic
release-$(BUILD.SOURCEBRANCHNAME)-1ES-nonofficial-$(Build.BuildId),
matching the pattern used by the other 1ES entry points in this
directory (pkgs-/bins-$(BUILD.SOURCEBRANCHNAME)-1ES-<official/
nonofficial>-$(Build.BuildId)).

The referenced stage template
(/templates/1ES/stages/PowerShell-WinGet-Release-Staging-Stages.yml@PowerShellEngineeringSystem)
and its job template
(/templates/1ES/winget-release-staging.yml@PowerShellEngineeringSystem)
are unchanged; this pipeline still only stages WinGet release inputs
and contains no ESRP Release task, so it cannot publish.
Replace the queue-time expectedProducerRunId/SourceBranch/SourceCommit and
expectedBundleName parameters with the declared resources.pipelines entry.
The package run is now chosen through the Azure DevOps resource picker and
every downstream template reads the standard
$(resources.pipeline.PSPackagesOfficial.*) variables, so there is nothing
left that a queuer can spoof.

The resource alias is PSPackagesOfficial, identical to the alias used by
the OneBranch release pipelines, so the ported validation templates keep
their `download: PSPackagesOfficial` and
$(Pipeline.Workspace)/PSPackagesOfficial/... references. Its source is the
NonOfficial 1ES package pipeline PowerShell-Packages-NonOfficial-1ES.

The entry point now extends the renamed, release-generic Engineering stage
aggregator, which adds the OneBranch validateSdk, gbltool and fxdpackages
gates ahead of the WinGet input staging stage. Engineering System is
repinned to 6480fd5fca46861f33e9b07328a441930e259143.

Still NonOfficial and non-releasing: no EsrpRelease, no EsrpCodeSigning@5,
no PowerShell-ESRP-Release, no WinGet publish, trigger: none on both the
pipeline and the resource.
…2616cf4771

Picks up the explicit arm64 hostArchitecture on the linuxArm64fxd
validation job.
Pin every 1ES entrypoint to the immutable Engineering System test-signing commit and attach the existing WIF request-auth metadata only to NonOfficial signing consumers. Document the fixed CP-466277/CP-466279 boundary and retain unsigned stubs for unsupported signing technologies.

Co-authored-by: Copilot App <[email protected]>
Advance the active coordinated, package, Official-preview, API, and release-staging entries to the hardened immutable Engineering System commit. Leave the superseded definition 4169 entry on its prior unsigned template pin so only requested definitions 4229 and 4273 consume the signing resources.

Co-authored-by: Copilot App <[email protected]>
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@jshigetomi Justin Chung (jshigetomi) added the CL-BuildPackaging Indicates that a PR should be marked as a build or packaging change in the Change Log label Sep 10, 2026
@jshigetomi Justin Chung (jshigetomi) changed the title Add lightweight 1ES pipeline entry points Add trust-separated lightweight 1ES pipeline entry points Sep 10, 2026
@jshigetomi Justin Chung (jshigetomi) changed the title Add trust-separated lightweight 1ES pipeline entry points Add trust-separated 1ES package and release-staging entry points Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backport-7.4.x-Consider Backport-7.5.x-Consider Backport-7.6.x-Consider CL-BuildPackaging Indicates that a PR should be marked as a build or packaging change in the Change Log

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant