287 lines
14 KiB
PowerShell
287 lines
14 KiB
PowerShell
#Requires -Version 5.1
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[ValidatePattern('^\d+\.\d+\.\d+([-.][0-9A-Za-z.-]+)?$')]
|
|
[string]$Version,
|
|
[string]$OutputRoot = (Join-Path $PSScriptRoot '..\artifacts\releases'),
|
|
[string]$ServerContentPath,
|
|
[switch]$SkipBuild
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$repositoryRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
|
|
$resolvedOutputRoot = [IO.Path]::GetFullPath($OutputRoot)
|
|
if (-not $resolvedOutputRoot.StartsWith($repositoryRoot + '\', [StringComparison]::OrdinalIgnoreCase)) {
|
|
throw 'OutputRoot must be beneath the repository root.'
|
|
}
|
|
|
|
function Copy-RequiredFile {
|
|
param(
|
|
[Parameter(Mandatory)][string]$Source,
|
|
[Parameter(Mandatory)][string]$Destination
|
|
)
|
|
|
|
if (-not (Test-Path -LiteralPath $Source -PathType Leaf)) {
|
|
throw "Required package input is missing: $Source"
|
|
}
|
|
New-Item -ItemType Directory -Path (Split-Path $Destination -Parent) -Force | Out-Null
|
|
Copy-Item -LiteralPath $Source -Destination $Destination -Force
|
|
}
|
|
|
|
function Write-PackageManifest {
|
|
param(
|
|
[Parameter(Mandatory)][string]$PackageRoot,
|
|
[Parameter(Mandatory)][string]$PackageVersion,
|
|
[Parameter(Mandatory)][string]$PackageKind,
|
|
[ValidateSet('Auto')]
|
|
[string]$CompatibilityProfile,
|
|
[string]$TargetOperatingSystem
|
|
)
|
|
|
|
$resolvedPackageRoot = (Resolve-Path -LiteralPath $PackageRoot).Path.TrimEnd('\')
|
|
$files = @(Get-ChildItem -LiteralPath $resolvedPackageRoot -Recurse -File |
|
|
Where-Object Name -ne 'package-manifest.json' |
|
|
Sort-Object FullName |
|
|
ForEach-Object {
|
|
[ordered]@{
|
|
Path = $_.FullName.Substring($resolvedPackageRoot.Length).TrimStart('\')
|
|
Sha256 = (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash
|
|
Length = $_.Length
|
|
}
|
|
})
|
|
$manifest = [ordered]@{
|
|
SchemaVersion = 2
|
|
Product = 'SGU Credential Provider'
|
|
PackageKind = $PackageKind
|
|
Version = $PackageVersion
|
|
CreatedAt = (Get-Date).ToUniversalTime().ToString('o')
|
|
Files = $files
|
|
}
|
|
if ($CompatibilityProfile) {
|
|
$manifest['CompatibilityProfile'] = $CompatibilityProfile
|
|
}
|
|
if ($TargetOperatingSystem) {
|
|
$manifest['TargetOperatingSystem'] = $TargetOperatingSystem
|
|
}
|
|
[IO.File]::WriteAllText(
|
|
(Join-Path $resolvedPackageRoot 'package-manifest.json'),
|
|
($manifest | ConvertTo-Json -Depth 6),
|
|
[Text.UTF8Encoding]::new($false))
|
|
}
|
|
|
|
if (-not $SkipBuild) {
|
|
& (Join-Path $PSScriptRoot 'Publish-Lab.ps1') -Configuration Release `
|
|
-OutputRoot (Join-Path $repositoryRoot 'artifacts') | Out-Null
|
|
}
|
|
|
|
$brokerOutput = Join-Path $repositoryRoot 'artifacts\broker'
|
|
$providerOutput = Join-Path $repositoryRoot 'artifacts\credential-provider'
|
|
$prerequisiteRoot = Join-Path $repositoryRoot 'artifacts\prerequisites'
|
|
$runtimeInstaller = Get-ChildItem -LiteralPath $prerequisiteRoot -Filter '*x64*.exe' `
|
|
-File -ErrorAction SilentlyContinue |
|
|
Sort-Object Name -Descending |
|
|
Select-Object -First 1
|
|
if (-not $runtimeInstaller) {
|
|
throw 'Place the offline Microsoft .NET 10 x64 runtime installer in artifacts\prerequisites.'
|
|
}
|
|
|
|
New-Item -ItemType Directory -Path $resolvedOutputRoot -Force | Out-Null
|
|
$clientRoot = Join-Path $resolvedOutputRoot "sgu-windows-client-bootstrap-$Version"
|
|
$serverRoot = Join-Path $resolvedOutputRoot "sgu-server-bootstrap-$Version"
|
|
$linuxClientRoot = Join-Path $resolvedOutputRoot "sgu-linux-client-bootstrap-$Version"
|
|
$azureRoot = Join-Path $resolvedOutputRoot "sgu-azure-infrastructure-$Version"
|
|
$clientZip = "$clientRoot.zip"
|
|
$serverZip = "$serverRoot.zip"
|
|
$linuxClientZip = "$linuxClientRoot.zip"
|
|
$azureZip = "$azureRoot.zip"
|
|
foreach ($target in @(
|
|
$clientRoot,$serverRoot,$linuxClientRoot,$azureRoot,
|
|
$clientZip,$serverZip,$linuxClientZip,$azureZip)) {
|
|
if (Test-Path -LiteralPath $target) {
|
|
throw "Release target already exists: $target"
|
|
}
|
|
}
|
|
|
|
New-Item -ItemType Directory `
|
|
-Path $clientRoot,$serverRoot,$linuxClientRoot,$azureRoot `
|
|
-Force | Out-Null
|
|
$welcomeFontNames = @(
|
|
'IndivisaTextSans-Regular.otf',
|
|
'IndivisaTextSans-Bold.otf',
|
|
'IndivisaTextSans-BoldItalic.otf',
|
|
'IndivisaTextSerif-Regular.otf',
|
|
'IndivisaTextSerif-BoldItalic.otf'
|
|
)
|
|
|
|
Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Invoke-SguClientBootstrap.ps1') `
|
|
-Destination (Join-Path $clientRoot 'Invoke-SguClientBootstrap.ps1')
|
|
Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Start-SguClientEnrollment.cmd') `
|
|
-Destination (Join-Path $clientRoot 'Start-SguClientEnrollment.cmd')
|
|
$clientScripts = @(
|
|
'Enable-LabRemoteAccess.ps1',
|
|
'Enable-SguClientMonitoring.ps1',
|
|
'Enroll-SguDomainClient.ps1',
|
|
'Install-CredentialProvider.ps1',
|
|
'Install-SguEnrollmentGuard.ps1',
|
|
'Install-SguRustDeskClient.ps1',
|
|
'Register-SguClientCertificate.ps1',
|
|
'Repair-SguClientEnrollment.ps1',
|
|
'Set-SguStandardLocalUser.ps1',
|
|
'Test-SguClientEnrollment.ps1'
|
|
)
|
|
foreach ($scriptName in $clientScripts) {
|
|
Copy-RequiredFile -Source (Join-Path $PSScriptRoot $scriptName) `
|
|
-Destination (Join-Path $clientRoot "payload\scripts\$scriptName")
|
|
}
|
|
Copy-Item -Path (Join-Path $providerOutput '*') `
|
|
-Destination (New-Item -ItemType Directory `
|
|
-Path (Join-Path $clientRoot 'payload\credential-provider') -Force).FullName `
|
|
-Recurse -Force
|
|
Copy-RequiredFile -Source (Join-Path $repositoryRoot 'assets\branding\lasalle-mascot-account.png') `
|
|
-Destination (Join-Path $clientRoot 'payload\credential-provider\branding\user.png')
|
|
Copy-RequiredFile -Source (Join-Path $repositoryRoot 'assets\branding\darkblue.jpg') `
|
|
-Destination (Join-Path $clientRoot 'payload\credential-provider\branding\darkblue.jpg')
|
|
Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Set-SguWelcomeWallpaper.ps1') `
|
|
-Destination (Join-Path $clientRoot 'payload\credential-provider\branding\Set-SguWelcomeWallpaper.ps1')
|
|
foreach ($fontName in $welcomeFontNames) {
|
|
Copy-RequiredFile -Source (Join-Path $repositoryRoot "assets\branding\fonts\$fontName") `
|
|
-Destination (Join-Path $clientRoot "payload\credential-provider\branding\fonts\$fontName")
|
|
}
|
|
Copy-RequiredFile -Source $runtimeInstaller.FullName `
|
|
-Destination (Join-Path $clientRoot "payload\prerequisites\$($runtimeInstaller.Name)")
|
|
|
|
# One Windows implementation supports existing LAN/VPN routes and optional Azure P2S.
|
|
Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Start-SguAzureClientEnrollment.cmd') `
|
|
-Destination (Join-Path $clientRoot 'Start-SguAzureClientEnrollment.cmd')
|
|
Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Install-SguAzureP2sClient.ps1') `
|
|
-Destination (Join-Path $clientRoot 'Install-SguAzureP2sClient.ps1')
|
|
Copy-RequiredFile -Source (Join-Path $repositoryRoot 'docs\client-enrollment.md') `
|
|
-Destination (Join-Path $clientRoot 'README.md')
|
|
Copy-Item -Path (Join-Path $repositoryRoot 'docs\*.md') -Destination $clientRoot
|
|
|
|
Write-PackageManifest -PackageRoot $clientRoot -PackageVersion $Version `
|
|
-PackageKind WindowsClient -CompatibilityProfile Auto `
|
|
-TargetOperatingSystem 'Windows 10 1607+ or Windows 11; x64 Pro, Enterprise, or Education'
|
|
Compress-Archive -Path (Join-Path $clientRoot '*') -DestinationPath $clientZip `
|
|
-CompressionLevel Optimal
|
|
|
|
# Linux clients use their native PAM/SSSD sign-in stack rather than the Windows
|
|
# Credential Provider. Keep their self-contained bootstrap independent so a
|
|
# Linux administrator never receives Windows binaries or certificate material.
|
|
Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Enroll-SguLinuxDomainClient.sh') `
|
|
-Destination (Join-Path $linuxClientRoot 'Enroll-SguLinuxDomainClient.sh')
|
|
Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Install-SguLinuxRustDeskClient.sh') `
|
|
-Destination (Join-Path $linuxClientRoot 'Install-SguLinuxRustDeskClient.sh')
|
|
Copy-RequiredFile -Source (Join-Path $repositoryRoot 'docs\linux-client-enrollment.md') `
|
|
-Destination (Join-Path $linuxClientRoot 'README.md')
|
|
Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Set-SguWelcomeWallpaper.sh') `
|
|
-Destination (Join-Path $linuxClientRoot 'welcome-wallpaper\Set-SguWelcomeWallpaper.sh')
|
|
Copy-RequiredFile -Source (Join-Path $repositoryRoot 'assets\branding\darkblue.jpg') `
|
|
-Destination (Join-Path $linuxClientRoot 'welcome-wallpaper\darkblue.jpg')
|
|
foreach ($fontName in $welcomeFontNames) {
|
|
Copy-RequiredFile -Source (Join-Path $repositoryRoot "assets\branding\fonts\$fontName") `
|
|
-Destination (Join-Path $linuxClientRoot "welcome-wallpaper\fonts\$fontName")
|
|
}
|
|
Write-PackageManifest -PackageRoot $linuxClientRoot -PackageVersion $Version -PackageKind LinuxClient
|
|
Compress-Archive -Path (Join-Path $linuxClientRoot '*') -DestinationPath $linuxClientZip `
|
|
-CompressionLevel Optimal
|
|
|
|
Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Initialize-SguDomainController.ps1') `
|
|
-Destination (Join-Path $serverRoot 'Initialize-SguDomainController.ps1')
|
|
Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Start-SguServerBootstrap.cmd') `
|
|
-Destination (Join-Path $serverRoot 'Start-SguServerBootstrap.cmd')
|
|
Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Start-SguAzureServerBootstrap.cmd') `
|
|
-Destination (Join-Path $serverRoot 'Start-SguAzureServerBootstrap.cmd')
|
|
$serverScripts = @(
|
|
'Deploy-AuthBroker.ps1',
|
|
'Enable-SguServerRemoteManagement.ps1',
|
|
'Get-SguUsageReport.ps1',
|
|
'Get-SguBrokerLog.ps1',
|
|
'Install-SguDomainMonitoring.ps1',
|
|
'Install-SguRustDeskClient.ps1',
|
|
'Install-SguRustDeskLinuxEnrollment.ps1',
|
|
'Install-SguRustDeskServer.ps1',
|
|
'Invoke-SguRustDeskLinuxRegistrationProcessor.ps1',
|
|
'Invoke-SguMonitoringMaintenance.ps1',
|
|
'New-LabCertificate.ps1',
|
|
'Get-SguRustDeskDevice.ps1',
|
|
'Register-SguClientCertificate.ps1',
|
|
'Register-SguRustDeskDevice.ps1',
|
|
'Set-LabBrokerDns.ps1',
|
|
'Set-SguDomainComputerPolicies.ps1',
|
|
'Set-SguDomainUserPolicies.ps1'
|
|
)
|
|
foreach ($scriptName in $serverScripts) {
|
|
Copy-RequiredFile -Source (Join-Path $PSScriptRoot $scriptName) `
|
|
-Destination (Join-Path $serverRoot "payload\scripts\$scriptName")
|
|
}
|
|
Copy-Item -Path (Join-Path $brokerOutput '*') `
|
|
-Destination (New-Item -ItemType Directory `
|
|
-Path (Join-Path $serverRoot 'payload\broker') -Force).FullName `
|
|
-Recurse -Force
|
|
$serverContentTarget = Join-Path $serverRoot 'payload\server-content\Packages'
|
|
New-Item -ItemType Directory -Path $serverContentTarget -Force | Out-Null
|
|
if ($ServerContentPath) {
|
|
if (-not (Test-Path -LiteralPath $ServerContentPath -PathType Container)) {
|
|
throw 'ServerContentPath does not exist.'
|
|
}
|
|
Copy-Item -Path (Join-Path $ServerContentPath '*') `
|
|
-Destination $serverContentTarget -Recurse -Force
|
|
}
|
|
Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Set-SguWelcomeWallpaper.ps1') `
|
|
-Destination (Join-Path $serverContentTarget 'welcome-wallpaper\Set-SguWelcomeWallpaper.ps1')
|
|
Copy-RequiredFile -Source (Join-Path $repositoryRoot 'assets\branding\darkblue.jpg') `
|
|
-Destination (Join-Path $serverContentTarget 'welcome-wallpaper\darkblue.jpg')
|
|
foreach ($fontName in $welcomeFontNames) {
|
|
Copy-RequiredFile -Source (Join-Path $repositoryRoot "assets\branding\fonts\$fontName") `
|
|
-Destination (Join-Path $serverContentTarget "welcome-wallpaper\fonts\$fontName")
|
|
}
|
|
Write-PackageManifest -PackageRoot $serverRoot -PackageVersion $Version -PackageKind Server
|
|
Compress-Archive -Path (Join-Path $serverRoot '*') -DestinationPath $serverZip `
|
|
-CompressionLevel Optimal
|
|
|
|
# Azure infrastructure is packaged separately because it runs on the trusted
|
|
# administrator workstation, not inside the domain controller or a client.
|
|
$azureScriptsRoot = Join-Path $azureRoot 'scripts'
|
|
$azureInfrastructureRoot = Join-Path $azureRoot 'infra\azure'
|
|
New-Item -ItemType Directory -Path $azureScriptsRoot,$azureInfrastructureRoot -Force | Out-Null
|
|
Copy-RequiredFile -Source (Join-Path $repositoryRoot 'infra\azure\main.bicep') `
|
|
-Destination (Join-Path $azureInfrastructureRoot 'main.bicep')
|
|
foreach ($scriptName in @(
|
|
'New-SguAzureP2sCertificates.ps1',
|
|
'Deploy-SguAzureInfrastructure.ps1',
|
|
'Get-SguAzureP2sPackage.ps1',
|
|
'Install-SguAzureP2sClient.ps1')) {
|
|
Copy-RequiredFile -Source (Join-Path $PSScriptRoot $scriptName) `
|
|
-Destination (Join-Path $azureScriptsRoot $scriptName)
|
|
}
|
|
Copy-RequiredFile -Source (Join-Path $repositoryRoot 'docs\azure-vpn-deployment.md') `
|
|
-Destination (Join-Path $azureRoot 'README.md')
|
|
Write-PackageManifest -PackageRoot $azureRoot -PackageVersion $Version -PackageKind AzureInfrastructure
|
|
Compress-Archive -Path (Join-Path $azureRoot '*') -DestinationPath $azureZip `
|
|
-CompressionLevel Optimal
|
|
|
|
$checksums = @(
|
|
("{0} {1}" -f (Get-FileHash -LiteralPath $clientZip -Algorithm SHA256).Hash, (Split-Path $clientZip -Leaf))
|
|
("{0} {1}" -f (Get-FileHash -LiteralPath $serverZip -Algorithm SHA256).Hash, (Split-Path $serverZip -Leaf))
|
|
("{0} {1}" -f (Get-FileHash -LiteralPath $linuxClientZip -Algorithm SHA256).Hash, (Split-Path $linuxClientZip -Leaf))
|
|
("{0} {1}" -f (Get-FileHash -LiteralPath $azureZip -Algorithm SHA256).Hash, (Split-Path $azureZip -Leaf))
|
|
)
|
|
$checksumsPath = Join-Path $resolvedOutputRoot "SHA256SUMS-$Version.txt"
|
|
[IO.File]::WriteAllLines($checksumsPath, $checksums, [Text.UTF8Encoding]::new($false))
|
|
|
|
[pscustomobject]@{
|
|
Version = $Version
|
|
WindowsClientPackage = $clientZip
|
|
WindowsClientSha256 = (Get-FileHash -LiteralPath $clientZip -Algorithm SHA256).Hash
|
|
LinuxClientPackage = $linuxClientZip
|
|
LinuxClientSha256 = (Get-FileHash -LiteralPath $linuxClientZip -Algorithm SHA256).Hash
|
|
ServerPackage = $serverZip
|
|
ServerSha256 = (Get-FileHash -LiteralPath $serverZip -Algorithm SHA256).Hash
|
|
AzureInfrastructurePackage = $azureZip
|
|
AzureInfrastructureSha256 = (Get-FileHash -LiteralPath $azureZip -Algorithm SHA256).Hash
|
|
Checksums = $checksumsPath
|
|
RuntimeInstaller = $runtimeInstaller.Name
|
|
}
|