From 582818c5dcbde083d0f95ffb6d6a878cd33b038f Mon Sep 17 00:00:00 2001 From: Joywambui-maina Date: Fri, 28 Aug 2026 13:14:50 -0700 Subject: [PATCH 1/5] build(wrapper): install only what the wrapper pipeline uses The pipeline reused install-tools.yml wholesale, which is the AutoRest toolchain - Node, the private npm feed, AutoRest, Rush and a full rush rebuild - none of which the wrapper build touches, and it installs .NET 8/6 while the generator targets net10.0. The build step then failed because Build-WrapperModule.ps1 refuses to run without the kiota CLI on PATH even under -SkipKiota. Installs the .NET 10 SDK, feed auth, and kiota explicitly. --- .azure-pipelines/wrapper-release.yml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/.azure-pipelines/wrapper-release.yml b/.azure-pipelines/wrapper-release.yml index ce41ce4d01..69d6b42075 100644 --- a/.azure-pipelines/wrapper-release.yml +++ b/.azure-pipelines/wrapper-release.yml @@ -74,7 +74,26 @@ extends: - script: git submodule update --init --recursive displayName: Initialize submodules - - template: .azure-pipelines/common-templates/install-tools.yml@self + # Deliberately NOT install-tools.yml: that template is the AutoRest toolchain - Node, + # the private npm feed, AutoRest, Rush and a full rush rebuild - none of which this + # pipeline uses. The wrapper modules compile from committed sources, so the whole + # toolchain is the .NET SDK, feed auth for restore, and the kiota CLI. + - task: UseDotNet@2 + displayName: Use .NET SDK 10 + retryCountOnTaskFailure: 2 + inputs: + version: 10.x + - task: NuGetAuthenticate@1 + # Build-WrapperModule.ps1 refuses to run without kiota on PATH even under -SkipKiota + # (the guard is unconditional), and future -Generate runs need it anyway. + - task: PowerShell@2 + displayName: Install kiota CLI + inputs: + targetType: inline + pwsh: true + script: | + dotnet tool install --global Microsoft.OpenApi.Kiota + Write-Host "##vso[task.prependpath]$env:USERPROFILE\.dotnet\tools" - template: .azure-pipelines/common-templates/security-pre-checks.yml@self # Version and prerelease go to the script directly (-ModuleVersion/-Prerelease); the From ce75e7221ca847914dbc37fb631d90a16437fc0b Mon Sep 17 00:00:00 2001 From: Joywambui-maina Date: Fri, 28 Aug 2026 13:52:46 -0700 Subject: [PATCH 2/5] build(wrapper): route NuGet through the internal feed under network isolation api.nuget.org is not reliably reachable from the 1ES pool, so the kiota tool install failed loading the service index - and the module restore would have failed the same way one step later. Writes a pipeline-local nuget.config that puts the MSGraph_PowerShell_V3_Build feed (whose upstream proxies nuget.org) first with nuget.org as fallback, used by every restore on the run. NuGetAuthenticate supplies the credentials; the kiota step also retries. --- .azure-pipelines/wrapper-release.yml | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/.azure-pipelines/wrapper-release.yml b/.azure-pipelines/wrapper-release.yml index 69d6b42075..5c0fff1d8e 100644 --- a/.azure-pipelines/wrapper-release.yml +++ b/.azure-pipelines/wrapper-release.yml @@ -84,15 +84,41 @@ extends: inputs: version: 10.x - task: NuGetAuthenticate@1 + # Under 1ES network isolation api.nuget.org is not reliably reachable, so every restore + # on this pipeline - the kiota tool below and the module build after it - resolves + # through the team's own feed first, whose upstream proxies nuget.org. The config is + # written at the sources root so dotnet picks it up everywhere; NuGetAuthenticate above + # supplies the credentials. + - task: PowerShell@2 + displayName: Route NuGet through the internal feed + inputs: + targetType: inline + pwsh: true + script: | + $cfg = @' + + + + + + + + + '@ + Set-Content -Path '$(Build.SourcesDirectory)/nuget.config' -Value $cfg -Encoding utf8 + Write-Host "wrote $(Build.SourcesDirectory)/nuget.config" # Build-WrapperModule.ps1 refuses to run without kiota on PATH even under -SkipKiota # (the guard is unconditional), and future -Generate runs need it anyway. - task: PowerShell@2 displayName: Install kiota CLI + retryCountOnTaskFailure: 2 inputs: targetType: inline pwsh: true + workingDirectory: $(Build.SourcesDirectory) script: | - dotnet tool install --global Microsoft.OpenApi.Kiota + dotnet tool install --global Microsoft.OpenApi.Kiota --configfile '$(Build.SourcesDirectory)/nuget.config' + if ($LASTEXITCODE -ne 0) { throw "kiota install failed with exit code $LASTEXITCODE" } Write-Host "##vso[task.prependpath]$env:USERPROFILE\.dotnet\tools" - template: .azure-pipelines/common-templates/security-pre-checks.yml@self From 2a9da01dc227f620095c9b11d54c545aa98a6b18 Mon Sep 17 00:00:00 2001 From: Joywambui-maina Date: Fri, 28 Aug 2026 14:18:46 -0700 Subject: [PATCH 3/5] build(wrapper): feed-only NuGet sources under network isolation dotnet tool install probes the service index of every configured source and fails hard if any is unreachable, so keeping nuget.org as a fallback defeated the routing entirely. The pipeline config now lists only the internal feed, whose upstream proxies nuget.org, and the install ignores failed sources. --- .azure-pipelines/wrapper-release.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.azure-pipelines/wrapper-release.yml b/.azure-pipelines/wrapper-release.yml index 5c0fff1d8e..9084e3f2fb 100644 --- a/.azure-pipelines/wrapper-release.yml +++ b/.azure-pipelines/wrapper-release.yml @@ -101,7 +101,6 @@ extends: - '@ @@ -117,7 +116,7 @@ extends: pwsh: true workingDirectory: $(Build.SourcesDirectory) script: | - dotnet tool install --global Microsoft.OpenApi.Kiota --configfile '$(Build.SourcesDirectory)/nuget.config' + dotnet tool install --global Microsoft.OpenApi.Kiota --configfile '$(Build.SourcesDirectory)/nuget.config' --ignore-failed-sources if ($LASTEXITCODE -ne 0) { throw "kiota install failed with exit code $LASTEXITCODE" } Write-Host "##vso[task.prependpath]$env:USERPROFILE\.dotnet\tools" - template: .azure-pipelines/common-templates/security-pre-checks.yml@self From 1243f0d85666d389f9b92766ecfe2e3075d7987b Mon Sep 17 00:00:00 2001 From: Joywambui-maina Date: Fri, 28 Aug 2026 15:16:30 -0700 Subject: [PATCH 4/5] build(wrapper): run the full generation chain at pipeline time Per review: the pipeline now generates the kiota client from the committed OpenAPI docs and the wrappers on top, then compiles - the whole process is built and tested end to end on every run, and a run can never fail on committed clients lagging the docs. Clients remain committed to the repo for reviewable diffs and clean local checkouts. --- .azure-pipelines/wrapper-release.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.azure-pipelines/wrapper-release.yml b/.azure-pipelines/wrapper-release.yml index 9084e3f2fb..a1b903270d 100644 --- a/.azure-pipelines/wrapper-release.yml +++ b/.azure-pipelines/wrapper-release.yml @@ -136,7 +136,11 @@ extends: ArtifactsLocation = '$(Build.ArtifactStagingDirectory)' ModuleVersion = '${{ parameters.PackageVersion }}' Prerelease = '$(WrapperPrerelease)' - SkipKiota = $true + # Deliberately NOT -SkipKiota: the pipeline runs the whole chain - kiota client + # generation from the committed OpenAPI docs, wrapper generation on top, then + # compile - so a change in any step is built and tested end to end, and the run + # can never fail on committed clients lagging the docs. Clients are still + # committed to the repo for reviewable diffs and clean local checkouts. Pack = $true } & '$(Build.SourcesDirectory)/tools/Build-WrapperModule.ps1' @params From bc95f5af022ef99d2f8ec5415bf6eb04daa7781e Mon Sep 17 00:00:00 2001 From: Joywambui-maina Date: Fri, 28 Aug 2026 17:26:38 -0700 Subject: [PATCH 5/5] build(wrapper): suppress credscan false positives in generated synchronization models The kiota-generated model for Graph's synchronizationSecret entity enumerates the API's secret-key names (Oauth2ClientSecret and similar) as enum member strings, which trips CSCAN-GENERAL0120 in four modules and breaks the guardian post-analysis. Schema vocabulary from the OpenAPI document, not secret values - same class of suppression the file already carries for generated examples. --- .../config/credscan/credscan-suppressions.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.azure-pipelines/config/credscan/credscan-suppressions.json b/.azure-pipelines/config/credscan/credscan-suppressions.json index 121de6a119..ba8a476a77 100644 --- a/.azure-pipelines/config/credscan/credscan-suppressions.json +++ b/.azure-pipelines/config/credscan/credscan-suppressions.json @@ -73,6 +73,15 @@ "tools\\Tests\\loadEnv.md" ], "_justification": "[ToolsTest] Examples contain random values recognized as secret" + }, + { + "file": [ + "src\\Applications\\wrapper\\v1.0\\Client\\Models\\SynchronizationSecret.cs", + "src\\Groups\\wrapper\\v1.0\\Client\\Models\\SynchronizationSecret.cs", + "src\\Identity.DirectoryManagement\\wrapper\\v1.0\\Client\\Models\\SynchronizationSecret.cs", + "src\\Users\\wrapper\\v1.0\\Client\\Models\\SynchronizationSecret.cs" + ], + "_justification": "[Wrapper] Kiota-generated model for Graph's synchronizationSecret entity enumerates the API's secret-key names (Oauth2ClientSecret and similar) as enum member strings; these are schema vocabulary from the OpenAPI document, not secret values" } ] }