#Requires -Version 5.1 <# .SYNOPSIS Creates a linked clone of the template VM for an ephemeral CI build. .DESCRIPTION Uses vmrun.exe to create a linked clone from a frozen template snapshot. Returns the full path to the new clone's .vmx file on success. The template VM and its snapshot are never modified. .PARAMETER TemplatePath Full path to the template VM's .vmx file. Example: F:\CI\Templates\WinBuild\WinBuild.vmx .PARAMETER SnapshotName Name of the snapshot on the template VM to clone from. Default: BaseClean .PARAMETER CloneBaseDir Directory under which the new clone folder will be created. The clone folder is named after the job ID and timestamp. Example: F:\CI\BuildVMs .PARAMETER JobId Unique identifier for the CI job (e.g. run ID from Gitea Actions). Used to name the clone: Clone_{JobId}_{yyyyMMdd_HHmmss} .PARAMETER VmrunPath Full path to vmrun.exe. Default: C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe .OUTPUTS [string] Full path to the new clone's .vmx file. .EXAMPLE $vmxPath = .\New-BuildVM.ps1 ` -TemplatePath "F:\CI\Templates\WinBuild\WinBuild.vmx" ` -CloneBaseDir "F:\CI\BuildVMs" ` -JobId "run-42" #> [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateScript({ Test-Path $_ -PathType Leaf })] [string] $TemplatePath, [string] $SnapshotName = 'BaseClean', [Parameter(Mandatory)] [string] $CloneBaseDir, [Parameter(Mandatory)] [string] $JobId, [string] $VmrunPath = 'C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe' ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' # --- Validate vmrun.exe --- if (-not (Test-Path $VmrunPath -PathType Leaf)) { throw "vmrun.exe not found at: $VmrunPath`nInstall VMware Workstation Pro and update -VmrunPath if needed." } # --- Build clone path --- $timestamp = Get-Date -Format 'yyyyMMdd_HHmmss' $cloneName = "Clone_${JobId}_${timestamp}" $cloneDir = Join-Path $CloneBaseDir $cloneName $cloneVmx = Join-Path $cloneDir "$cloneName.vmx" # Ensure clone base dir exists if (-not (Test-Path $CloneBaseDir)) { New-Item -ItemType Directory -Path $CloneBaseDir -Force | Out-Null } Write-Host "[New-BuildVM] Creating linked clone..." Write-Host " Template : $TemplatePath" Write-Host " Snapshot : $SnapshotName" Write-Host " Clone VMX : $cloneVmx" $sw = [System.Diagnostics.Stopwatch]::StartNew() # --- Run vmrun clone --- $vmrunArgs = @( '-T', 'ws', 'clone', $TemplatePath, $cloneVmx, 'linked', '-snapshot', $SnapshotName ) $result = & $VmrunPath @vmrunArgs 2>&1 $exitCode = $LASTEXITCODE $sw.Stop() if ($exitCode -ne 0) { # Clean up partial clone directory if it was created if (Test-Path $cloneDir) { Remove-Item $cloneDir -Recurse -Force -ErrorAction SilentlyContinue } throw "vmrun clone failed (exit $exitCode).`nOutput: $result" } if (-not (Test-Path $cloneVmx -PathType Leaf)) { throw "vmrun reported success but clone VMX not found at: $cloneVmx" } Write-Host "[New-BuildVM] Clone created in $($sw.Elapsed.TotalSeconds.ToString('F1'))s : $cloneVmx" # Return the VMX path as output return $cloneVmx