From 4687cb48c5bd3162861098e5295e73150b521521 Mon Sep 17 00:00:00 2001 From: Joywambui-maina Date: Thu, 27 Aug 2026 11:47:46 -0700 Subject: [PATCH 1/2] build(wrapper): add the internal-feed release pipeline and install guide Builds and packs the wrapper modules from committed sources, verifies packages were actually produced before signing, ESRP-signs, and publishes to the project-scoped MSGraph_PowerShell_V3_Build feed. Mirrors sdk-release.yml - same 1ES template, security templates and publish task - with no AutoRest or Node bootstrap, manual trigger only, and Publish defaulting to false so the first runs produce a drop artifact without pushing packages. The guide covers feed registration and Install-Module -AllowPrerelease for testers. --- .azure-pipelines/wrapper-release.yml | 149 ++++++++++++++++++++++ docs/install-wrapper-modules-from-feed.md | 84 ++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 .azure-pipelines/wrapper-release.yml create mode 100644 docs/install-wrapper-modules-from-feed.md diff --git a/.azure-pipelines/wrapper-release.yml b/.azure-pipelines/wrapper-release.yml new file mode 100644 index 00000000000..ce41ce4d01b --- /dev/null +++ b/.azure-pipelines/wrapper-release.yml @@ -0,0 +1,149 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +name: $(BuildDefinitionName)_$(SourceBranchName)_$(Date:yyyyMMdd)$(Rev:.r) + +parameters: +- name: BuildAgent + default: 1es-windows-ps-compute-m + displayName: Build Agent +- name: PackageVersion + type: string + default: 3.0.0 + displayName: Package version +- name: Sign + type: boolean + default: true +- name: Publish + type: boolean + default: false +- name: InternalFeed + type: string + # The feed is PROJECT-scoped (dev.azure.com/microsoftgraph/Graph Developer Experiences/ + # _artifacts/feed/MSGraph_PowerShell_V3_Build), so publishVstsFeed needs the project + # qualifier - a bare feed name only resolves for organization-scoped feeds. + default: Graph Developer Experiences/MSGraph_PowerShell_V3_Build + displayName: Internal NuGet feed + +variables: + BuildAgent: ${{ parameters.BuildAgent }} + WrapperConfiguration: Release + WrapperPrerelease: alpha$(Build.BuildId) + +trigger: none +pr: none + +resources: + repositories: + - repository: 1ESPipelineTemplates + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release + +extends: + template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates + parameters: + pool: $(BuildAgent) + settings: + networkIsolationPolicy: Permissive + sdl: + binskim: + enabled: false + justificationForDisabling: "Matches sdk-release.yml; BinSkim currently blocks internal-feed publishing." + credscan: + suppressionsFile: $(Build.SourcesDirectory)/.azure-pipelines/config/credscan/credscan-suppressions.json + policheck: + exclusionFile: $(Build.SourcesDirectory)/.azure-pipelines/config/policheck/policheck-exclusions.xml + customBuildTags: + - ES365AIMigrationTooling + stages: + - stage: Build + displayName: Build wrapper modules + jobs: + - job: Wrapper_Build + displayName: Generate, build, pack, and sign wrapper modules + timeoutInMinutes: 840 + templateContext: + outputs: + - output: pipelineArtifact + displayName: Publish wrapper module artifacts + targetPath: $(Build.ArtifactStagingDirectory) + artifactName: drop + publishLocation: Container + steps: + - script: git submodule update --init --recursive + displayName: Initialize submodules + + - template: .azure-pipelines/common-templates/install-tools.yml@self + - template: .azure-pipelines/common-templates/security-pre-checks.yml@self + + # Version and prerelease go to the script directly (-ModuleVersion/-Prerelease); the + # script owns package identity. Nothing here edits ModuleMetadata.json - that file + # belongs to the v2 release train. + - task: PowerShell@2 + displayName: Build and pack wrapper modules + inputs: + targetType: inline + pwsh: true + script: | + $params = @{ + ApiVersion = 'v1.0' + Configuration = '$(WrapperConfiguration)' + ArtifactsLocation = '$(Build.ArtifactStagingDirectory)' + ModuleVersion = '${{ parameters.PackageVersion }}' + Prerelease = '$(WrapperPrerelease)' + SkipKiota = $true + Pack = $true + } + & '$(Build.SourcesDirectory)/tools/Build-WrapperModule.ps1' @params + if ($LASTEXITCODE -ne 0) { throw "Wrapper build failed with exit code $LASTEXITCODE." } + + - task: PowerShell@2 + displayName: Verify wrapper packages were produced + inputs: + targetType: inline + pwsh: true + script: | + $packages = @(Get-ChildItem '$(Build.ArtifactStagingDirectory)' -Recurse -Filter 'Microsoft.Graph.Wrapper.*.nupkg') + Write-Host "Wrapper packages produced: $($packages.Count)" + $packages | ForEach-Object { Write-Host " $($_.FullName)" } + if ($packages.Count -eq 0) { throw 'No wrapper packages were produced.' } + + - template: .azure-pipelines/common-templates/guardian-analyzer.yml@self + + - ${{ if eq(parameters.Sign, true) }}: + - template: .azure-pipelines/common-templates/esrp/codesign-nuget.yml@self + parameters: + FolderPath: $(Build.ArtifactStagingDirectory) + Pattern: Microsoft.Graph.Wrapper.*.nupkg + + - template: .azure-pipelines/common-templates/security-post-checks.yml@self + + - ${{ if eq(parameters.Publish, true) }}: + - stage: Deploy_to_Internal_Feed + displayName: Deploy wrapper packages to internal feed + dependsOn: Build + jobs: + - deployment: DeployToInternalFeed + displayName: Publish to MSGraph PowerShell V3 build feed + environment: PowerShellInternalFeed + templateContext: + type: releaseJob + isProduction: true + inputs: + - input: pipelineArtifact + artifactName: drop + targetPath: $(System.DefaultWorkingDirectory)/drop + strategy: + runOnce: + deploy: + steps: + - task: 1ES.PublishNuget@1 + displayName: Publish wrapper packages to internal feed + inputs: + useDotNetTask: false + packageParentPath: $(System.DefaultWorkingDirectory) + packagesToPush: $(System.DefaultWorkingDirectory)/**/drop/**/Microsoft.Graph.Wrapper.*.nupkg + publishVstsFeed: ${{ parameters.InternalFeed }} + nuGetFeedType: internal + allowPackageConflicts: false \ No newline at end of file diff --git a/docs/install-wrapper-modules-from-feed.md b/docs/install-wrapper-modules-from-feed.md new file mode 100644 index 00000000000..fe69d4d3bf0 --- /dev/null +++ b/docs/install-wrapper-modules-from-feed.md @@ -0,0 +1,84 @@ +# Install wrapper modules from the internal feed + +The wrapper release pipeline publishes `Microsoft.Graph.Wrapper.*` prerelease packages to the +`MSGraph_PowerShell_V3_Build` Azure Artifacts feed for team validation. + +## Prerequisites + +- Access to the Azure DevOps project and read permission on the feed. +- PowerShellGet 2.x. +- `Microsoft.Graph.Authentication` installed at the version used by the build. + +Get the feed's NuGet v2 URL from **Azure Artifacts > Connect to feed > NuGet > v2**. PowerShellGet +2.x requires the v2 endpoint when registering a `PSRepository`. + +## Register the feed + +Use an Azure DevOps personal access token with Packaging Read permission. Enter the token only at +the secure prompt; do not put it in a script, command history, or source-controlled file. + +```powershell +$secureToken = Read-Host 'Azure DevOps PAT (Packaging: Read)' -AsSecureString +$credential = [pscredential]::new('AzureDevOps', $secureToken) +$feedUrl = 'https://pkgs.dev.azure.com///_packaging/MSGraph_PowerShell_V3_Build/nuget/v2' + +Register-PSRepository ` + -Name MSGraphPowerShellV3Build ` + -SourceLocation $feedUrl ` + -PublishLocation $feedUrl ` + -InstallationPolicy Trusted ` + -Credential $credential +``` + +Replace `/` with the values from **Connect to feed**. Do not infer them from +the feed's display name. + +## Install and validate a package + +Every pipeline run publishes a version such as `3.0.0-alpha12345`. `-AllowPrerelease` is therefore +required. + +```powershell +Install-Module Microsoft.Graph.Authentication -Scope CurrentUser -Force + +Install-Module ` + -Name Microsoft.Graph.Wrapper.Users ` + -Repository MSGraphPowerShellV3Build ` + -Credential $credential ` + -AllowPrerelease ` + -Scope CurrentUser ` + -Force + +Import-Module Microsoft.Graph.Wrapper.Users -Force + +$module = Get-Module Microsoft.Graph.Wrapper.Users +$commands = @(Get-Command -Module $module.Name) +if (-not $module -or $commands.Count -eq 0) { + throw 'The wrapper module did not import or export any commands.' +} + +$module | Select-Object Name, Version, Path +Write-Host "Exported commands: $($commands.Count)" +``` + +For an operation test, start a clean PowerShell process, import only the wrapper module under test, +connect with the least privileges needed, and invoke a read-only command. Loading the shipping and +wrapper workload modules together can create command-name conflicts, even though their module and +package IDs differ. + +## Find or update packages + +```powershell +Find-Module Microsoft.Graph.Wrapper.* ` + -Repository MSGraphPowerShellV3Build ` + -Credential $credential ` + -AllowPrerelease + +Update-Module Microsoft.Graph.Wrapper.Users -AllowPrerelease -Force +``` + +Remove the repository registration when testing is complete: + +```powershell +Unregister-PSRepository -Name MSGraphPowerShellV3Build +``` \ No newline at end of file From c6010c7f3b1fc891f56483e30aad4b0fe987df2a Mon Sep 17 00:00:00 2001 From: Joywambui-maina Date: Fri, 28 Aug 2026 10:35:53 -0700 Subject: [PATCH 2/2] docs(wrapper): keep the install guide off main The guide rides #3753 to powershell-v3, where the wrapper modules live; main only carries the pipeline definition. --- docs/install-wrapper-modules-from-feed.md | 84 ----------------------- 1 file changed, 84 deletions(-) delete mode 100644 docs/install-wrapper-modules-from-feed.md diff --git a/docs/install-wrapper-modules-from-feed.md b/docs/install-wrapper-modules-from-feed.md deleted file mode 100644 index fe69d4d3bf0..00000000000 --- a/docs/install-wrapper-modules-from-feed.md +++ /dev/null @@ -1,84 +0,0 @@ -# Install wrapper modules from the internal feed - -The wrapper release pipeline publishes `Microsoft.Graph.Wrapper.*` prerelease packages to the -`MSGraph_PowerShell_V3_Build` Azure Artifacts feed for team validation. - -## Prerequisites - -- Access to the Azure DevOps project and read permission on the feed. -- PowerShellGet 2.x. -- `Microsoft.Graph.Authentication` installed at the version used by the build. - -Get the feed's NuGet v2 URL from **Azure Artifacts > Connect to feed > NuGet > v2**. PowerShellGet -2.x requires the v2 endpoint when registering a `PSRepository`. - -## Register the feed - -Use an Azure DevOps personal access token with Packaging Read permission. Enter the token only at -the secure prompt; do not put it in a script, command history, or source-controlled file. - -```powershell -$secureToken = Read-Host 'Azure DevOps PAT (Packaging: Read)' -AsSecureString -$credential = [pscredential]::new('AzureDevOps', $secureToken) -$feedUrl = 'https://pkgs.dev.azure.com///_packaging/MSGraph_PowerShell_V3_Build/nuget/v2' - -Register-PSRepository ` - -Name MSGraphPowerShellV3Build ` - -SourceLocation $feedUrl ` - -PublishLocation $feedUrl ` - -InstallationPolicy Trusted ` - -Credential $credential -``` - -Replace `/` with the values from **Connect to feed**. Do not infer them from -the feed's display name. - -## Install and validate a package - -Every pipeline run publishes a version such as `3.0.0-alpha12345`. `-AllowPrerelease` is therefore -required. - -```powershell -Install-Module Microsoft.Graph.Authentication -Scope CurrentUser -Force - -Install-Module ` - -Name Microsoft.Graph.Wrapper.Users ` - -Repository MSGraphPowerShellV3Build ` - -Credential $credential ` - -AllowPrerelease ` - -Scope CurrentUser ` - -Force - -Import-Module Microsoft.Graph.Wrapper.Users -Force - -$module = Get-Module Microsoft.Graph.Wrapper.Users -$commands = @(Get-Command -Module $module.Name) -if (-not $module -or $commands.Count -eq 0) { - throw 'The wrapper module did not import or export any commands.' -} - -$module | Select-Object Name, Version, Path -Write-Host "Exported commands: $($commands.Count)" -``` - -For an operation test, start a clean PowerShell process, import only the wrapper module under test, -connect with the least privileges needed, and invoke a read-only command. Loading the shipping and -wrapper workload modules together can create command-name conflicts, even though their module and -package IDs differ. - -## Find or update packages - -```powershell -Find-Module Microsoft.Graph.Wrapper.* ` - -Repository MSGraphPowerShellV3Build ` - -Credential $credential ` - -AllowPrerelease - -Update-Module Microsoft.Graph.Wrapper.Users -AllowPrerelease -Force -``` - -Remove the repository registration when testing is complete: - -```powershell -Unregister-PSRepository -Name MSGraphPowerShellV3Build -``` \ No newline at end of file