diff --git a/.azure-pipelines/ci-build.yml b/.azure-pipelines/ci-build.yml index 835d60c9fd7..a3077918519 100644 --- a/.azure-pipelines/ci-build.yml +++ b/.azure-pipelines/ci-build.yml @@ -31,6 +31,9 @@ variables: REGISTRY: 'msgraphprodregistry.azurecr.io' REGISTRY_NAME: 'msgraphprodregistry' IMAGE_NAME: 'public/microsoftgraph/powershell' + # Expose the build identity's token as a process-wide env var so generation scripts (including + # ForEach-Object -Parallel runspaces) can authenticate to the private CFS module feed. (CFSClean) + SYSTEM_ACCESSTOKEN: $(System.AccessToken) trigger: branches: diff --git a/.azure-pipelines/command-metadata-refresh.yml b/.azure-pipelines/command-metadata-refresh.yml index 801e7ebd72b..68b47ade3de 100644 --- a/.azure-pipelines/command-metadata-refresh.yml +++ b/.azure-pipelines/command-metadata-refresh.yml @@ -31,6 +31,9 @@ variables: BuildAgent: ${{ parameters.BuildAgent }} Branch: "ModuleCommandMetadataRefresh" BaseBranch: ${{ parameters.BaseBranch }} + # Expose the build identity's token as a process-wide env var so generation scripts (including + # ForEach-Object -Parallel runspaces) can authenticate to the private CFS module feed. (CFSClean) + SYSTEM_ACCESSTOKEN: $(System.AccessToken) trigger: branches: diff --git a/.azure-pipelines/common-templates/install-tools.yml b/.azure-pipelines/common-templates/install-tools.yml index 0b64943de14..d26de8160cc 100644 --- a/.azure-pipelines/common-templates/install-tools.yml +++ b/.azure-pipelines/common-templates/install-tools.yml @@ -61,6 +61,21 @@ steps: Copy-Item -Path $src -Destination $dst -Force Write-Host "Copied npm config to $dst" + - task: PowerShell@2 + displayName: Register private module feed (CFSClean) + inputs: + targetType: inline + pwsh: true + script: | + # Register the private Azure Artifacts feed (PowerShell Gallery upstream) as a Trusted + # PSRepository so generation-time Install-Module/Find-Module resolve through it instead of + # the public PowerShell Gallery. Persisted for the job (visible to later steps + runspaces). + . "$(Build.SourcesDirectory)/tools/Get-CfsFeedCredential.ps1" + Register-CfsFeed + Get-PSRepository -Name PowerShell_V2_Build | Format-List Name, SourceLocation, InstallationPolicy, Trusted + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + - task: Npm@1 displayName: Install AutoRest inputs: diff --git a/.azure-pipelines/weekly-generation.yml b/.azure-pipelines/weekly-generation.yml index 6be0893c0c6..5cf86adc267 100644 --- a/.azure-pipelines/weekly-generation.yml +++ b/.azure-pipelines/weekly-generation.yml @@ -42,6 +42,9 @@ parameters: variables: BaseBranch: ${{ parameters.BaseBranch }} BuildAgent: ${{ parameters.BuildAgent }} + # Expose the build identity's token as a process-wide env var so generation scripts (including + # ForEach-Object -Parallel runspaces) can authenticate to the private CFS module feed. (CFSClean) + SYSTEM_ACCESSTOKEN: $(System.AccessToken) trigger: none pr: none schedules: diff --git a/tools/BuildModule.ps1 b/tools/BuildModule.ps1 index e6485f5dfd4..3ba051e5e82 100644 --- a/tools/BuildModule.ps1 +++ b/tools/BuildModule.ps1 @@ -66,7 +66,11 @@ if ($ModuleFullName -ne "Microsoft.Graph.Authentication") { } # Lock module GUID. See https://github.com/Azure/autorest.powershell/issues/981. -$ExistingModule = Find-Module $ModuleFullName -Repository PSGallery -ErrorAction SilentlyContinue +# CFSClean: authenticate module queries to the private feed (credential from the build token). +. (Join-Path $PSScriptRoot 'Get-CfsFeedCredential.ps1') +$__cfsCred = Get-CfsFeedCredential +if ($null -ne $__cfsCred) { $PSDefaultParameterValues['Find-Module:Credential'] = $__cfsCred } +$ExistingModule = Find-Module $ModuleFullName -Repository (Get-CfsFeedName) -ErrorAction SilentlyContinue $ModuleGuid = ($null -eq $ExistingModule) ? (New-Guid).Guid : $ExistingModule.AdditionalMetadata.GUID [HashTable]$ModuleManifestSettings = @{ diff --git a/tools/GenerateAuthenticationModule.ps1 b/tools/GenerateAuthenticationModule.ps1 index 6d71c22f35d..18939a6ff0c 100644 --- a/tools/GenerateAuthenticationModule.ps1 +++ b/tools/GenerateAuthenticationModule.ps1 @@ -2,7 +2,7 @@ # Licensed under the MIT License. [CmdletBinding()] Param( - [string] $RepositoryName = "PSGallery", + [string] $RepositoryName = "PowerShell_V2_Build", [string] $RepositoryApiKey, [string] $ArtifactsLocation = (Join-Path $PSScriptRoot "..\artifacts\"), [switch] $Build, diff --git a/tools/GenerateHelp.ps1 b/tools/GenerateHelp.ps1 index 24750d984f1..1138a96912d 100644 --- a/tools/GenerateHelp.ps1 +++ b/tools/GenerateHelp.ps1 @@ -7,8 +7,12 @@ Param( [string] $ModuleMappingConfigPath = (Join-Path $PSScriptRoot "..\config\ModulesMapping.jsonc") ) # Install PlatyPS +# CFSClean: install tooling modules from the private feed (PowerShell Gallery upstream). +. (Join-Path $PSScriptRoot 'Get-CfsFeedCredential.ps1') +$__cfsCred = Get-CfsFeedCredential +if ($null -ne $__cfsCred) { $PSDefaultParameterValues['Install-Module:Credential'] = $__cfsCred } if (!(Get-Module -Name PlatyPS -ListAvailable)) { - Install-Module PlatyPS -Force + Install-Module PlatyPS -Repository (Get-CfsFeedName) -Force } Import-Module PlatyPS -Force -Scope Global diff --git a/tools/GenerateMetaModule.ps1 b/tools/GenerateMetaModule.ps1 index a921fb8cb25..1419db24f47 100644 --- a/tools/GenerateMetaModule.ps1 +++ b/tools/GenerateMetaModule.ps1 @@ -6,7 +6,7 @@ Param( [ValidateSet("v1.0", "beta")] $ApiVersion = @("v1.0", "beta"), [string] $RepositoryApiKey, - [string] $RepositoryName = "PSGallery", + [string] $RepositoryName = "PowerShell_V2_Build", [string] $ArtifactsLocation = (Join-Path $PSScriptRoot "..\artifacts\"), [switch] $Pack, [switch] $Publish, diff --git a/tools/GenerateRollUpModule.ps1 b/tools/GenerateRollUpModule.ps1 index 25e779bdf6b..7a76433b230 100644 --- a/tools/GenerateRollUpModule.ps1 +++ b/tools/GenerateRollUpModule.ps1 @@ -4,7 +4,7 @@ [CmdletBinding()] Param( [string] $RepositoryApiKey, - [string] $RepositoryName = "PSGallery", + [string] $RepositoryName = "PowerShell_V2_Build", [string] $ArtifactsLocation = (Join-Path $PSScriptRoot "..\artifacts\"), [string] $ModuleMappingConfigPath = (Join-Path $PSScriptRoot "..\config\ModulesMapping.jsonc"), [int] $ModulePreviewNumber = -1, @@ -19,6 +19,14 @@ enum VersionState { } $ErrorActionPreference = 'Stop' $LASTEXITCODE = $null +# CFSClean: authenticate module installs/queries to the private feed (credential from the build token). +. (Join-Path $PSScriptRoot 'Get-CfsFeedCredential.ps1') +$__cfsCred = Get-CfsFeedCredential +if ($null -ne $__cfsCred) { + $PSDefaultParameterValues['Find-Module:Credential'] = $__cfsCred + $PSDefaultParameterValues['Install-Module:Credential'] = $__cfsCred + $PSDefaultParameterValues['Save-Module:Credential'] = $__cfsCred +} if ($PSEdition -ne 'Core') { Write-Error 'This script requires PowerShell Core to execute. [Note] Generated cmdlets will work in both PowerShell Core or Windows PowerShell.' } diff --git a/tools/Get-CfsFeedCredential.ps1 b/tools/Get-CfsFeedCredential.ps1 new file mode 100644 index 00000000000..9356ff906ed --- /dev/null +++ b/tools/Get-CfsFeedCredential.ps1 @@ -0,0 +1,41 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +<# +.SYNOPSIS + Shared helpers to route PowerShell module installs/queries through the private Azure Artifacts + feed (CFSClean network isolation) instead of the public PowerShell Gallery. + +.DESCRIPTION + The private feed `PowerShell_V2_Build` has a PowerShell Gallery upstream, so it can serve both + the internally published Graph modules and public tooling modules (Pester, PlatyPS, + powershell-yaml, PowerHTML). Reads the credential from the process-wide $env:SYSTEM_ACCESSTOKEN + (mapped from $(System.AccessToken) at the pipeline `variables:` level), so it works inside + ForEach-Object -Parallel runspaces where session state is not inherited. +#> + +$script:CfsFeedName = 'PowerShell_V2_Build' +$script:CfsFeedUrl = 'https://microsoftgraph.pkgs.visualstudio.com/0985d294-5762-4bc2-a565-161ef349ca3e/_packaging/PowerShell_V2_Build/nuget/v2' + +function Get-CfsFeedName { + return $script:CfsFeedName +} + +function Get-CfsFeedCredential { + # Returns a PSCredential built from the build identity's access token, or $null when the token is + # unavailable (e.g. local dev), in which case callers fall back to their default behaviour. + if ([string]::IsNullOrWhiteSpace($env:SYSTEM_ACCESSTOKEN)) { + return $null + } + $token = ConvertTo-SecureString $env:SYSTEM_ACCESSTOKEN -AsPlainText -Force + return [System.Management.Automation.PSCredential]::new('azure', $token) +} + +function Register-CfsFeed { + # Registers the private feed as a Trusted PSRepository (idempotent). Persisted under the user's + # PowerShellGet config, so a single registration per job is visible to later steps and runspaces. + $cred = Get-CfsFeedCredential + if (-not (Get-PSRepository -Name $script:CfsFeedName -ErrorAction SilentlyContinue)) { + Register-PSRepository -Name $script:CfsFeedName -SourceLocation $script:CfsFeedUrl -InstallationPolicy Trusted -Credential $cred + } +} diff --git a/tools/ImportExamples.ps1 b/tools/ImportExamples.ps1 index 030b224e757..3212d68e1f8 100644 --- a/tools/ImportExamples.ps1 +++ b/tools/ImportExamples.ps1 @@ -575,13 +575,17 @@ function Get-ExistingCorrectExamples { } $RetainedExamples = New-Object Collections.Generic.List[string] +# CFSClean: install tooling modules from the private feed (PowerShell Gallery upstream). +. (Join-Path $PSScriptRoot 'Get-CfsFeedCredential.ps1') +$__cfsCred = Get-CfsFeedCredential +if ($null -ne $__cfsCred) { $PSDefaultParameterValues['Install-Module:Credential'] = $__cfsCred } if (!(Get-Module "powershell-yaml" -ListAvailable -ErrorAction SilentlyContinue)) { - Install-Module "powershell-yaml" -AcceptLicense -Scope CurrentUser -Force + Install-Module "powershell-yaml" -Repository (Get-CfsFeedName) -AcceptLicense -Scope CurrentUser -Force } If (-not (Get-Module -ErrorAction Ignore -ListAvailable PowerHTML)) { Write-Verbose "Installing PowerHTML module for the current user..." - Install-Module PowerHTML -ErrorAction Stop -Scope CurrentUser -Force + Install-Module PowerHTML -Repository (Get-CfsFeedName) -ErrorAction Stop -Scope CurrentUser -Force } Import-Module -ErrorAction Stop PowerHTML diff --git a/tools/TestModule.ps1 b/tools/TestModule.ps1 index adfb15d7576..081f9ca81d8 100644 --- a/tools/TestModule.ps1 +++ b/tools/TestModule.ps1 @@ -5,8 +5,12 @@ param([string] $ModulePath, [string] $ModuleName, [string] $ModuleTestsPath, [sw $ErrorActionPreference = 'Stop' # Install Pester +# CFSClean: install tooling modules from the private feed (PowerShell Gallery upstream). +. (Join-Path $PSScriptRoot 'Get-CfsFeedCredential.ps1') +$__cfsCred = Get-CfsFeedCredential +if ($null -ne $__cfsCred) { $PSDefaultParameterValues['Install-Module:Credential'] = $__cfsCred } if (!(Get-Module -Name Pester -ListAvailable)) { - Install-Module -Name Pester -Force -SkipPublisherCheck + Install-Module -Name Pester -Repository (Get-CfsFeedName) -Force -SkipPublisherCheck } if(-not $Isolated) { diff --git a/tools/UpdateOpenApi.ps1 b/tools/UpdateOpenApi.ps1 index 64d4e4f154c..1b98f04ca41 100644 --- a/tools/UpdateOpenApi.ps1 +++ b/tools/UpdateOpenApi.ps1 @@ -15,8 +15,11 @@ if ($PSEdition -ne 'Core') { } if (!(Get-Module powershell-yaml -ListAvailable)) { - # Install Powershell-yaml - Install-Module powershell-yaml -Force + # Install Powershell-yaml from the private feed (PowerShell Gallery upstream). (CFSClean) + . (Join-Path $PSScriptRoot 'Get-CfsFeedCredential.ps1') + $__cfsCred = Get-CfsFeedCredential + if ($null -ne $__cfsCred) { $PSDefaultParameterValues['Install-Module:Credential'] = $__cfsCred } + Install-Module powershell-yaml -Repository (Get-CfsFeedName) -Force } $GraphVersion = "v1.0" diff --git a/tools/ValidateUpdatedModuleVersion.ps1 b/tools/ValidateUpdatedModuleVersion.ps1 index b916f78f5b6..69d925b1725 100644 --- a/tools/ValidateUpdatedModuleVersion.ps1 +++ b/tools/ValidateUpdatedModuleVersion.ps1 @@ -4,7 +4,7 @@ param( [Parameter()][ValidateNotNullOrEmpty()][string] $ModuleName, [Parameter()][ValidateNotNullOrEmpty()][string] $NextVersion, - [Parameter()][string] $PSRepository = "PSGallery", + [Parameter()][string] $PSRepository = "PowerShell_V2_Build", [int] $ModulePreviewNumber = -1 ) enum VersionState { @@ -18,6 +18,15 @@ enum VersionState { Import-Module PackageManagement Import-Module PowerShellGet +# CFSClean: authenticate module queries to the private feed (credential from the build token). +. (Join-Path $PSScriptRoot 'Get-CfsFeedCredential.ps1') +$__cfsCred = Get-CfsFeedCredential +if ($null -ne $__cfsCred) { + $PSDefaultParameterValues['Find-Module:Credential'] = $__cfsCred + $PSDefaultParameterValues['Install-Module:Credential'] = $__cfsCred + $PSDefaultParameterValues['Save-Module:Credential'] = $__cfsCred +} + $AllowPreRelease = $true if($ModulePreviewNumber -eq -1) { $AllowPreRelease = $false diff --git a/tools/Versions/BumpModuleVersion.ps1 b/tools/Versions/BumpModuleVersion.ps1 index 6325b5ddc0b..411fe80b426 100644 --- a/tools/Versions/BumpModuleVersion.ps1 +++ b/tools/Versions/BumpModuleVersion.ps1 @@ -7,12 +7,17 @@ Param( [switch] $BumpBetaModule, [switch] $BumpAuthModule, [string] $PreReleaseTag, - [string] $Repository = "PSGallery" + [string] $Repository = "PowerShell_V2_Build" ) $ErrorActionPreference = "Stop" . $PSScriptRoot\SetModuleVersion.ps1 +# CFSClean: authenticate module queries to the private feed (credential from the build token). +. (Join-Path $PSScriptRoot '..\Get-CfsFeedCredential.ps1') +$__cfsCred = Get-CfsFeedCredential +if ($null -ne $__cfsCred) { $PSDefaultParameterValues['Find-Module:Credential'] = $__cfsCred } + # Calculate and bump v1.0 module version if ($BumpV1Module.IsPresent) { $v1Module = Find-Module "Microsoft.Graph" -Repository $Repository -AllowPrerelease