All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 1m3s
CD Pipeline / build-and-deploy (push) Successful in 4m29s
CD Pipeline / post-deploy-checks (push) Successful in 1m48s
407 lines
15 KiB
PowerShell
407 lines
15 KiB
PowerShell
param(
|
|
[ValidateSet("Verify", "DryRun", "Apply")]
|
|
[string]$Mode = "Verify",
|
|
[string]$VmrunPath = "C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe",
|
|
[string]$Host110Vmx = "",
|
|
[string]$Host111Vmx = "",
|
|
[string]$Host188Vmx = "",
|
|
[string]$Host120Vmx = "",
|
|
[string]$Host121Vmx = "",
|
|
[string]$Host112Vmx = "",
|
|
[string[]]$RequiredVmAliases = @("111", "188", "120", "121", "112"),
|
|
[switch]$EnableRecursiveDiscovery,
|
|
[string[]]$DiscoveryRoot = @(
|
|
"D:\Documents\Virtual Machines",
|
|
"D:\Downloads",
|
|
"D:\VMs",
|
|
"E:\VMs",
|
|
"C:\VMs",
|
|
"C:\Users\Public\Documents\Virtual Machines"
|
|
)
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
$TaskName = "AWOOOI-Start-VMware-VMs"
|
|
$ProgramDataDir = "C:\ProgramData\AWOOOI"
|
|
$StartScript = Join-Path $ProgramDataDir "Start-AWOOOI-VMs.ps1"
|
|
$VmOrder = @($RequiredVmAliases)
|
|
$SuppliedVmx = @{
|
|
"110" = $Host110Vmx
|
|
"111" = $Host111Vmx
|
|
"188" = $Host188Vmx
|
|
"120" = $Host120Vmx
|
|
"121" = $Host121Vmx
|
|
"112" = $Host112Vmx
|
|
}
|
|
$KnownVmxCandidates = @{
|
|
"110" = @("D:\Documents\Virtual Machines\Ubuntu 64-bit (3)\Ubuntu 64-bit (3).vmx")
|
|
"111" = @("D:\Documents\Virtual Machines\192.168.0.111_Ubuntu_64-bit\192.168.0.111_Ubuntu_64-bit.vmx")
|
|
"188" = @("D:\Documents\Virtual Machines\Ollama_Ubuntu_64-bit\Ollama_Ubuntu_64-bit.vmx")
|
|
"120" = @("D:\Documents\Virtual Machines\192.168.0.120_Ubuntu_64-bit\192.168.0.120_Ubuntu_64-bit.vmx")
|
|
"121" = @("D:\Documents\Virtual Machines\192.168.0.121_Ubuntu_64-bit\192.168.0.121_Ubuntu_64-bit.vmx")
|
|
"112" = @("D:\Downloads\kali-linux-2025.4-vmware-amd64\kali-linux-2025.4-vmware-amd64.vmwarevm\kali-linux-2025.4-vmware-amd64.vmx")
|
|
}
|
|
|
|
function Resolve-VmxPath {
|
|
param([string]$HostAlias)
|
|
|
|
$supplied = $SuppliedVmx[$HostAlias]
|
|
if ($supplied -and (Test-Path -LiteralPath $supplied)) {
|
|
return (Resolve-Path -LiteralPath $supplied).Path
|
|
}
|
|
|
|
foreach ($candidate in @($KnownVmxCandidates[$HostAlias])) {
|
|
if ($candidate -and (Test-Path -LiteralPath $candidate)) {
|
|
return (Resolve-Path -LiteralPath $candidate).Path
|
|
}
|
|
}
|
|
|
|
if (-not $EnableRecursiveDiscovery) {
|
|
return ""
|
|
}
|
|
|
|
foreach ($root in $DiscoveryRoot) {
|
|
if (-not (Test-Path -LiteralPath $root)) {
|
|
continue
|
|
}
|
|
$match = Get-ChildItem -LiteralPath $root -Recurse -Filter "*.vmx" -ErrorAction SilentlyContinue |
|
|
Where-Object {
|
|
$displayName = ""
|
|
try {
|
|
$displayName = (Select-String -LiteralPath $_.FullName -Pattern "^displayName\s*=\s*" -ErrorAction SilentlyContinue |
|
|
Select-Object -First 1).Line
|
|
} catch {
|
|
$displayName = ""
|
|
}
|
|
$_.FullName -match "(^|[\\_\-\s])$HostAlias([\\_\-\s]|$)" -or
|
|
$displayName -match "(^|[\\_\-\s])$HostAlias([\\_\-\s]|$)"
|
|
} |
|
|
Select-Object -First 1
|
|
if ($match) {
|
|
return $match.FullName
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
function Get-VmMap {
|
|
$map = [ordered]@{}
|
|
foreach ($alias in $VmOrder) {
|
|
$map[$alias] = Resolve-VmxPath -HostAlias $alias
|
|
}
|
|
return $map
|
|
}
|
|
|
|
function Write-StartupScript {
|
|
param([hashtable]$VmMap)
|
|
|
|
New-Item -ItemType Directory -Path $ProgramDataDir -Force | Out-Null
|
|
$vmRows = foreach ($alias in $VmOrder) {
|
|
" @{ Alias = `"$alias`"; Path = `"$($VmMap[$alias])`" }"
|
|
}
|
|
$body = @"
|
|
`$ErrorActionPreference = "Continue"
|
|
`$VmrunPath = "$VmrunPath"
|
|
`$VMs = @(
|
|
$($vmRows -join ",`n")
|
|
)
|
|
|
|
Start-Service -Name "VMAuthdService" -ErrorAction SilentlyContinue
|
|
Start-Service -Name "VMnetDHCP" -ErrorAction SilentlyContinue
|
|
Start-Sleep -Seconds 30
|
|
`$running = (& `$VmrunPath list 2>`$null) -join [Environment]::NewLine
|
|
|
|
foreach (`$vm in `$VMs) {
|
|
if (-not (Test-Path -LiteralPath `$vm.Path)) {
|
|
Write-Output "VMX_MISSING alias=`$(`$vm.Alias) path=`$(`$vm.Path)"
|
|
continue
|
|
}
|
|
if (`$running -like "*`$(`$vm.Path)*") {
|
|
Write-Output "VM_ALREADY_RUNNING alias=`$(`$vm.Alias) path=`$(`$vm.Path)"
|
|
continue
|
|
}
|
|
& `$VmrunPath -T ws start `$vm.Path nogui
|
|
Write-Output "VM_START_REQUESTED alias=`$(`$vm.Alias) path=`$(`$vm.Path) exit=`$LASTEXITCODE"
|
|
Start-Sleep -Seconds 20
|
|
}
|
|
"@
|
|
Set-Content -LiteralPath $StartScript -Value $body -Encoding UTF8
|
|
}
|
|
|
|
function Apply-WindowsUpdatePolicy {
|
|
$auPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU"
|
|
New-Item -Path $auPath -Force | Out-Null
|
|
New-ItemProperty -Path $auPath -Name "NoAutoRebootWithLoggedOnUsers" -Value 1 -PropertyType DWord -Force | Out-Null
|
|
New-ItemProperty -Path $auPath -Name "AlwaysAutoRebootAtScheduledTime" -Value 0 -PropertyType DWord -Force | Out-Null
|
|
New-ItemProperty -Path $auPath -Name "AUOptions" -Value 3 -PropertyType DWord -Force | Out-Null
|
|
New-ItemProperty -Path $auPath -Name "ScheduledInstallDay" -Value 0 -PropertyType DWord -Force | Out-Null
|
|
New-ItemProperty -Path $auPath -Name "ScheduledInstallTime" -Value 3 -PropertyType DWord -Force | Out-Null
|
|
New-ItemProperty -Path $auPath -Name "AUPowerManagement" -Value 0 -PropertyType DWord -Force | Out-Null
|
|
}
|
|
|
|
function Apply-ScheduledTask {
|
|
$action = New-ScheduledTaskAction `
|
|
-Execute "powershell.exe" `
|
|
-Argument "-NoProfile -ExecutionPolicy Bypass -File `"$StartScript`""
|
|
$trigger = New-ScheduledTaskTrigger -AtStartup
|
|
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest
|
|
$settings = New-ScheduledTaskSettingsSet `
|
|
-AllowStartIfOnBatteries `
|
|
-DontStopIfGoingOnBatteries `
|
|
-MultipleInstances IgnoreNew `
|
|
-RestartCount 3 `
|
|
-RestartInterval (New-TimeSpan -Minutes 2) `
|
|
-StartWhenAvailable
|
|
Register-ScheduledTask `
|
|
-TaskName $TaskName `
|
|
-Action $action `
|
|
-Trigger $trigger `
|
|
-Principal $principal `
|
|
-Settings $settings `
|
|
-Force | Out-Null
|
|
}
|
|
|
|
function Get-VmrunList {
|
|
if (-not (Test-Path -LiteralPath $VmrunPath)) {
|
|
return @()
|
|
}
|
|
try {
|
|
return @(& $VmrunPath list 2>$null | Select-Object -Skip 1)
|
|
} catch {
|
|
return @()
|
|
}
|
|
}
|
|
|
|
function Get-VmwareServiceReadback {
|
|
$serviceNames = @("VMAuthdService", "VMnetDHCP")
|
|
foreach ($name in $serviceNames) {
|
|
$service = Get-Service -Name $name -ErrorAction SilentlyContinue
|
|
$startupType = "unknown"
|
|
try {
|
|
$cim = Get-CimInstance -ClassName Win32_Service -Filter "Name='$name'" -ErrorAction SilentlyContinue
|
|
if ($cim) {
|
|
$startupType = $cim.StartMode
|
|
}
|
|
} catch {
|
|
$startupType = "unknown"
|
|
}
|
|
$present = [int][bool]$service
|
|
$status = if ($service) { "$($service.Status)" } else { "missing" }
|
|
$ok = [int]($service -and $service.Status -eq "Running" -and $startupType -in @("Auto", "Automatic"))
|
|
Write-Output "VMWARE_SERVICE name=$name present=$present status=$status startup_type=$startupType ok=$ok"
|
|
}
|
|
}
|
|
|
|
function Format-ReadbackValue {
|
|
param([object]$Value)
|
|
|
|
if ($null -eq $Value) {
|
|
return "unknown"
|
|
}
|
|
$text = "$Value"
|
|
if ([string]::IsNullOrWhiteSpace($text)) {
|
|
return "unknown"
|
|
}
|
|
return ($text -replace "\s+", "_")
|
|
}
|
|
|
|
function Get-AutostartTaskReadback {
|
|
$task = $null
|
|
$taskInfo = $null
|
|
try {
|
|
$task = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
|
|
} catch {
|
|
$task = $null
|
|
}
|
|
try {
|
|
$taskInfo = Get-ScheduledTaskInfo -TaskName $TaskName -ErrorAction SilentlyContinue
|
|
} catch {
|
|
$taskInfo = $null
|
|
}
|
|
$present = [int][bool]$task
|
|
$enabled = 0
|
|
$state = "missing"
|
|
$triggerCount = 0
|
|
$actionCount = 0
|
|
$principalUser = "missing"
|
|
$runLevel = "missing"
|
|
$logonType = "missing"
|
|
$lastRunTime = "never"
|
|
$lastTaskResult = "unknown"
|
|
$nextRunTime = "unknown"
|
|
$actionExecute = "missing"
|
|
$actionArgument = "missing"
|
|
if ($task) {
|
|
$enabled = [int]($task.State -ne "Disabled")
|
|
$state = "$($task.State)"
|
|
$triggerCount = @($task.Triggers).Count
|
|
$actionCount = @($task.Actions).Count
|
|
if ($task.Principal) {
|
|
$principalUser = "$($task.Principal.UserId)"
|
|
$runLevel = "$($task.Principal.RunLevel)"
|
|
$logonType = "$($task.Principal.LogonType)"
|
|
}
|
|
$actionExecute = (@($task.Actions) | ForEach-Object { "$($_.Execute)" }) -join "|"
|
|
$actionArgument = (@($task.Actions) | ForEach-Object { "$($_.Arguments)" }) -join "|"
|
|
}
|
|
if ($taskInfo) {
|
|
if ($taskInfo.LastRunTime) {
|
|
$lastRunTime = $taskInfo.LastRunTime.ToString("s")
|
|
}
|
|
$lastTaskResult = "$($taskInfo.LastTaskResult)"
|
|
if ($taskInfo.NextRunTime) {
|
|
$nextRunTime = $taskInfo.NextRunTime.ToString("s")
|
|
}
|
|
}
|
|
$startScriptPresent = [int](Test-Path -LiteralPath $StartScript)
|
|
$ok = [int]($task -and $enabled -eq 1 -and $triggerCount -gt 0 -and $actionCount -gt 0)
|
|
Write-Output "VMWARE_AUTOSTART_TASK name=$TaskName present=$present enabled=$enabled state=$state trigger_count=$triggerCount action_count=$actionCount ok=$ok"
|
|
Write-Output "VMWARE_AUTOSTART_TASK_DETAIL name=$TaskName principal_user=$(Format-ReadbackValue $principalUser) run_level=$(Format-ReadbackValue $runLevel) logon_type=$(Format-ReadbackValue $logonType) start_script_present=$startScriptPresent last_run_time=$(Format-ReadbackValue $lastRunTime) last_task_result=$(Format-ReadbackValue $lastTaskResult) next_run_time=$(Format-ReadbackValue $nextRunTime)"
|
|
Write-Output "VMWARE_AUTOSTART_TASK_ACTION name=$TaskName execute=$(Format-ReadbackValue $actionExecute) argument=$(Format-ReadbackValue $actionArgument)"
|
|
}
|
|
|
|
function Get-WindowsUpdatePolicyReadback {
|
|
$auPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU"
|
|
$expected = [ordered]@{
|
|
"NoAutoRebootWithLoggedOnUsers" = 1
|
|
"AlwaysAutoRebootAtScheduledTime" = 0
|
|
"AUOptions" = 3
|
|
"ScheduledInstallDay" = 0
|
|
"ScheduledInstallTime" = 3
|
|
"AUPowerManagement" = 0
|
|
}
|
|
$props = $null
|
|
if (Test-Path -LiteralPath $auPath) {
|
|
$props = Get-ItemProperty -LiteralPath $auPath -ErrorAction SilentlyContinue
|
|
}
|
|
foreach ($name in $expected.Keys) {
|
|
$value = "missing"
|
|
if ($props -and $null -ne $props.$name) {
|
|
$value = "$($props.$name)"
|
|
}
|
|
$ok = [int]($value -eq "$($expected[$name])")
|
|
Write-Output "WINDOWS_UPDATE_POLICY name=$name value=$value expected=$($expected[$name]) ok=$ok"
|
|
}
|
|
}
|
|
|
|
function Get-VmPowerReadback {
|
|
param([hashtable]$VmMap)
|
|
|
|
$running = Get-VmrunList
|
|
foreach ($alias in $VmOrder) {
|
|
$path = "$($VmMap[$alias])"
|
|
$present = [int][bool]$path
|
|
$runningMatch = 0
|
|
if ($path) {
|
|
$runningMatch = [int]($running -contains $path)
|
|
}
|
|
Write-Output "VM_POWER alias=$alias vmx_present=$present running=$runningMatch"
|
|
}
|
|
}
|
|
|
|
$vmMap = Get-VmMap
|
|
$missing = @($VmOrder | Where-Object { -not $vmMap[$_] })
|
|
$vmrunPresent = Test-Path -LiteralPath $VmrunPath
|
|
|
|
Write-Output "AWOOOI_WINDOWS99_VMWARE_AUTOSTART=1"
|
|
Write-Output "MODE=$Mode"
|
|
Write-Output "VMRUN_PRESENT=$([int]$vmrunPresent)"
|
|
if ($SuppliedVmx["111"] -or $VmOrder -contains "111") {
|
|
Write-Output "HOST111_VMWARE_TARGET=explicit_or_required"
|
|
} else {
|
|
Write-Output "HOST111_VMWARE_TARGET=not_required_by_default_missing_no_vmx_found_on_99"
|
|
}
|
|
foreach ($alias in $VmOrder) {
|
|
Write-Output "VMX alias=$alias path=$($vmMap[$alias]) present=$([int][bool]$vmMap[$alias])"
|
|
}
|
|
Get-VmwareServiceReadback
|
|
Get-AutostartTaskReadback
|
|
Get-WindowsUpdatePolicyReadback
|
|
Get-VmPowerReadback -VmMap $vmMap
|
|
|
|
$serviceReady = @("VMAuthdService", "VMnetDHCP") | ForEach-Object {
|
|
$svc = Get-Service -Name $_ -ErrorAction SilentlyContinue
|
|
$svc -and $svc.Status -eq "Running"
|
|
}
|
|
$task = $null
|
|
try {
|
|
$task = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
|
|
} catch {
|
|
$task = $null
|
|
}
|
|
$policyValues = @{}
|
|
try {
|
|
$policy = Get-ItemProperty -LiteralPath "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -ErrorAction SilentlyContinue
|
|
if ($policy) {
|
|
$policyValues["NoAutoRebootWithLoggedOnUsers"] = $policy.NoAutoRebootWithLoggedOnUsers
|
|
$policyValues["AlwaysAutoRebootAtScheduledTime"] = $policy.AlwaysAutoRebootAtScheduledTime
|
|
$policyValues["AUOptions"] = $policy.AUOptions
|
|
$policyValues["ScheduledInstallDay"] = $policy.ScheduledInstallDay
|
|
$policyValues["ScheduledInstallTime"] = $policy.ScheduledInstallTime
|
|
$policyValues["AUPowerManagement"] = $policy.AUPowerManagement
|
|
}
|
|
} catch {
|
|
$policyValues = @{}
|
|
}
|
|
$windowsUpdateReady = (
|
|
"$($policyValues["NoAutoRebootWithLoggedOnUsers"])" -eq "1" -and
|
|
"$($policyValues["AlwaysAutoRebootAtScheduledTime"])" -eq "0" -and
|
|
"$($policyValues["AUOptions"])" -eq "3" -and
|
|
"$($policyValues["ScheduledInstallDay"])" -eq "0" -and
|
|
"$($policyValues["ScheduledInstallTime"])" -eq "3" -and
|
|
"$($policyValues["AUPowerManagement"])" -eq "0"
|
|
)
|
|
$runningVmList = Get-VmrunList
|
|
$vmPowerReady = $true
|
|
foreach ($alias in $VmOrder) {
|
|
$path = "$($vmMap[$alias])"
|
|
if (-not $path -or -not ($runningVmList -contains $path)) {
|
|
$vmPowerReady = $false
|
|
}
|
|
}
|
|
$configReady = (
|
|
$vmrunPresent -and
|
|
$missing.Count -eq 0 -and
|
|
($serviceReady -notcontains $false) -and
|
|
$task -and
|
|
$task.State -ne "Disabled" -and
|
|
@($task.Triggers).Count -gt 0 -and
|
|
@($task.Actions).Count -gt 0 -and
|
|
$windowsUpdateReady
|
|
)
|
|
Write-Output "VMWARE_AUTOSTART_CONFIG_READY=$([int]$configReady)"
|
|
Write-Output "VMWARE_AUTOSTART_POWER_READY=$([int]$vmPowerReady)"
|
|
Write-Output "WINDOWS_UPDATE_NO_AUTO_REBOOT_READY=$([int]$windowsUpdateReady)"
|
|
Write-Output "VMWARE_AUTOSTART_VERIFY_READY=$([int]($configReady -and $vmPowerReady))"
|
|
|
|
if (-not $vmrunPresent) {
|
|
Write-Error "vmrun.exe not found at $VmrunPath"
|
|
}
|
|
|
|
if ($missing.Count -gt 0) {
|
|
Write-Output "MISSING_VMX_ALIASES=$($missing -join ',')"
|
|
if ($Mode -eq "Apply") {
|
|
throw "Apply requires explicit or discoverable VMX paths for every required VM."
|
|
}
|
|
}
|
|
|
|
if ($Mode -eq "Verify") {
|
|
exit 0
|
|
}
|
|
|
|
if ($Mode -eq "DryRun") {
|
|
Write-Output "DRY_RUN would_write_start_script=$StartScript"
|
|
Write-Output "DRY_RUN would_register_task=$TaskName"
|
|
Write-Output "DRY_RUN would_apply_windows_update_no_auto_reboot_policy=1"
|
|
exit 0
|
|
}
|
|
|
|
Write-StartupScript -VmMap $vmMap
|
|
Set-Service -Name "VMAuthdService" -StartupType Automatic -ErrorAction SilentlyContinue
|
|
Set-Service -Name "VMnetDHCP" -StartupType Automatic -ErrorAction SilentlyContinue
|
|
Apply-ScheduledTask
|
|
Apply-WindowsUpdatePolicy
|
|
|
|
Write-Output "APPLIED scheduled_task=$TaskName start_script=$StartScript windows_update_no_auto_reboot_policy=1"
|