Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .azure-pipelines/1es-entra-powershell-ci-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ extends:
- template: .azure-pipelines/generation-templates/generate_adapter-1es.yml@self
parameters:
Sign: ${{ parameters.Sign }}
# 1ES network isolation (CFSClean/CFSClean2) blocks the public
# PowerShell Gallery. Install dependencies from the CFS feed instead.
DependencyRepository: 'EntraCFS'
# TODO: replace with the onboarded CFS feed v2 index URL (see https://aka.ms/cfs).
CfsFeedUrl: 'https://pkgs.dev.azure.com/msazure/One/_packaging/REPLACE_WITH_CFS_FEED/nuget/v2'

- ${{ if and(eq(parameters.Pack, true), eq(parameters.Sign, true)) }}:
- template: .azure-pipelines/common-templates/esrp/codesign-nuget.yml@self
Expand Down
5 changes: 5 additions & 0 deletions .azure-pipelines/1es-entra-powershell-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,9 @@ extends:
- template: .azure-pipelines/generation-templates/generate_adapter-1es.yml@self
parameters:
Sign: false
# 1ES network isolation (CFSClean/CFSClean2) blocks the public
# PowerShell Gallery. Install dependencies from the CFS feed instead.
DependencyRepository: 'EntraCFS'
# TODO: replace with the onboarded CFS feed v2 index URL (see https://aka.ms/cfs).
CfsFeedUrl: 'https://pkgs.dev.azure.com/msazure/One/_packaging/REPLACE_WITH_CFS_FEED/nuget/v2'

33 changes: 31 additions & 2 deletions .azure-pipelines/generation-templates/generate_adapter-1es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,21 @@ parameters:
- name: Integration
type: boolean
default: false
# Name of the PowerShell repository build/test dependencies are installed from.
# Defaults to the public PowerShell Gallery; 1ES pipelines pass a CFS-backed
# feed to satisfy network-isolation (CFSClean) policies.
- name: DependencyRepository
type: string
default: 'PSGallery'
# v2 index URL of the CFS feed backing DependencyRepository.
- name: CfsFeedUrl
type: string
default: ''

steps:
- ${{ if ne(parameters.CfsFeedUrl, '') }}:
- task: NuGetAuthenticate@1
displayName: 'Authenticate to CFS feed'
- task: powershell@2
displayName: 'Show current PowerShell version information'
inputs:
Expand All @@ -30,12 +43,20 @@ steps:
script: |
./build/Install-Dependencies.ps1 -ModuleName Entra -Verbose
pwsh: true
env:
DEPENDENCY_PS_REPO: ${{ parameters.DependencyRepository }}
DEPENDENCY_PS_FEED_URL: ${{ parameters.CfsFeedUrl }}
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- task: powershell@2
displayName: 'Install PlatyPS'
inputs:
targetType: inline
script: Install-Module PlatyPS -scope currentuser -Force
script: ./build/Install-GalleryModule.ps1 -Name PlatyPS
pwsh: true
env:
DEPENDENCY_PS_REPO: ${{ parameters.DependencyRepository }}
DEPENDENCY_PS_FEED_URL: ${{ parameters.CfsFeedUrl }}
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- task: powershell@2
displayName: 'Build Entra'
inputs:
Expand Down Expand Up @@ -102,6 +123,10 @@ steps:
script: |
./build/Install-Dependencies.ps1 -ModuleName EntraBeta -Verbose
pwsh: true
env:
DEPENDENCY_PS_REPO: ${{ parameters.DependencyRepository }}
DEPENDENCY_PS_FEED_URL: ${{ parameters.CfsFeedUrl }}
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- task: powershell@2
displayName: 'Build EntraBeta'
inputs:
Expand Down Expand Up @@ -157,8 +182,12 @@ steps:
displayName: 'Install Pester'
inputs:
targetType: inline
script: Install-Module Pester -scope currentuser -SkipPublisherCheck -Force
script: ./build/Install-GalleryModule.ps1 -Name Pester -SkipPublisherCheck
pwsh: true
env:
DEPENDENCY_PS_REPO: ${{ parameters.DependencyRepository }}
DEPENDENCY_PS_FEED_URL: ${{ parameters.CfsFeedUrl }}
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- task: powershell@2
displayName: 'Run tests Entra'
inputs:
Expand Down
33 changes: 31 additions & 2 deletions .azure-pipelines/generation-templates/generate_adapter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,21 @@ parameters:
- name: Integration
type: boolean
default: false
# Name of the PowerShell repository build/test dependencies are installed from.
# Defaults to the public PowerShell Gallery; pipelines pass a CFS-backed feed
# to satisfy network-isolation (CFSClean) policies.
- name: DependencyRepository
type: string
default: 'PSGallery'
# v2 index URL of the CFS feed backing DependencyRepository.
- name: CfsFeedUrl
type: string
default: ''

steps:
- ${{ if ne(parameters.CfsFeedUrl, '') }}:
- task: NuGetAuthenticate@1
displayName: 'Authenticate to CFS feed'
- task: powershell@2
displayName: 'Show current PowerShell version information'
inputs:
Expand All @@ -30,12 +43,20 @@ steps:
script: |
./build/Install-Dependencies.ps1 -ModuleName Entra -Verbose
pwsh: true
env:
DEPENDENCY_PS_REPO: ${{ parameters.DependencyRepository }}
DEPENDENCY_PS_FEED_URL: ${{ parameters.CfsFeedUrl }}
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- task: powershell@2
displayName: 'Install PlatyPS'
inputs:
targetType: inline
script: Install-Module PlatyPS -scope currentuser -Force
script: ./build/Install-GalleryModule.ps1 -Name PlatyPS
pwsh: true
env:
DEPENDENCY_PS_REPO: ${{ parameters.DependencyRepository }}
DEPENDENCY_PS_FEED_URL: ${{ parameters.CfsFeedUrl }}
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- task: powershell@2
displayName: 'Build Entra'
inputs:
Expand Down Expand Up @@ -106,6 +127,10 @@ steps:
script: |
./build/Install-Dependencies.ps1 -ModuleName EntraBeta -Verbose
pwsh: true
env:
DEPENDENCY_PS_REPO: ${{ parameters.DependencyRepository }}
DEPENDENCY_PS_FEED_URL: ${{ parameters.CfsFeedUrl }}
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- task: powershell@2
displayName: 'Build EntraBeta'
inputs:
Expand Down Expand Up @@ -165,8 +190,12 @@ steps:
displayName: 'Install Pester'
inputs:
targetType: inline
script: Install-Module Pester -scope currentuser -SkipPublisherCheck -Force
script: ./build/Install-GalleryModule.ps1 -Name Pester -SkipPublisherCheck
pwsh: true
env:
DEPENDENCY_PS_REPO: ${{ parameters.DependencyRepository }}
DEPENDENCY_PS_FEED_URL: ${{ parameters.CfsFeedUrl }}
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- task: powershell@2
displayName: 'Run tests Entra'
inputs:
Expand Down
7 changes: 6 additions & 1 deletion .azure-pipelines/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,9 @@ stages:
- template: ./generation-templates/generate_adapter.yml
parameters:
Integration: true
Sign: false
Sign: false
# Install dependencies from the CFS feed to stay consistent with the
# 1ES CI/PR pipelines (CFSClean/CFSClean2 network-isolation policies).
DependencyRepository: 'EntraCFS'
# TODO: replace with the onboarded CFS feed v2 index URL (see https://aka.ms/cfs).
CfsFeedUrl: 'https://pkgs.dev.azure.com/msazure/One/_packaging/REPLACE_WITH_CFS_FEED/nuget/v2'
11 changes: 9 additions & 2 deletions build/Install-Dependencies.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ param(

[ValidateScript({ Test-Path $_ })]
[string]
$ModuleSettingsPath
$ModuleSettingsPath,

# PowerShell repository to install dependencies from. Defaults to the public
# PowerShell Gallery for local development. CI pipelines running under 1ES
# network isolation set DEPENDENCY_PS_REPO to a CFS-backed feed to satisfy the
# CFSClean/CFSClean2 policies. See build/Install-GalleryModule.ps1.
[string]
$Repository = $(if ($env:DEPENDENCY_PS_REPO) { $env:DEPENDENCY_PS_REPO } else { 'PSGallery' })
)

. "$psscriptroot/common-functions.ps1"
Expand All @@ -26,5 +33,5 @@ Write-Verbose("Skipping deprecated source module '$($content.sourceModule)' - no

foreach ($moduleName in $content.destinationModuleName){
Write-Verbose("Installing Module $($moduleName)")
Install-Module $moduleName -scope currentuser -RequiredVersion $content.destinationModuleVersion -Force -AllowClobber
& "$PSScriptRoot/Install-GalleryModule.ps1" -Name $moduleName -RequiredVersion $content.destinationModuleVersion -Repository $Repository -AllowClobber -Verbose:($VerbosePreference -eq 'Continue')
}
119 changes: 119 additions & 0 deletions build/Install-GalleryModule.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# ------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
# ------------------------------------------------------------------------------

<#
.SYNOPSIS
Installs PowerShell modules used by the build, routing the download through a
CFS (Centralized Feed Service) feed when one is configured.

.DESCRIPTION
The 1ES CI/PR pipelines run under network isolation. The CFSClean/CFSClean2
policies block access to the public PowerShell Gallery (powershellgallery.com)
and require every package to be restored from a CFS-backed Azure Artifacts
feed instead. See build/BUILD.md and https://aka.ms/1es/netiso/pipelinetemplates.

This helper is the single place where build/test dependencies are installed so
that the CFS wiring lives in one location:

* Local development uses the public PowerShell Gallery (the default), so no
extra configuration is required.
* CI pipelines set the DEPENDENCY_PS_REPO and DEPENDENCY_PS_FEED_URL
environment variables (and expose SYSTEM_ACCESSTOKEN) so the same modules
are restored from the CFS feed.

If a non-PSGallery repository is requested but no feed URL has been supplied
(for example while the CFS feed URL is still a placeholder in a draft PR), the
helper warns and falls back to the public PowerShell Gallery instead of
failing the build.

.PARAMETER Name
One or more module names to install.

.PARAMETER RequiredVersion
Optional exact version to install.

.PARAMETER Repository
Name of the PowerShell repository to install from. Defaults to the value of
the DEPENDENCY_PS_REPO environment variable, or 'PSGallery' when it is unset.

.PARAMETER FeedUrl
The v2 index URL of the CFS feed backing -Repository. Defaults to the
DEPENDENCY_PS_FEED_URL environment variable. Only used when -Repository is not
'PSGallery'.

.PARAMETER Token
Access token used to authenticate to the CFS feed. Defaults to the
SYSTEM_ACCESSTOKEN environment variable (mapped from $(System.AccessToken) in
the pipeline).
#>
[CmdletBinding()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'The pipeline access token is only available as plain text and must be converted to a SecureString to build a PSCredential for the CFS feed.')]
param(
[Parameter(Mandatory)]
[string[]] $Name,

[string] $RequiredVersion,

[string] $Repository = $(if ($env:DEPENDENCY_PS_REPO) { $env:DEPENDENCY_PS_REPO } else { 'PSGallery' }),

[string] $FeedUrl = $env:DEPENDENCY_PS_FEED_URL,

[string] $Token = $env:SYSTEM_ACCESSTOKEN,

[switch] $SkipPublisherCheck,

[switch] $AllowClobber
)

$ErrorActionPreference = 'Stop'

# Treat a not-yet-onboarded placeholder feed URL as "no feed configured".
$feedIsPlaceholder = [string]::IsNullOrWhiteSpace($FeedUrl) -or $FeedUrl -match 'REPLACE_WITH|<.*>'

if ($Repository -ne 'PSGallery' -and $feedIsPlaceholder) {
Write-Warning "Repository '$Repository' was requested but no CFS feed URL is configured (DEPENDENCY_PS_FEED_URL). Falling back to the public PowerShell Gallery. Set the CFS feed URL to satisfy 1ES network-isolation (CFSClean) policies."
$Repository = 'PSGallery'
}

$credential = $null

if ($Repository -ne 'PSGallery') {
if ($Token) {
$securePat = ConvertTo-SecureString $Token -AsPlainText -Force
$credential = [System.Management.Automation.PSCredential]::new('cfs', $securePat)
}
else {
Write-Warning "No access token available (SYSTEM_ACCESSTOKEN); authentication to '$Repository' may fail."
}

# Register the CFS feed for PowerShellGet (idempotent - registration persists
# across pipeline steps, but the credential does not, so it is always supplied
# again at install time below).
if (-not (Get-PSRepository -Name $Repository -ErrorAction SilentlyContinue)) {
Write-Verbose "Registering PSRepository '$Repository' -> $FeedUrl"
$register = @{
Name = $Repository
SourceLocation = $FeedUrl
InstallationPolicy = 'Trusted'
}
if ($credential) { $register['Credential'] = $credential }
Register-PSRepository @register
}
}

foreach ($module in $Name) {
Write-Verbose "Installing module '$module' from repository '$Repository'"
$install = @{
Name = $module
Repository = $Repository
Scope = 'CurrentUser'
Force = $true
}
if ($RequiredVersion) { $install['RequiredVersion'] = $RequiredVersion }
if ($SkipPublisherCheck) { $install['SkipPublisherCheck'] = $true }
if ($AllowClobber) { $install['AllowClobber'] = $true }
if ($credential) { $install['Credential'] = $credential }

Install-Module @install
}