Skip to content

Build Harden System Security App for Store #16

Build Harden System Security App for Store

Build Harden System Security App for Store #16

name: Build Harden System Security App for Store
on:
workflow_dispatch: # Only the repository's owner can initiate this workflow
# Prevent the workflow from running more than once per change
concurrency:
group: HSSBuildWorkFlow
cancel-in-progress: true
jobs:
build:
runs-on: windows-2025
# Create outputs for this job where only other job that use "needs" keyword can use them. This job itself cannot access them.
outputs:
DRAFT_RELEASE_ID: ${{ steps.find_draft_release.outputs.DRAFT_RELEASE_ID }}
DRAFT_RELEASE_TAG: ${{ steps.find_draft_release.outputs.DRAFT_RELEASE_TAG }}
PACKAGE_VERSION: ${{ steps.main_buildOp.outputs.PACKAGE_VERSION }}
MSIXBundle_PATH: ${{ steps.main_buildOp.outputs.MSIXBundle_PATH }}
MSIXBundle_NAME: ${{ steps.main_buildOp.outputs.MSIXBundle_NAME }}
X64MSBuildLog_PATH: ${{ steps.main_buildOp.outputs.X64MSBuildLog_PATH }}
ARM64MSBuildLog_PATH: ${{ steps.main_buildOp.outputs.ARM64MSBuildLog_PATH }}
X64Symbol_PATH: ${{ steps.main_buildOp.outputs.X64Symbol_PATH }}
X64Symbol_NAME: ${{ steps.main_buildOp.outputs.X64Symbol_NAME }}
ARM64Symbol_PATH: ${{ steps.main_buildOp.outputs.ARM64Symbol_PATH }}
ARM64Symbol_NAME: ${{ steps.main_buildOp.outputs.ARM64Symbol_NAME }}
SBOM_PATH: ${{ steps.main_buildOp.outputs.SBOM_PATH }}
SBOM_NAME: ${{ steps.main_buildOp.outputs.SBOM_NAME }}
permissions:
contents: write # Required for adding files to the GitHub draft release
steps:
# - name: Updating Winget
# shell: pwsh
# run: |
# try {
# Write-Host -Object 'The version of the pre-installed Winget on the Runner:'
# Write-Host -Object (winget --version)
# }
# catch {
# Write-Host -Object 'Winget is not installed.'
# }
#
# # Retrieve the latest Winget release information
# $WingetReleases = Invoke-RestMethod -Uri 'https://api.github.com/repos/microsoft/winget-cli/releases'
# $LatestRelease = $WingetReleases | Select-Object -First 1
# # Direct links to the latest Winget release assets
# [string]$WingetURL = $LatestRelease.assets.browser_download_url | Where-Object -FilterScript { $_.EndsWith('.msixbundle') } | Select-Object -First 1
# [string]$WingetLicense = $LatestRelease.assets.browser_download_url | Where-Object -FilterScript { $_.EndsWith('License1.xml') } | Select-Object -First 1
# [string]$LatestWingetReleaseDependenciesZipURL = $LatestRelease.assets.browser_download_url | Where-Object -FilterScript { $_.EndsWith('DesktopAppInstaller_Dependencies.zip') } | Select-Object -First 1
# [hashtable]$Downloads = @{
# # 'Winget.msixbundle' = 'https://aka.ms/getwinget' This is updated slower than the GitHub release
# 'DesktopAppInstaller_Dependencies.zip' = $LatestWingetReleaseDependenciesZipURL
# 'Winget.msixbundle' = $WingetURL
# 'License1.xml' = $WingetLicense
# }
# $Downloads.GetEnumerator() | ForEach-Object -Parallel {
# Invoke-RestMethod -Uri $_.Value -OutFile $_.Key
# }
#
# Expand-Archive -Path 'DesktopAppInstaller_Dependencies.zip' -DestinationPath .\ -Force
# # Get the paths to all of the dependencies
# [string[]]$DependencyPaths = (Get-ChildItem -Path .\x64 -Filter '*.appx' -File -Force).FullName
#
# # Required to update the Winget
# Stop-Process -Name 'WindowsTerminal' -Force -ErrorAction Ignore
#
# Add-AppxProvisionedPackage -Online -PackagePath 'Winget.msixbundle' -DependencyPackagePath $DependencyPaths -LicensePath 'License1.xml'
#
# Add-AppPackage -Path 'Winget.msixbundle' -DependencyPath "$($DependencyPaths[0])", "$($DependencyPaths[1])" -ForceTargetApplicationShutdown -ForceUpdateFromAnyVersion
- name: Installing the necessary programs # Builds the Harden System Security application securely with 0 usage of 3rd party tools, workflows and such.
shell: pwsh
run: |
Write-Host -Object "The version of the Winget currently in use:"
Write-Host -Object (winget --version)
winget source update
Write-Host -Object "`nInstalling Rust toolchain" -ForegroundColor Magenta
$null = winget install --id Rustlang.Rustup --exact --accept-package-agreements --accept-source-agreements --uninstall-previous --force --source winget
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed to install the Rust toolchain: $LASTEXITCODE") }
Write-Host -Object "`nInstalling .NET SDK" -ForegroundColor Magenta
$null = winget install --id Microsoft.DotNet.SDK.Preview --exact --accept-package-agreements --accept-source-agreements --uninstall-previous --force --source winget
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed to install .NET SDK: $LASTEXITCODE") }
Write-Host -Object "`nInstalling Visual Studio Build Tools" -ForegroundColor Magenta
$null = winget install --id Microsoft.VisualStudio.2022.BuildTools --exact --accept-package-agreements --accept-source-agreements --uninstall-previous --force --source winget --override '--force --wait --passive --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Workload.MSBuildTools --add Microsoft.VisualStudio.Workload.UniversalBuildTools --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows11SDK.26100 --includeRecommended --add Microsoft.VisualStudio.Component.VC.Tools.ARM64'
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed to install Visual Studio Build Tools: $LASTEXITCODE") }
Write-Host -Object "`nInstalling Visual C++ Redistributable" -ForegroundColor Magenta
$null = winget install --id Microsoft.VCRedist.2015+.x64 --exact --accept-package-agreements --accept-source-agreements --uninstall-previous --force --source winget
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed to install Microsoft.VCRedist: $LASTEXITCODE") }
- name: Check out the repository code
uses: actions/checkout@v5
- name: Building And Packaging the Harden System Security
id: main_buildOp
shell: pwsh
env:
PARTNERCENTER_APPLICATIONID_HSS: ${{ secrets.PARTNERCENTER_APPLICATIONID_HSS }}
PARTNERCENTER_CLIENTID: ${{ secrets.PARTNERCENTER_CLIENTID }}
PARTNERCENTER_CLIENTSECRET: ${{ secrets.PARTNERCENTER_CLIENTSECRET }}
PARTNERCENTER_TOKENENDPOINT: ${{ secrets.PARTNERCENTER_TOKENENDPOINT }}
working-directory: './Harden System Security' # Setting up working directory to ensure dotnet build will see the global.json file in the "Harden System Security" sub-directory
run: |
# Requires -Version 7.5
# Requires -RunAsAdministrator
function Build_HSS {
param(
[bool]$DownloadRepo,
[bool]$InstallDeps,
[bool]$Workflow,
[bool]$UpdateWorkLoads,
[bool]$Upload
)
$ErrorActionPreference = 'Stop'
$Stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name 'LongPathsEnabled' -Value 1 -Force
[System.String]$script:AppControlManagerDirectory
if ($DownloadRepo) {
[System.String]$BranchName = 'main'
[System.String]$RepoName = 'Harden-Windows-Security'
[System.String]$RepoUrl = "https://github.com/HotCakeX/$RepoName/archive/refs/heads/$BranchName.zip"
[System.String]$ZipPath = [System.IO.Path]::Combine($env:TEMP, "$RepoName.zip")
[System.String]$InitialWorkingDirectory = $PWD.Path
$script:AppControlManagerDirectory = [System.IO.Path]::Combine($InitialWorkingDirectory, "$RepoName-$BranchName", 'Harden System Security')
if (Test-Path -Path $script:AppControlManagerDirectory -PathType Container) {
Remove-Item -Path $script:AppControlManagerDirectory -Recurse -Force
}
Invoke-WebRequest -Uri $RepoUrl -OutFile $ZipPath
Expand-Archive -Path $ZipPath -DestinationPath $InitialWorkingDirectory -Force
Remove-Item -Path $ZipPath -Force
Set-Location -Path $script:AppControlManagerDirectory
}
else {
$script:AppControlManagerDirectory = $PWD.Path
}
if ($InstallDeps) {
# Install Winget if it doesn't exist
if (!(Get-Command -Name 'winget.exe' -ErrorAction Ignore)) {
# Retrieve the latest Winget release information
$WingetReleases = Invoke-RestMethod -Uri 'https://api.github.com/repos/microsoft/winget-cli/releases'
$LatestRelease = $WingetReleases | Select-Object -First 1
# Direct links to the latest Winget release assets
[string]$WingetURL = $LatestRelease.assets.browser_download_url | Where-Object -FilterScript { $_.EndsWith('.msixbundle') } | Select-Object -First 1
[string]$WingetLicense = $LatestRelease.assets.browser_download_url | Where-Object -FilterScript { $_.EndsWith('License1.xml') } | Select-Object -First 1
[string]$LatestWingetReleaseDependenciesZipURL = $LatestRelease.assets.browser_download_url | Where-Object -FilterScript { $_.EndsWith('DesktopAppInstaller_Dependencies.zip') } | Select-Object -First 1
[hashtable]$Downloads = @{
# 'Winget.msixbundle' = 'https://aka.ms/getwinget' This is updated slower than the GitHub release
'DesktopAppInstaller_Dependencies.zip' = $LatestWingetReleaseDependenciesZipURL
'Winget.msixbundle' = $WingetURL
'License1.xml' = $WingetLicense
}
$Downloads.GetEnumerator() | ForEach-Object -Parallel {
Invoke-RestMethod -Uri $_.Value -OutFile $_.Key
}
Expand-Archive -Path 'DesktopAppInstaller_Dependencies.zip' -DestinationPath .\ -Force
# Required to update the Winget
Stop-Process -Name 'WindowsTerminal' -Force -ErrorAction Ignore
# Get the paths to all of the dependencies
[string[]]$DependencyPaths = (Get-ChildItem -Path .\x64 -Filter '*.appx' -File -Force).FullName
Add-AppxProvisionedPackage -Online -PackagePath 'Winget.msixbundle' -DependencyPackagePath $DependencyPaths -LicensePath 'License1.xml'
Add-AppPackage -Path 'Winget.msixbundle' -DependencyPath "$($DependencyPaths[0])", "$($DependencyPaths[1])" -ForceTargetApplicationShutdown -ForceUpdateFromAnyVersion
}
Write-Host -Object 'The version of the Winget currently in use:'
Write-Host -Object (winget --version)
winget source update
Write-Host -Object "`nInstalling Rust toolchain" -ForegroundColor Magenta
$null = winget install --id Rustlang.Rustup --exact --accept-package-agreements --accept-source-agreements --uninstall-previous --force --source winget
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed to install the Rust toolchain: $LASTEXITCODE") }
Write-Host -Object "`nInstalling .NET SDK" -ForegroundColor Magenta
$null = winget install --id Microsoft.DotNet.SDK.Preview --exact --accept-package-agreements --accept-source-agreements --uninstall-previous --force --source winget
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed to install .NET SDK: $LASTEXITCODE") }
Write-Host -Object "`nInstalling Visual Studio Build Tools" -ForegroundColor Magenta
# Downloads the online installer and automatically runs it and installs the build tools
# https://learn.microsoft.com/windows/apps/windows-app-sdk/set-up-your-development-environment
# https://learn.microsoft.com/visualstudio/install/workload-component-id-vs-build-tools
# https://learn.microsoft.com/visualstudio/install/use-command-line-parameters-to-install-visual-studio
# https://learn.microsoft.com/visualstudio/install/workload-component-id-vs-community
winget install --id Microsoft.VisualStudio.2022.BuildTools --exact --accept-package-agreements --accept-source-agreements --uninstall-previous --force --source winget --override '--force --wait --passive --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Workload.MSBuildTools --add Microsoft.VisualStudio.Workload.UniversalBuildTools --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows11SDK.26100 --includeRecommended --add Microsoft.VisualStudio.Component.VC.Tools.ARM64'
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New('Failed to install Visual Studio Build Tools') }
winget install --id Microsoft.VCRedist.2015+.x64 --exact --accept-package-agreements --accept-source-agreements --uninstall-previous --force --source winget
}
# Refresh the environment variables so the current session detects the new dotnet installation
$Env:Path = [System.Environment]::GetEnvironmentVariable('Path', [System.EnvironmentVariableTarget]::Machine) + ';' +
[System.Environment]::GetEnvironmentVariable('Path', [System.EnvironmentVariableTarget]::User)
# https://github.com/Microsoft/vswhere/wiki/Start-Developer-Command-Prompt#using-powershell
$installationPath = . 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -prerelease -latest -property installationPath
if ($installationPath -and (Test-Path -Path "$installationPath\Common7\Tools\vsdevcmd.bat" -PathType Leaf)) {
& "${env:COMSPEC}" /s /c "`"$installationPath\Common7\Tools\vsdevcmd.bat`" -no_logo && set" | ForEach-Object -Process {
$name, $value = $_ -split '=', 2
Set-Content -Path env:\"$name" -Value $value -Force
Write-Host -Object "Setting environment variable: $name=$value"
}
}
# Remove any possible existing directories
Remove-Item -Path .\MSIXOutputX64 -Recurse -Force -ErrorAction Ignore
Remove-Item -Path .\MSIXOutputARM64 -Recurse -Force -ErrorAction Ignore
Remove-Item -Path .\MSIXBundleOutput -Recurse -Force -ErrorAction Ignore
Remove-Item -Path .\bin -Recurse -Force -ErrorAction Ignore
Remove-Item -Path .\obj -Recurse -Force -ErrorAction Ignore
if ($UpdateWorkLoads) {
# Update the workloads
dotnet workload update
dotnet workload config --update-mode workload-set
dotnet workload update
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed updating the workloads. Exit Code: $LASTEXITCODE") }
}
Write-Host -Object "`nChecking .NET info`n`n" -ForegroundColor Magenta
dotnet --info
Write-Host -Object "`nListing installed .NET SDKs`n`n" -ForegroundColor Magenta
dotnet --list-sdks
function Find-mspdbcmf {
# "-products *" is necessary to detect BuildTools too
[string]$VisualStudioPath = . 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -prerelease -latest -property resolvedInstallationPath -products *
[string]$BasePath = [System.IO.Path]::Combine($VisualStudioPath, 'VC', 'Tools', 'MSVC')
# Get all subdirectories under the base path
[System.String[]]$VersionDirs = [System.IO.Directory]::GetDirectories($BasePath)
# Initialize the highest version with a minimal version value.
[System.Version]$HighestVersion = [System.Version]::New('0.0.0.0')
[System.String]$HighestVersionFolder = $null
# Loop through each directory to find the highest version folder.
foreach ($Dir in $VersionDirs) {
# Extract the folder name
[System.String]$FolderName = [System.IO.Path]::GetFileName($Dir)
[System.Version]$CurrentVersion = $null
# Try parsing the folder name as a Version.
if ([System.Version]::TryParse($FolderName, [ref] $CurrentVersion)) {
# Compare versions
if ($CurrentVersion.CompareTo($HighestVersion) -gt 0) {
$HighestVersion = $CurrentVersion
$HighestVersionFolder = $FolderName
}
}
}
# If no valid version folder is found
if (!$HighestVersionFolder) {
throw [System.IO.DirectoryNotFoundException]::New("No valid version directories found in $BasePath")
}
# Combine the base path, the highest version folder, the architecture folder, and the file name.
[System.String]$mspdbcmfPath = [System.IO.Path]::Combine($BasePath, $HighestVersionFolder, 'bin', 'Hostx64', 'x64', 'mspdbcmf.exe')
if (![System.IO.File]::Exists($mspdbcmfPath)) {
throw [System.IO.FileNotFoundException]::New("mspdbcmf.exe not found at $mspdbcmfPath")
}
return $mspdbcmfPath
}
[string]$mspdbcmfPath = Find-mspdbcmf
function Find-MSBuild {
# "-products *" is necessary to detect BuildTools too
[string]$VisualStudioPath = . 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -prerelease -latest -property resolvedInstallationPath -products *
[string]$MSBuildPath = [System.IO.Path]::Combine($VisualStudioPath, 'MSBuild', 'Current', 'Bin', 'MSBuild.exe')
if (![System.IO.File]::Exists($MSBuildPath)) {
throw [System.IO.FileNotFoundException]::New("MSBuild.exe not found at $MSBuildPath")
}
return $MSBuildPath
}
[string]$MSBuildPath = Find-MSBuild
#region --- Compile C++ projects ---
### ComManager
. $MSBuildPath '..\AppControl Manager\eXclude\ComManager\ComManager.slnx' /p:Configuration=Release /p:Platform=x64 /target:"clean;Rebuild"
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed building MS Defender solution for X64. Exit Code: $LASTEXITCODE") }
. $MSBuildPath '..\AppControl Manager\eXclude\ComManager\ComManager.slnx' /p:Configuration=Release /p:Platform=arm64 /target:"clean;Rebuild"
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed building MS Defender solution for ARM64. Exit Code: $LASTEXITCODE") }
#region --- RUST projects ---
# Uncomment this once stable toolchain supports ehcont security feature, till then we use nightly only
# rustup default stable
rustup default nightly
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed setting Rust toolchain to Stable. Exit Code: $LASTEXITCODE") }
rustup target add aarch64-pc-windows-msvc
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed adding aarch64-pc-windows-msvc target to Rust toolchain. Exit Code: $LASTEXITCODE") }
rustup target add x86_64-pc-windows-msvc
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed adding x86_64-pc-windows-msvc target to Rust toolchain. Exit Code: $LASTEXITCODE") }
rustup update
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed updating Rust. Exit Code: $LASTEXITCODE") }
cargo version
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed checking for Rust version. Exit Code: $LASTEXITCODE") }
[string]$Current_Location = (Get-Location).Path
Set-Location -Path '..\AppControl Manager\eXclude\Rust Interop Library'
if (Test-Path -PathType Leaf -LiteralPath 'Cargo.lock') {
Remove-Item -Force -LiteralPath 'Cargo.lock'
}
rustup toolchain install nightly
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed installing nightly Rust toolchain. Exit Code: $LASTEXITCODE") }
rustup default nightly
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed setting Rust toolchain to Nightly. Exit Code: $LASTEXITCODE") }
rustup component add rust-src --toolchain nightly-x86_64-pc-windows-msvc
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed adding rust-src component to Nightly toolchain. Exit Code: $LASTEXITCODE") }
rustup update
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed updating Rust. Exit Code: $LASTEXITCODE") }
cargo version
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed checking for Rust version. Exit Code: $LASTEXITCODE") }
cargo clean
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed cleaning the Rust project. Exit Code: $LASTEXITCODE") }
cargo update --verbose
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed updating Rust. Exit Code: $LASTEXITCODE") }
cargo tree
rustup show active-toolchain
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed showing active Rust toolchain. Exit Code: $LASTEXITCODE") }
cargo build_x64
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed building x64 Rust Interop project. Exit Code: $LASTEXITCODE") }
cargo build_arm64
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed building ARM64 Rust Interop project. Exit Code: $LASTEXITCODE") }
Set-Location -Path $Current_Location
#endregion
#region --- C# projects ---
dotnet restore '..\AppControl Manager\eXclude\DISMService\DISMService.csproj' -r win-x64
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed restoring DISMService for x64. Exit Code: $LASTEXITCODE") }
dotnet restore '..\AppControl Manager\eXclude\DISMService\DISMService.csproj' -r win-arm64
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed restoring DISMService for ARM64. Exit Code: $LASTEXITCODE") }
dotnet clean '..\AppControl Manager\eXclude\DISMService\DISMService.slnx' --configuration Release
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed cleaning DISMService (first pass). Exit Code: $LASTEXITCODE") }
dotnet build '..\AppControl Manager\eXclude\DISMService\DISMService.slnx' --configuration Release --verbosity minimal
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed building DISMService (first pass). Exit Code: $LASTEXITCODE") }
dotnet msbuild '..\AppControl Manager\eXclude\DISMService\DISMService.slnx' /p:Configuration=Release /restore /p:Platform=x64 /p:PublishProfile="..\AppControl Manager\eXclude\DISMService\Properties\PublishProfiles\win-x64.pubxml" /t:Publish -v:minimal
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed publishing DISMService for x64. Exit Code: $LASTEXITCODE") }
dotnet clean '..\AppControl Manager\eXclude\DISMService\DISMService.slnx' --configuration Release
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed cleaning DISMService (second pass). Exit Code: $LASTEXITCODE") }
dotnet build '..\AppControl Manager\eXclude\DISMService\DISMService.slnx' --configuration Release --verbosity minimal
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed building DISMService (second pass). Exit Code: $LASTEXITCODE") }
dotnet msbuild '..\AppControl Manager\eXclude\DISMService\DISMService.slnx' /p:Configuration=Release /restore /p:Platform=arm64 /p:PublishProfile="..\AppControl Manager\eXclude\DISMService\Properties\PublishProfiles\win-arm64.pubxml" /t:Publish -v:minimal
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed publishing DISMService for ARM64. Exit Code: $LASTEXITCODE") }
#endregion
[string]$CsProjFilePath = (Resolve-Path -Path '.\Harden System Security.csproj').Path
# https://learn.microsoft.com/dotnet/core/tools/dotnet-build
# https://learn.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference
# https://learn.microsoft.com/visualstudio/msbuild/common-msbuild-project-properties
# Copy the X64 components to the directory before the build starts
Copy-Item -Path '..\AppControl Manager\eXclude\ComManager\x64\Release\ComManager.exe' -Destination '.\CppInterop\ComManager.exe' -Force
Copy-Item -Path '..\AppControl Manager\eXclude\DISMService\OutputX64\DISMService.exe' -Destination '.\DISMService.exe' -Force
# Generate for X64 architecture
dotnet clean 'Harden System Security.csproj' --configuration Release
dotnet build 'Harden System Security.csproj' --configuration Release --verbosity minimal /p:Platform=x64 /p:RuntimeIdentifier=win-x64
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed building x64 Harden System Security project. Exit Code: $LASTEXITCODE") }
dotnet msbuild 'Harden System Security.csproj' /t:Publish /p:Configuration=Release /p:RuntimeIdentifier=win-x64 /p:AppxPackageDir="MSIXOutputX64\" /p:GenerateAppxPackageOnBuild=true /p:Platform=x64 -v:minimal /p:MsPdbCmfExeFullpath=$mspdbcmfPath -bl:X64MSBuildLog.binlog
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed packaging x64 Harden System Security project. Exit Code: $LASTEXITCODE") }
# Copy the ARM64 components to the directory before the build starts
Copy-Item -Path '..\AppControl Manager\eXclude\ComManager\ARM64\Release\ComManager.exe' -Destination '.\CppInterop\ComManager.exe' -Force
Copy-Item -Path '..\AppControl Manager\eXclude\DISMService\OutputARM64\DISMService.exe' -Destination '.\DISMService.exe' -Force
# Generate for ARM64 architecture
dotnet clean 'Harden System Security.csproj' --configuration Release
dotnet build 'Harden System Security.csproj' --configuration Release --verbosity minimal /p:Platform=ARM64 /p:RuntimeIdentifier=win-arm64
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed building ARM64 Harden System Security project. Exit Code: $LASTEXITCODE") }
dotnet msbuild 'Harden System Security.csproj' /t:Publish /p:Configuration=Release /p:RuntimeIdentifier=win-arm64 /p:AppxPackageDir="MSIXOutputARM64\" /p:GenerateAppxPackageOnBuild=true /p:Platform=ARM64 -v:minimal /p:MsPdbCmfExeFullpath=$mspdbcmfPath -bl:ARM64MSBuildLog.binlog
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("Failed packaging ARM64 Harden System Security project. Exit Code: $LASTEXITCODE") }
function Get-MSIXFile {
param(
[System.String]$BasePath,
[System.String]$FolderPattern,
[System.String]$FileNamePattern,
[System.String]$ErrorMessageFolder,
[System.String]$ErrorMessageFile
)
# Get all subdirectories in the base path matching the folder pattern
[System.String[]]$Folders = [System.IO.Directory]::GetDirectories($BasePath)
[System.String]$DetectedFolder = $null
foreach ($Folder in $Folders) {
if ([System.Text.RegularExpressions.Regex]::IsMatch($Folder, $FolderPattern)) {
$DetectedFolder = $Folder
break
}
}
if (!$DetectedFolder) {
throw [System.InvalidOperationException]::New($ErrorMessageFolder)
}
# Get the full path of the first file matching the file name pattern inside the found folder
[System.String[]]$Files = [System.IO.Directory]::GetFiles($DetectedFolder)
[System.String]$DetectedFile = $null
foreach ($File in $Files) {
if ([System.Text.RegularExpressions.Regex]::IsMatch($File, $FileNamePattern)) {
$DetectedFile = $File
break
}
}
if (!$DetectedFile) {
throw [System.InvalidOperationException]::New($ErrorMessageFile)
}
return $DetectedFile
}
#region Finding X64 outputs
[System.String]$FinalMSIXX64Path = Get-MSIXFile -BasePath ([System.IO.Path]::Combine($PWD.Path, 'MSIXOutputX64')) -FolderPattern 'Harden System Security_\d+\.\d+\.\d+\.\d+_x64_Test' -FileNamePattern 'Harden System Security_\d+\.\d+\.\d+\.\d+_x64\.msix' -ErrorMessageFolder 'Could not find the directory for X64 MSIX file' -ErrorMessageFile 'Could not find the X64 MSIX file'
[System.String]$FinalMSIXX64Name = [System.IO.Path]::GetFileName($FinalMSIXX64Path)
[System.String]$FinalMSIXX64SymbolPath = Get-MSIXFile -BasePath ([System.IO.Path]::Combine($PWD.Path, 'MSIXOutputX64')) -FolderPattern 'Harden System Security_\d+\.\d+\.\d+\.\d+_x64_Test' -FileNamePattern 'Harden System Security_\d+\.\d+\.\d+\.\d+_x64\.appxsym' -ErrorMessageFolder 'Could not find the directory for X64 symbol file' -ErrorMessageFile 'Could not find the X64 symbol file'
[System.String]$FinalMSIXX64SymbolName = [System.IO.Path]::GetFileName($FinalMSIXX64SymbolPath)
#endregion
#region Finding ARM64 outputs
[System.String]$FinalMSIXARM64Path = Get-MSIXFile -BasePath ([System.IO.Path]::Combine($PWD.Path, 'MSIXOutputARM64')) -FolderPattern 'Harden System Security_\d+\.\d+\.\d+\.\d+_arm64_Test' -FileNamePattern 'Harden System Security_\d+\.\d+\.\d+\.\d+_arm64\.msix' -ErrorMessageFolder 'Could not find the directory for ARM64 MSIX file' -ErrorMessageFile 'Could not find the ARM64 MSIX file'
[System.String]$FinalMSIXARM64Name = [System.IO.Path]::GetFileName($FinalMSIXARM64Path)
[System.String]$FinalMSIXARM64SymbolPath = Get-MSIXFile -BasePath ([System.IO.Path]::Combine($PWD.Path, 'MSIXOutputARM64')) -FolderPattern 'Harden System Security_\d+\.\d+\.\d+\.\d+_arm64_Test' -FileNamePattern 'Harden System Security_\d+\.\d+\.\d+\.\d+_arm64\.appxsym' -ErrorMessageFolder 'Could not find the directory for ARM64 symbol file' -ErrorMessageFile 'Could not find the ARM64 symbol file'
[System.String]$FinalMSIXARM64SymbolName = [System.IO.Path]::GetFileName($FinalMSIXARM64SymbolPath)
#endregion
#region Detect and Validate File Versions
[System.Text.RegularExpressions.Regex]$versionRegexX64 = [System.Text.RegularExpressions.Regex]::New('Harden System Security_(\d+\.\d+\.\d+\.\d+)_x64\.msix')
[System.Text.RegularExpressions.Regex]$versionRegexARM64 = [System.Text.RegularExpressions.Regex]::New('Harden System Security_(\d+\.\d+\.\d+\.\d+)_arm64\.msix')
[System.Text.RegularExpressions.Match]$MatchX64 = $versionRegexX64.Match($FinalMSIXX64Name)
[System.Text.RegularExpressions.Match]$MatchARM64 = $versionRegexARM64.Match($FinalMSIXARM64Name)
if (!$MatchX64.Success) {
throw [System.InvalidOperationException]::New('Could not detect version from X64 file name')
}
if (!$MatchARM64.Success) {
throw [System.InvalidOperationException]::New('Could not detect version from ARM64 file name')
}
[System.String]$versionX64 = $MatchX64.Groups[1].Value
[System.String]$versionARM64 = $MatchARM64.Groups[1].Value
if ($versionX64 -ne $versionARM64) {
throw [System.InvalidOperationException]::New('The versions in X64 and ARM64 files do not match')
}
# Craft the file name for the MSIX Bundle file
[System.String]$FinalBundleFileName = "Harden System Security_$versionX64.msixbundle"
#endregion
# Creating the directory where the MSIX packages will be copied to
[System.String]$MSIXBundleOutput = [System.IO.Directory]::CreateDirectory([System.IO.Path]::Combine($script:AppControlManagerDirectory, 'MSIXBundleOutput')).FullName
[System.IO.File]::Copy($FinalMSIXX64Path, [System.IO.Path]::Combine($MSIXBundleOutput, $FinalMSIXX64Name), $true)
[System.IO.File]::Copy($FinalMSIXARM64Path, [System.IO.Path]::Combine($MSIXBundleOutput, $FinalMSIXARM64Name), $true)
# The path to the final MSIX Bundle file
[System.String]$MSIXBundle = [System.IO.Path]::Combine($MSIXBundleOutput, $FinalBundleFileName)
function Get-MakeAppxPath {
[System.String]$BasePath = 'C:\Program Files (x86)\Windows Kits\10\bin'
# Get all subdirectories under the base path
[System.String[]]$VersionDirs = [System.IO.Directory]::GetDirectories($BasePath)
# Initialize the highest version with a minimal version value.
[System.Version]$HighestVersion = [System.Version]::New('0.0.0.0')
[System.String]$HighestVersionFolder = $null
# Loop through each directory to find the highest version folder.
foreach ($Dir in $VersionDirs) {
# Extract the folder name
[System.String]$FolderName = [System.IO.Path]::GetFileName($Dir)
[System.Version]$CurrentVersion = $null
# Try parsing the folder name as a Version.
if ([System.Version]::TryParse($FolderName, [ref] $CurrentVersion)) {
# Compare versions
if ($CurrentVersion.CompareTo($HighestVersion) -gt 0) {
$HighestVersion = $CurrentVersion
$HighestVersionFolder = $FolderName
}
}
}
# If no valid version folder is found
if (!$HighestVersionFolder) {
throw [System.IO.DirectoryNotFoundException]::New("No valid version directories found in $BasePath")
}
[string]$CPUArch = @{AMD64 = 'x64'; ARM64 = 'arm64' }[$Env:PROCESSOR_ARCHITECTURE]
if ([System.String]::IsNullOrWhiteSpace($CPUArch)) { throw [System.PlatformNotSupportedException]::New('Only AMD64 and ARM64 architectures are supported.') }
# Combine the base path, the highest version folder, the architecture folder, and the file name.
[System.String]$MakeAppxPath = [System.IO.Path]::Combine($BasePath, $HighestVersionFolder, $CPUArch, 'makeappx.exe')
return $MakeAppxPath
}
[System.String]$MakeAppxPath = Get-MakeAppxPath
if ([System.string]::IsNullOrWhiteSpace($MakeAppxPath)) {
throw [System.IO.FileNotFoundException]::New('Could not find the makeappx.exe')
}
# https://learn.microsoft.com/windows/win32/appxpkg/make-appx-package--makeappx-exe-#to-create-a-package-bundle-using-a-directory-structure
. $MakeAppxPath bundle /d $MSIXBundleOutput /p $MSIXBundle /o /v
if ($LASTEXITCODE -ne 0) { throw [System.InvalidOperationException]::New("MakeAppx failed creating the MSIXBundle. Exit Code: $LASTEXITCODE") }
#Endregion
Write-Host -Object "X64 MSIX File Path: $FinalMSIXX64Path" -ForegroundColor Green
Write-Host -Object "X64 MSIX File Name: $FinalMSIXX64Name" -ForegroundColor Green
Write-Host -Object "X64 Symbols: $FinalMSIXX64SymbolPath" -ForegroundColor Green
Write-Host -Object "ARM64 MSIX File Path: $FinalMSIXARM64Path" -ForegroundColor Cyan
Write-Host -Object "ARM64 MSIX File Name: $FinalMSIXARM64Name" -ForegroundColor Cyan
Write-Host -Object "ARM64 Symbols: $FinalMSIXARM64SymbolPath" -ForegroundColor Cyan
Write-Host -Object "MSIX Bundle File Path: $MSIXBundle" -ForegroundColor Yellow
Write-Host -Object "MSIX Bundle File Name: $FinalBundleFileName" -ForegroundColor Yellow
if ($Workflow) {
[XML]$CSProjXMLContent = Get-Content -Path $CsProjFilePath -Force
[string]$MSIXVersion = $CSProjXMLContent.Project.PropertyGroup.FileVersion
[string]$MSIXVersion = $MSIXVersion.Trim() # It would have trailing whitespaces
if ([string]::IsNullOrWhiteSpace($FinalMSIXX64Path) -or [string]::IsNullOrWhiteSpace($FinalMSIXX64Name) -or [string]::IsNullOrWhiteSpace($MSIXVersion)) { throw 'Necessary info could not be found' }
# Write the MSIXVersion to GITHUB_ENV to set it as an environment variable for the entire workflow
Add-Content -Path ($env:GITHUB_ENV, $env:GITHUB_OUTPUT) -Value "PACKAGE_VERSION=$MSIXVersion"
# Saving the details for the MSIX Bundle file
Add-Content -Path ($env:GITHUB_ENV, $env:GITHUB_OUTPUT) -Value "MSIXBundle_PATH=$MSIXBundle"
Add-Content -Path ($env:GITHUB_ENV, $env:GITHUB_OUTPUT) -Value "MSIXBundle_NAME=$FinalBundleFileName"
# Saving the details of the log files
Add-Content -Path ($env:GITHUB_ENV, $env:GITHUB_OUTPUT) -Value "X64MSBuildLog_PATH=$((Resolve-Path -Path .\X64MSBuildLog.binlog).Path)"
Add-Content -Path ($env:GITHUB_ENV, $env:GITHUB_OUTPUT) -Value "ARM64MSBuildLog_PATH=$((Resolve-Path -Path .\ARM64MSBuildLog.binlog).Path)"
# Saving the details of the X64 symbol file
Add-Content -Path ($env:GITHUB_ENV, $env:GITHUB_OUTPUT) -Value "X64Symbol_PATH=$FinalMSIXX64SymbolPath"
Add-Content -Path ($env:GITHUB_ENV, $env:GITHUB_OUTPUT) -Value "X64Symbol_NAME=$FinalMSIXX64SymbolName"
# Saving the details of the ARM64 symbol file
Add-Content -Path ($env:GITHUB_ENV, $env:GITHUB_OUTPUT) -Value "ARM64Symbol_PATH=$FinalMSIXARM64SymbolPath"
Add-Content -Path ($env:GITHUB_ENV, $env:GITHUB_OUTPUT) -Value "ARM64Symbol_NAME=$FinalMSIXARM64SymbolName"
# https://github.com/microsoft/sbom-tool
# Generating SBOM
Invoke-WebRequest -Uri 'https://github.com/microsoft/sbom-tool/releases/latest/download/sbom-tool-win-x64.exe' -OutFile "${Env:RUNNER_TEMP}\sbom-tool.exe"
# https://github.com/microsoft/sbom-tool/blob/main/docs/sbom-tool-arguments.md
. "${Env:RUNNER_TEMP}\sbom-tool.exe" generate -b $MSIXBundleOutput -bc .\ -pn 'Harden System Security' -ps 'Violet Hansen' -pv $MSIXVersion -nsb 'https://github.com/HotCakeX/Harden-Windows-Security' -V Verbose -gt true -li true -pm true -D true -lto 80
# Saving the details of the SBOM file
Add-Content -Path ($env:GITHUB_ENV, $env:GITHUB_OUTPUT) -Value "SBOM_PATH=$MSIXBundleOutput/_manifest/spdx_2.2/manifest.spdx.json"
Add-Content -Path ($env:GITHUB_ENV, $env:GITHUB_OUTPUT) -Value 'SBOM_NAME=manifest.spdx.json'
}
if ($Upload) {
dotnet clean '..\AppControl Manager\eXclude\PartnerCenter\PartnerCenter.slnx' --configuration Release
dotnet build '..\AppControl Manager\eXclude\PartnerCenter\PartnerCenter.slnx' --configuration Release --verbosity minimal
dotnet msbuild '..\AppControl Manager\eXclude\PartnerCenter\PartnerCenter.slnx' /p:Configuration=Release /p:Platform=x64 /p:PublishProfile=win-x64 /t:Publish -v:minimal
[System.String]$TokenEndpoint = $env:PARTNERCENTER_TOKENENDPOINT
[System.String]$ClientId = $env:PARTNERCENTER_CLIENTID
[System.String]$ClientSecret = $env:PARTNERCENTER_CLIENTSECRET
[System.String]$ApplicationId = $env:PARTNERCENTER_APPLICATIONID_HSS
[System.String]$PackageFilePath = $MSIXBundle
[System.String]$ReleaseNotesFilePath = (Resolve-Path -Path ReleaseNotes.txt).Path
. '..\AppControl Manager\eXclude\PartnerCenter\X64Output\PartnerCenter.exe' $TokenEndpoint $ClientId $ClientSecret $ApplicationId $PackageFilePath $ReleaseNotesFilePath
}
if ($null -ne $Stopwatch) {
$Stopwatch.Stop()
$Elapsed = $Stopwatch.Elapsed
[string]$Result = @"
Execution Time:
----------------------------
Total Time : $($Elapsed.ToString('g'))
Hours : $($Elapsed.Hours)
Minutes : $($Elapsed.Minutes)
Seconds : $($Elapsed.Seconds)
Milliseconds : $($Elapsed.Milliseconds)
----------------------------
"@
Write-Host -Object $Result -ForegroundColor Cyan
}
}
# For GitHub workflow
Build_HSS -DownloadRepo $false -InstallDeps $false -Workflow $true -UpdateWorkLoads $false -Upload $true
# Local - ARM64 + X64
# Build_HSS -DownloadRepo $false -InstallDeps $false -Workflow $false -UpdateWorkLoads $false -Upload $false