Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added WikiSource documentation and DscResource.DocGenerator build and publish workflows.
- Added post-deployment notification validation that fails the build when PowerShell Universal reports a deployment error.
- Added module-scoped compound tasks for pulling a packaged module from a PSU resource repository or packaging and pushing a complete offline repository, including post-deployment validation.
- Added `UniversalSkipCertificateCheck` build task and yaml configuration setting to bypass TLS certificate validation when calling the PowerShell Universal server, useful for self-signed certificates.

### Changed

- Converted the generated sample module into an InvokeBuild task module.
- Changed the offline automation repository manifest file name from a fixed `repository.psd1` to `<ModuleName>.psd1`, and preserved the prerelease tag on the module version, so PowerShell Universal shows the correct module name and full version during deployment.

### Deprecated

Expand Down
58 changes: 58 additions & 0 deletions source/Private/Invoke-UniversalRestMethod.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
function Invoke-UniversalRestMethod
{
<#
.SYNOPSIS
Invokes Invoke-RestMethod with optional certificate validation bypass.

.DESCRIPTION
Forwards the supplied parameters to Invoke-RestMethod. When
SkipCertificateCheck is enabled, the request bypasses TLS
certificate validation using the native parameter on PowerShell 6
and above, or a temporary ServicePointManager callback on Windows
PowerShell.

.PARAMETER RestMethodParameters
Parameters forwarded to Invoke-RestMethod.

.PARAMETER SkipCertificateCheck
Whether to bypass TLS certificate validation for the request.

.EXAMPLE
Invoke-UniversalRestMethod -RestMethodParameters $requestParameters -SkipCertificateCheck $true
#>
[CmdletBinding()]
[OutputType([System.Object])]
param
(
[Parameter(Mandatory = $true)]
[System.Collections.Hashtable]
$RestMethodParameters,

[Parameter()]
[System.Boolean]
$SkipCertificateCheck = $false
)

if (-not $SkipCertificateCheck)
{
return Invoke-RestMethod @RestMethodParameters
}

if ($PSVersionTable.PSVersion.Major -ge 6)
{
return Invoke-RestMethod @RestMethodParameters -SkipCertificateCheck
}

$originalCallback = [System.Net.ServicePointManager]::ServerCertificateValidationCallback

try
{
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }

Invoke-RestMethod @RestMethodParameters
}
finally
{
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = $originalCallback
}
}
32 changes: 28 additions & 4 deletions source/Private/New-UniversalAutomationRepositoryManifest.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ function New-UniversalAutomationRepositoryManifest
Creates the descriptor for a PowerShell Universal repository package.

.DESCRIPTION
Writes repository.psd1 with the module name and metadata required by
PowerShell Universal when activating an offline automation repository.
Writes a <ModuleName>.psd1 manifest with the module name, prerelease
tag, and metadata required by PowerShell Universal when activating an
offline automation repository.

.PARAMETER Path
Destination path for repository.psd1.
Destination path for the <ModuleName>.psd1 repository manifest.

.PARAMETER Module
Built module information used to populate repository metadata.
Expand Down Expand Up @@ -37,10 +38,20 @@ function New-UniversalAutomationRepositoryManifest
$ModuleVersion
)

$versionPart = $ModuleVersion -replace '-.*$', ''
$prereleasePart = if ($ModuleVersion -match '-(.+)$')
{
$Matches[1]
}
else
{
''
}

$manifestParameters = @{
Path = $Path
RootModule = $Module.Name
ModuleVersion = ($ModuleVersion -replace '-.*$', '')
ModuleVersion = $versionPart
Guid = (New-Guid)
Author = $Module.Author
CompanyName = $Module.CompanyName
Expand All @@ -52,6 +63,19 @@ function New-UniversalAutomationRepositoryManifest
AliasesToExport = '*'
}

if (-not [System.String]::IsNullOrWhiteSpace($prereleasePart))
{
# Use PrivateData.PSData.Prerelease instead of the -Prerelease parameter because
# New-ModuleManifest only gained -Prerelease in PowerShell 6.0. PrivateData works
# identically on Windows PowerShell 5.1 and PowerShell 7, and is the same location
# PowerShellGet/PSResourceGet store the prerelease tag in the resulting manifest.
$manifestParameters['PrivateData'] = @{
PSData = @{
Prerelease = $prereleasePart
}
}
}

if ($PSCmdlet.ShouldProcess($Path, 'Create PowerShell Universal repository manifest'))
{
New-ModuleManifest @manifestParameters
Expand Down
11 changes: 9 additions & 2 deletions source/Public/Get-UniversalDeploymentError.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ function Get-UniversalDeploymentError
.PARAMETER FilterText
Optional text that must appear in the notification title or description.

.PARAMETER SkipCertificateCheck
Whether to bypass TLS certificate validation for the request.

.EXAMPLE
Get-UniversalDeploymentError -ServerUrl $url -AppToken $token -Since $startedAt
#>
Expand All @@ -41,7 +44,11 @@ function Get-UniversalDeploymentError

[Parameter()]
[System.String]
$FilterText
$FilterText,

[Parameter()]
[System.Boolean]
$SkipCertificateCheck = $false
)

$requestParameters = @{
Expand All @@ -51,7 +58,7 @@ function Get-UniversalDeploymentError
}
Method = 'Get'
}
$response = Invoke-RestMethod @requestParameters
$response = Invoke-UniversalRestMethod -RestMethodParameters $requestParameters -SkipCertificateCheck $SkipCertificateCheck
$notifications = if ($null -ne $response.page)
{
@($response.page)
Expand Down
47 changes: 41 additions & 6 deletions source/Public/Install-UniversalModuleFromRepository.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ function Install-UniversalModuleFromRepository
.PARAMETER RepositoryAutoRemove
Whether to remove the repository after the deployment attempt.

.PARAMETER SkipCertificateCheck
Whether to bypass TLS certificate validation for the request.

.EXAMPLE
Install-UniversalModuleFromRepository @installParameters
#>
Expand Down Expand Up @@ -62,15 +65,24 @@ function Install-UniversalModuleFromRepository

[Parameter()]
[System.Boolean]
$RepositoryAutoRemove = $true
$RepositoryAutoRemove = $true,

[Parameter()]
[System.Boolean]
$SkipCertificateCheck = $false
)

$headers = @{
Authorization = 'Bearer {0}' -f $AppToken
Accept = 'application/json'
}
$repositoryEndpoint = '{0}/api/v1/resourceRepository' -f $ServerUrl
$repositories = @(Invoke-RestMethod -Uri $repositoryEndpoint -Headers $headers -Method Get)
$listRepositoriesParameters = @{
Uri = $repositoryEndpoint
Headers = $headers
Method = 'Get'
}
$repositories = @(Invoke-UniversalRestMethod -RestMethodParameters $listRepositoriesParameters -SkipCertificateCheck $SkipCertificateCheck)
$existingRepository = $repositories |
Where-Object -FilterScript { $_.name -eq $RepositoryName } |
Select-Object -First 1
Expand All @@ -86,7 +98,12 @@ function Install-UniversalModuleFromRepository
if ((-not $sameUrl -or -not [System.Boolean] $existingRepository.trusted) -and $RepositoryAutoRemove)
{
$deleteUri = '{0}/{1}' -f $repositoryEndpoint, [System.Uri]::EscapeDataString($RepositoryName)
$null = Invoke-RestMethod -Uri $deleteUri -Headers $headers -Method Delete
$deleteRepositoryParameters = @{
Uri = $deleteUri
Headers = $headers
Method = 'Delete'
}
$null = Invoke-UniversalRestMethod -RestMethodParameters $deleteRepositoryParameters -SkipCertificateCheck $SkipCertificateCheck
$existingRepository = $null
}
}
Expand All @@ -100,7 +117,14 @@ function Install-UniversalModuleFromRepository
id = 0
} | ConvertTo-Json -Depth 5

$null = Invoke-RestMethod -Uri $repositoryEndpoint -Headers $headers -Method Post -Body $body -ContentType 'application/json; charset=utf-8'
$createRepositoryParameters = @{
Uri = $repositoryEndpoint
Headers = $headers
Method = 'Post'
Body = $body
ContentType = 'application/json; charset=utf-8'
}
$null = Invoke-UniversalRestMethod -RestMethodParameters $createRepositoryParameters -SkipCertificateCheck $SkipCertificateCheck
}

try
Expand All @@ -111,7 +135,13 @@ function Install-UniversalModuleFromRepository
$ModuleVersion
[System.Uri]::EscapeDataString($RepositoryName)
)
$null = Invoke-RestMethod -Uri $deployUri -Headers $headers -Method Put -ContentType 'application/octet-stream; charset=utf-8'
$deployModuleParameters = @{
Uri = $deployUri
Headers = $headers
Method = 'Put'
ContentType = 'application/octet-stream; charset=utf-8'
}
$null = Invoke-UniversalRestMethod -RestMethodParameters $deployModuleParameters -SkipCertificateCheck $SkipCertificateCheck
}
finally
{
Expand All @@ -120,7 +150,12 @@ function Install-UniversalModuleFromRepository
$deleteUri = '{0}/{1}' -f $repositoryEndpoint, [System.Uri]::EscapeDataString($RepositoryName)
try
{
$null = Invoke-RestMethod -Uri $deleteUri -Headers $headers -Method Delete
$deleteRepositoryParameters = @{
Uri = $deleteUri
Headers = $headers
Method = 'Delete'
}
$null = Invoke-UniversalRestMethod -RestMethodParameters $deleteRepositoryParameters -SkipCertificateCheck $SkipCertificateCheck
}
catch
{
Expand Down
11 changes: 9 additions & 2 deletions source/Public/Invoke-UniversalDeploymentUpload.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ function Invoke-UniversalDeploymentUpload
.PARAMETER Path
Path to the package that is uploaded as the request body.

.PARAMETER SkipCertificateCheck
Whether to bypass TLS certificate validation for the request.

.EXAMPLE
Invoke-UniversalDeploymentUpload -Uri $uri -AppToken $token -Path $packagePath
#>
Expand All @@ -34,7 +37,11 @@ function Invoke-UniversalDeploymentUpload

[Parameter(Mandatory = $true)]
[System.String]
$Path
$Path,

[Parameter()]
[System.Boolean]
$SkipCertificateCheck = $false
)

if (-not (Test-Path -Path $Path))
Expand All @@ -52,5 +59,5 @@ function Invoke-UniversalDeploymentUpload
ContentType = 'application/octet-stream; charset=utf-8'
}

Invoke-RestMethod @requestParameters
Invoke-UniversalRestMethod -RestMethodParameters $requestParameters -SkipCertificateCheck $SkipCertificateCheck
}
5 changes: 3 additions & 2 deletions source/Public/New-UniversalAutomationRepositoryPackage.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ function New-UniversalAutomationRepositoryPackage

.DESCRIPTION
Stages the built module and its required modules using the PowerShell
Universal repository layout, creates repository.psd1, and compresses it.
Universal repository layout, creates a <ModuleName>.psd1 repository
manifest, and compresses it.

.PARAMETER BuiltModuleManifest
Path to the manifest of the built project module.
Expand Down Expand Up @@ -79,7 +80,7 @@ function New-UniversalAutomationRepositoryPackage
$null = New-Item -Path $modulesDestination -ItemType Directory -Force
Copy-UniversalRepositoryModule -Module $module -ModulesDestinationPath $modulesDestination -Visited @{ }

$repositoryManifestPath = Join-Path -Path $stagingDirectory -ChildPath 'repository.psd1'
$repositoryManifestPath = Join-Path -Path $stagingDirectory -ChildPath ('{0}.psd1' -f $module.Name)
New-UniversalAutomationRepositoryManifest -Path $repositoryManifestPath -Module $module -ModuleVersion $ModuleVersion -Confirm:$false

if (Test-Path -Path $zipPath)
Expand Down
20 changes: 20 additions & 0 deletions source/Public/Resolve-UniversalServerConfiguration.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ function Resolve-UniversalServerConfiguration
.PARAMETER UnpinnedWasBound
Indicates that Unpinned was supplied explicitly.

.PARAMETER SkipCertificateCheck
Whether to bypass TLS certificate validation for requests to the server.

.PARAMETER SkipCertificateCheckWasBound
Indicates that SkipCertificateCheck was supplied explicitly.

.PARAMETER RequireRepository
Requires and resolves resource repository settings.

Expand Down Expand Up @@ -81,6 +87,14 @@ function Resolve-UniversalServerConfiguration
[System.Boolean]
$UnpinnedWasBound = $false,

[Parameter()]
[System.Boolean]
$SkipCertificateCheck = $false,

[Parameter()]
[System.Boolean]
$SkipCertificateCheckWasBound = $false,

[Parameter()]
[System.Management.Automation.SwitchParameter]
$RequireRepository
Expand Down Expand Up @@ -148,12 +162,18 @@ function Resolve-UniversalServerConfiguration
$Unpinned = [System.Convert]::ToBoolean($server.UniversalUnpinned)
}

if (-not $SkipCertificateCheckWasBound -and $null -ne $server.UniversalSkipCertificateCheck)
{
$SkipCertificateCheck = [System.Convert]::ToBoolean($server.UniversalSkipCertificateCheck)
}

[PSCustomObject]@{
ServerUrl = $ServerUrl.TrimEnd('/')
AppToken = $AppToken
RepositoryName = $RepositoryName
RepositoryUrl = $RepositoryUrl
RepositoryAutoRemove = $RepositoryAutoRemove
Unpinned = $Unpinned
SkipCertificateCheck = $SkipCertificateCheck
}
}
Loading
Loading