EnhancedHyperVAO.psm1
#Region '.\Public\Add-DVDDriveToVM.ps1' -1 function Add-DVDDriveToVM { <# .SYNOPSIS Adds a DVD drive with the specified ISO to the VM and validates the addition. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$VMName, [Parameter(Mandatory = $true)] [string]$InstallMediaPath ) Begin { Write-EnhancedLog -Message "Starting Add-DVDDriveToVM function" -Level "INFO" Log-Params -Params @{ VMName = $VMName; InstallMediaPath = $InstallMediaPath } } Process { try { Write-EnhancedLog -Message "Validating if the ISO is already added to VM: $VMName" -Level "INFO" if (Validate-ISOAdded -VMName $VMName -InstallMediaPath $InstallMediaPath) { Write-EnhancedLog -Message "ISO is already added to VM: $VMName" -Level "INFO" return } Write-EnhancedLog -Message "Adding SCSI controller to VM: $VMName" -Level "INFO" Add-VMScsiController -VMName $VMName -ErrorAction Stop Write-EnhancedLog -Message "Adding DVD drive with ISO to VM: $VMName" -Level "INFO" Add-VMDvdDrive -VMName $VMName -Path $InstallMediaPath -ErrorAction Stop Write-EnhancedLog -Message "DVD drive with ISO added to VM: $VMName" -Level "INFO" Write-EnhancedLog -Message "Validating the ISO addition for VM: $VMName" -Level "INFO" if (-not (Validate-ISOAdded -VMName $VMName -InstallMediaPath $InstallMediaPath)) { Write-EnhancedLog -Message "Failed to validate the ISO addition for VM: $VMName" -Level "ERROR" throw "ISO validation failed." } } catch { Write-EnhancedLog -Message "An error occurred while adding DVD drive to VM: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ } } End { Write-EnhancedLog -Message "Exiting Add-DVDDriveToVM function" -Level "INFO" } } #EndRegion '.\Public\Add-DVDDriveToVM.ps1' 52 #Region '.\Public\ConfigureVM.ps1' -1 function ConfigureVM { <# .SYNOPSIS Configures the specified VM with the given processor count and memory settings. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$VMName, [Parameter(Mandatory = $true)] [int]$ProcessorCount ) Begin { Write-EnhancedLog -Message "Starting Configure-VM function" -Level "INFO" Log-Params -Params @{ VMName = $VMName; ProcessorCount = $ProcessorCount } } Process { try { Write-EnhancedLog -Message "Configuring VM processor for VM: $VMName with $ProcessorCount processors" -Level "INFO" Set-VMProcessor -VMName $VMName -ExposeVirtualizationExtensions $true -Count $ProcessorCount Write-EnhancedLog -Message "Configuring memory for VM: $VMName" -Level "INFO" Set-VMMemory -VMName $VMName Write-EnhancedLog -Message "VM $VMName configured" -Level "INFO" } catch { Write-EnhancedLog -Message "An error occurred while configuring VM $VMName $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ } } End { Write-EnhancedLog -Message "Exiting Configure-VM function" -Level "INFO" } } #EndRegion '.\Public\ConfigureVM.ps1' 39 #Region '.\Public\ConfigureVMBoot.ps1' -1 function ConfigureVMBoot { <# .SYNOPSIS Configures the boot order of the specified VM. Can configure boot from either DVD drive or differencing disk. .DESCRIPTION Configures the boot settings for a VM. If DifferencingDiskPath is provided, sets the VM to boot from that disk. Otherwise, configures the VM to boot from DVD drive. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$VMName, [Parameter(Mandatory = $false)] [string]$DifferencingDiskPath ) Begin { Write-EnhancedLog -Message "Starting Configure-VMBoot function" -Level "INFO" $logParams = @{ VMName = $VMName } if ($DifferencingDiskPath) { $logParams['DifferencingDiskPath'] = $DifferencingDiskPath } Log-Params -Params $logParams } Process { try { if ($DifferencingDiskPath) { # Differencing disk boot configuration Write-EnhancedLog -Message "Retrieving hard disk drive for VM: $VMName with path: $DifferencingDiskPath" -Level "INFO" $VHD = Get-VMHardDiskDrive -VMName $VMName | Where-Object { $_.Path -eq $DifferencingDiskPath } if ($null -eq $VHD) { Write-EnhancedLog -Message "No hard disk drive found for VM: $VMName with the specified path: $DifferencingDiskPath" -Level "ERROR" throw "Hard disk drive not found." } Write-EnhancedLog -Message "Setting VM firmware for VM: $VMName to boot from the specified disk" -Level "INFO" Set-VMFirmware -VMName $VMName -FirstBootDevice $VHD } else { # DVD drive boot configuration Write-EnhancedLog -Message "Retrieving DVD drive for VM: $VMName" -Level "INFO" $DVDDrive = Get-VMDvdDrive -VMName $VMName if ($null -eq $DVDDrive) { Write-EnhancedLog -Message "No DVD drive found for VM: $VMName" -Level "ERROR" throw "DVD drive not found." } Write-EnhancedLog -Message "Setting VM firmware for VM: $VMName to boot from DVD" -Level "INFO" Set-VMFirmware -VMName $VMName -FirstBootDevice $DVDDrive } Write-EnhancedLog -Message "VM boot configured for $VMName" -Level "INFO" } catch { Write-EnhancedLog -Message "An error occurred while configuring VM boot for $VMName $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ } } End { Write-EnhancedLog -Message "Exiting Configure-VMBoot function" -Level "INFO" } } #EndRegion '.\Public\ConfigureVMBoot.ps1' 71 #Region '.\Public\Connect-VMConsole.ps1' -1 function Connect-VMConsole { <# .SYNOPSIS Connects to the console of the specified VM. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$VMName, [Parameter(Mandatory = $false)] [string]$ServerName = "localhost", [Parameter(Mandatory = $false)] [int]$Count = 1 ) Begin { Write-EnhancedLog -Message "Starting Connect-VMConsole function" -Level "INFO" Log-Params -Params @{ VMName = $VMName; ServerName = $ServerName; Count = $Count } } Process { try { Write-EnhancedLog -Message "Validating if VM $VMName exists" -Level "INFO" if (-not (Validate-VMExists -VMName $VMName)) { Write-EnhancedLog -Message "VM $VMName does not exist. Exiting function." -Level "ERROR" return } Write-EnhancedLog -Message "Checking if VM $VMName is running" -Level "INFO" if (-not (Validate-VMStarted -VMName $VMName)) { Write-EnhancedLog -Message "VM $VMName is not running. Cannot connect to console." -Level "ERROR" return } $vmConnectArgs = "$ServerName `"$VMName`"" if ($Count -gt 1) { $vmConnectArgs += " -C $Count" } Write-EnhancedLog -Message "VMConnect arguments: $vmConnectArgs" -Level "DEBUG" Start-Process -FilePath "vmconnect.exe" -ArgumentList $vmConnectArgs -ErrorAction Stop Write-EnhancedLog -Message "VMConnect launched for VM $VMName on $ServerName with count $Count." -Level "INFO" } catch { Write-EnhancedLog -Message "An error occurred while launching VMConnect for VM $VMName. $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ } } End { Write-EnhancedLog -Message "Exiting Connect-VMConsole function" -Level "INFO" } } #EndRegion '.\Public\Connect-VMConsole.ps1' 55 #Region '.\Public\CreateVMFolder.ps1' -1 function CreateVMFolder { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$VMPath, [Parameter(Mandatory = $true)] [string]$VMName ) Begin { Write-EnhancedLog -Message "Starting CreateVMFolder function" -Level "INFO" Log-Params -Params @{ VMPath = $VMPath; VMName = $VMName } } Process { try { $VMFullPath = Join-Path -Path $VMPath -ChildPath $VMName Write-EnhancedLog -Message "Creating VM folder at path: $VMFullPath" -Level "INFO" New-Item -ItemType Directory -Force -Path $VMFullPath | Out-Null Write-EnhancedLog -Message "VM folder created at $VMFullPath" -Level "INFO" -ForegroundColor ([ConsoleColor]::Green) return $VMFullPath } catch { Write-EnhancedLog -Message "An error occurred while creating the VM folder: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ } } End { Write-EnhancedLog -Message "Exiting Create-VMFolder function" -Level "INFO" } } #EndRegion '.\Public\CreateVMFolder.ps1' 33 #Region '.\Public\Dismount-VHDX.ps1' -1 function Dismount-VHDX { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$VHDXPath ) Begin { Write-EnhancedLog -Message "Starting Dismount-VHDX function" -Level "INFO" Log-Params -Params @{ VHDXPath = $VHDXPath } } Process { try { Write-EnhancedLog -Message "Validating if the VHDX is mounted: $VHDXPath" -Level "INFO" $isMounted = Validate-VHDMount -VHDXPath $VHDXPath Write-EnhancedLog -Message "Validation result: VHDX is mounted = $isMounted" -Level "INFO" if ($isMounted) { Write-EnhancedLog -Message "Checking for dependent VMs using the VHDX: $VHDXPath" -Level "INFO" $dependentVMs = Get-DependentVMs -VHDXPath $VHDXPath $runningVMs = $dependentVMs | Where-Object { $_.State -eq 'Running' } if ($runningVMs.Count -gt 0) { Write-EnhancedLog -Message "Found running VMs using the VHDX. Skipping dismount." -Level "WARNING" foreach ($vm in $runningVMs) { Write-EnhancedLog -Message "Running dependent VM: $($vm.Name)" -Level "WARNING" } return } Write-EnhancedLog -Message "No running VMs found using the VHDX. Proceeding with dismount." -Level "INFO" Dismount-VHD -Path $VHDXPath -ErrorAction Stop Write-EnhancedLog -Message "VHDX dismounted successfully." -Level "INFO" } else { Write-EnhancedLog -Message "$VHDXPath is already dismounted or not mounted." -Level "INFO" } } catch { Write-EnhancedLog -Message "An error occurred while dismounting the VHDX: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ } } End { Write-EnhancedLog -Message "Exiting Dismount-VHDX function" -Level "INFO" } } #EndRegion '.\Public\Dismount-VHDX.ps1' 48 #Region '.\Public\EnableVMTPM.ps1' -1 function EnableVMTPM { <# .SYNOPSIS Enables TPM for the specified VM. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$VMName ) Begin { Write-EnhancedLog -Message "Starting Enable-VMTPM function" -Level "INFO" Log-Params -Params @{ VMName = $VMName } } Process { try { Write-EnhancedLog -Message "Retrieving HGS Guardian" -Level "INFO" $owner = Get-HgsGuardian -Name "UntrustedGuardian" Write-EnhancedLog -Message "Creating new HGS Key Protector" -Level "INFO" $kp = New-HgsKeyProtector -Owner $owner -AllowUntrustedRoot Write-EnhancedLog -Message "Setting VM Key Protector for VM: $VMName" -Level "INFO" Set-VMKeyProtector -VMName $VMName -KeyProtector $kp.RawData Write-EnhancedLog -Message "Enabling TPM for VM: $VMName" -Level "INFO" Enable-VMTPM -VMName $VMName Write-EnhancedLog -Message "TPM enabled for $VMName" -Level "INFO" } catch { Write-EnhancedLog -Message "An error occurred while enabling TPM for VM $VMName $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ } } End { Write-EnhancedLog -Message "Exiting Enable-VMTPM function" -Level "INFO" } } #EndRegion '.\Public\EnableVMTPM.ps1' 42 #Region '.\Public\EnsureUntrustedGuardianExists.ps1' -1 function EnsureUntrustedGuardianExists { <# .SYNOPSIS Ensures that an untrusted guardian exists. If not, creates one. #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string]$GuardianName = 'UntrustedGuardian' ) Begin { Write-EnhancedLog -Message "Starting Ensure-UntrustedGuardianExists function" -Level "INFO" Log-Params -Params @{ GuardianName = $GuardianName } } Process { try { Write-EnhancedLog -Message "Checking for the existence of the guardian: $GuardianName" -Level "INFO" $guardian = Get-HgsGuardian -Name $GuardianName -ErrorAction SilentlyContinue if ($null -eq $guardian) { Write-EnhancedLog -Message "Guardian $GuardianName not found. Creating..." -Level "WARNING" New-HgsGuardian -Name $GuardianName -GenerateCertificates Write-EnhancedLog -Message "Guardian $GuardianName created successfully" -Level "INFO" -ForegroundColor ([ConsoleColor]::Green) } else { Write-EnhancedLog -Message "Guardian $GuardianName already exists" -Level "INFO" } } catch { Write-EnhancedLog -Message "An error occurred while checking or creating the guardian: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ } } End { Write-EnhancedLog -Message "Exiting Ensure-UntrustedGuardianExists function" -Level "INFO" } } #EndRegion '.\Public\EnsureUntrustedGuardianExists.ps1' 39 #Region '.\Public\Get-DependentVMs.ps1' -1 function Get-DependentVMs { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$VHDXPath ) Begin { Write-EnhancedLog -Message "Starting Get-DependentVMs function" -Level "INFO" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } Process { try { Write-EnhancedLog -Message "Retrieving all VMs" -Level "INFO" $allVMs = Get-VM Write-EnhancedLog -Message "Total VMs found: $($allVMs.Count)" -Level "INFO" $dependentVMs = [System.Collections.Generic.List[PSObject]]::new() foreach ($vm in $allVMs) { $hardDrives = $vm.HardDrives foreach ($hd in $hardDrives) { try { # Check if the VHD file exists before trying to access it if (Test-Path $hd.Path) { # Attempt to get the parent path of the VHD $parentPath = (Get-VHD -Path $hd.Path).ParentPath if ($parentPath -eq $VHDXPath) { $dependentVMs.Add($vm) Write-EnhancedLog -Message "Dependent VM: $($vm.Name)" -Level "INFO" break } } else { # Log a warning if the VHDX file does not exist Write-EnhancedLog -Message "Warning: VHDX file not found: $($hd.Path). Skipping VM: $($vm.Name)" -Level "WARNING" } } catch { # Log a warning if there was an error accessing the VHDX file Write-EnhancedLog -Message "Warning: An error occurred while accessing VHDX file: $($hd.Path). Skipping VM: $($vm.Name)" -Level "WARNING" } } } Write-EnhancedLog -Message "Total dependent VMs using VHDX $VHDXPath $($dependentVMs.Count)" -Level "INFO" return $dependentVMs } catch { Write-EnhancedLog -Message "An error occurred while retrieving dependent VMs: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ return [System.Collections.Generic.List[PSObject]]::new() } } End { Write-EnhancedLog -Message "Exiting Get-DependentVMs function" -Level "INFO" } } #EndRegion '.\Public\Get-DependentVMs.ps1' 59 #Region '.\Public\Get-NextVMNamePrefix.ps1' -1 function Get-NextVMNamePrefix { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [PSCustomObject]$Config ) Begin { Write-EnhancedLog -Message "Starting Get-NextVMNamePrefix function" -Level "INFO" } Process { try { Write-EnhancedLog -Message "Retrieving the most recent VM" -Level "INFO" $mostRecentVM = Get-VM | Sort-Object -Property CreationTime -Descending | Select-Object -First 1 $prefixNumber = 0 if ($null -ne $mostRecentVM) { Write-EnhancedLog -Message "Most recent VM found: $($mostRecentVM.Name)" -Level "INFO" if ($mostRecentVM.Name -match '^\d+') { $prefixNumber = [int]$matches[0] Write-EnhancedLog -Message "Extracted prefix number: $prefixNumber" -Level "INFO" } else { Write-EnhancedLog -Message "Most recent VM name does not start with a number" -Level "INFO" } } else { Write-EnhancedLog -Message "No existing VMs found" -Level "INFO" } $nextPrefixNumber = $prefixNumber + 1 $newVMNamePrefix = $Config.VMNamePrefixFormat -f $nextPrefixNumber Write-EnhancedLog -Message "Generated new VM name prefix: $newVMNamePrefix" -Level "INFO" return $newVMNamePrefix } catch { Write-EnhancedLog -Message "An error occurred in Get-NextVMNamePrefix: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ } } End { Write-EnhancedLog -Message "Get-NextVMNamePrefix function completed" -Level "INFO" } } #EndRegion '.\Public\Get-NextVMNamePrefix.ps1' 45 #Region '.\Public\Get-VMConfiguration.ps1' -1 # function Get-ConfigurationFile { # $configPath = "D:\VM\Configs" # Adjust this path as needed # $configs = Get-ChildItem -Path $configPath -Filter "*.psd1" -ErrorAction SilentlyContinue # if ($configs.Count -eq 0) { # Write-EnhancedLog -Message "No configuration files found in $configPath" -Level "ERROR" # return $null # } # Write-Host "`n=== Available VM Configurations ===" -ForegroundColor Cyan # for ($i = 0; $i -lt $configs.Count; $i++) { # Write-Host "$($i + 1). $($configs[$i].BaseName)" # } # Write-Host "`nPlease select a configuration (1-$($configs.Count)):" -NoNewline # $selection = Read-Host # while ([int]$selection -lt 1 -or [int]$selection -gt $configs.Count) { # Write-Host "Invalid selection. Please enter a number between 1 and $($configs.Count):" -ForegroundColor Yellow -NoNewline # $selection = Read-Host # } # return $configs[$selection - 1].FullName # } function Get-VMConfiguration { [CmdletBinding()] param( [Parameter()] [string]$ConfigPath = "D:\VM\Configs", [Parameter()] [ValidateSet('VSCode', 'Notepad', 'None')] [string]$Editor = 'VSCode' ) Begin { Write-EnhancedLog -Message "Starting configuration file selection process" -Level "INFO" # Get all PSD1 files $getPSD1Params = @{ Path = $ConfigPath Filter = "*.psd1" ErrorAction = 'SilentlyContinue' } $configFiles = Get-ChildItem @getPSD1Params if ($configFiles.Count -eq 0) { Write-EnhancedLog -Message "No configuration files found in $ConfigPath" -Level "ERROR" return $null } } Process { try { # Display available configurations Write-Host "`n=== Available VM Configurations ===" -ForegroundColor Cyan Write-Host "----------------------------------------" -ForegroundColor Cyan $configFiles | ForEach-Object -Begin { $index = 1 } -Process { Write-Host ("{0,3}. {1}" -f $index++, $_.BaseName) } Write-Host "----------------------------------------" -ForegroundColor Cyan # Get and validate user selection do { $selection = Read-Host "`nSelect a configuration (1-$($configFiles.Count))" $validSelection = $selection -match "^[1-$($configFiles.Count)]$" if (-not $validSelection) { Write-Host "Invalid selection. Please enter a number between 1 and $($configFiles.Count)" -ForegroundColor Yellow } } while (-not $validSelection) # Load selected configuration $selectedConfig = $configFiles[$selection - 1] $configPath = $selectedConfig.FullName $importParams = @{ Path = $configPath ErrorAction = 'Stop' } $config = Import-PowerShellDataFile @importParams # Display configuration details Write-Host "`nConfiguration Details:" -ForegroundColor Cyan Write-Host "----------------------------------------" -ForegroundColor Cyan $config.GetEnumerator() | Sort-Object Key | ForEach-Object { Write-Host ("{0,-20} = {1}" -f $_.Key, $_.Value) } Write-Host "----------------------------------------" -ForegroundColor Cyan # Confirm or edit configuration do { $proceed = Read-Host "`nProceed with this configuration? (Y)es, (E)dit, or (C)ancel" switch -Regex ($proceed) { '^[Yy]$' { Write-EnhancedLog -Message "Configuration selected: $($selectedConfig.Name)" -Level "INFO" return $config } '^[Ee]$' { Write-EnhancedLog -Message "Opening configuration for editing" -Level "INFO" switch ($Editor) { 'VSCode' { Start-Process code -ArgumentList $configPath -Wait } 'Notepad' { Start-Process notepad -ArgumentList $configPath -Wait } } # Reload configuration after editing $config = Import-PowerShellDataFile @importParams Write-Host "`nUpdated Configuration:" -ForegroundColor Cyan $config.GetEnumerator() | Sort-Object Key | ForEach-Object { Write-Host ("{0,-20} = {1}" -f $_.Key, $_.Value) } } '^[Cc]$' { Write-EnhancedLog -Message "Configuration selection cancelled" -Level "INFO" return $null } default { Write-Host "Invalid input. Please enter Y, E, or C" -ForegroundColor Yellow } } } while ($proceed -notmatch '^[Yy]$|^[Cc]$') } catch { Write-EnhancedLog -Message "Error processing configuration: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ return $null } } End { Write-EnhancedLog -Message "Configuration selection process completed" -Level "INFO" } } #EndRegion '.\Public\Get-VMConfiguration.ps1' 145 #Region '.\Public\Initialize-HyperVServices.ps1' -1 function Initialize-HyperVServices { [CmdletBinding()] param () Begin { Write-EnhancedLog -Message "Starting Initialize-HyperVServices function" -Level "INFO" } Process { try { Write-EnhancedLog -Message "Checking for Hyper-V services" -Level "INFO" if (Get-Service -DisplayName *hyper*) { Write-EnhancedLog -Message "Starting Hyper-V services: vmcompute and vmms" -Level "INFO" Start-Service -Name vmcompute -ErrorAction SilentlyContinue Start-Service -Name vmms -ErrorAction SilentlyContinue Write-EnhancedLog -Message "Hyper-V services started" -Level "INFO" -ForegroundColor ([ConsoleColor]::Green) } else { Write-EnhancedLog -Message "No Hyper-V services found" -Level "WARNING" } } catch { Write-EnhancedLog -Message "An error occurred while starting Hyper-V services: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ } } End { Write-EnhancedLog -Message "Exiting Initialize-HyperVServices function" -Level "INFO" } } #EndRegion '.\Public\Initialize-HyperVServices.ps1' 30 #Region '.\Public\New-CustomVMWithDifferencingDisk.ps1' -1 function New-CustomVMWithDifferencingDisk { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$VMName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$VMFullPath, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$VHDPath, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$SwitchName, [Parameter(Mandatory = $true)] [ValidateRange(512MB, 1024GB)] [int64]$MemoryStartupBytes, [Parameter(Mandatory = $true)] [ValidateRange(512MB, 1024GB)] [int64]$MemoryMinimumBytes, [Parameter(Mandatory = $true)] [ValidateRange(512MB, 1024GB)] [int64]$MemoryMaximumBytes, [Parameter(Mandatory = $true)] [ValidateRange(1, 2)] [int]$Generation, [Parameter()] [string]$ParentVHDPath, [Parameter()] [bool]$UseDifferencing = $false, [Parameter()] [int64]$NewVHDSizeBytes = 100GB ) Begin { Write-EnhancedLog -Message "Starting New-CustomVMWithDifferencingDisk function" -Level "INFO" Log-Params -Params $PSBoundParameters } Process { try { # Check if VM already exists if (Get-VM -Name $VMName -ErrorAction SilentlyContinue) { throw "A VM with name '$VMName' already exists" } # Prepare VM parameters $newVMParams = @{ Generation = $Generation Path = $VMFullPath Name = $VMName MemoryStartupBytes = $MemoryStartupBytes SwitchName = $SwitchName } if ($UseDifferencing) { $newVMParams['NoVHD'] = $true } else { $newVMParams['NewVHDPath'] = $VHDPath $newVMParams['NewVHDSizeBytes'] = $NewVHDSizeBytes } # Create the VM Write-EnhancedLog -Message "Creating new VM '$VMName'" -Level "INFO" $vm = New-VM @newVMParams # Configure VM Memory $memoryParams = @{ VMName = $VMName DynamicMemoryEnabled = $true MinimumBytes = $MemoryMinimumBytes MaximumBytes = $MemoryMaximumBytes StartupBytes = $MemoryStartupBytes } Write-EnhancedLog -Message "Configuring VM memory settings" -Level "INFO" Set-VMMemory @memoryParams if ($UseDifferencing) { Write-EnhancedLog -Message "Creating differencing disk" -Level "INFO" $vhdParams = @{ Path = $VHDPath ParentPath = $ParentVHDPath Differencing = $true } New-VHD @vhdParams Write-EnhancedLog -Message "Attaching differencing disk to VM" -Level "INFO" Add-VMHardDiskDrive -VMName $VMName -Path $VHDPath } # Verify VM Configuration $vmCheck = Get-VM -Name $VMName -ErrorAction Stop if ($vmCheck.State -eq 'Off') { Write-EnhancedLog -Message "VM '$VMName' created successfully" -Level "INFO" -ForegroundColor ([ConsoleColor]::Green) return $true } else { throw "VM is in unexpected state: $($vmCheck.State)" } } catch { Write-EnhancedLog -Message "Failed to create VM: $($_.Exception.Message)" -Level "ERROR" # Cleanup on failure try { if (Get-VM -Name $VMName -ErrorAction SilentlyContinue) { Write-EnhancedLog -Message "Cleaning up failed VM" -Level "WARNING" Remove-VM -Name $VMName -Force -ErrorAction Stop } if (Test-Path $VHDPath) { Write-EnhancedLog -Message "Cleaning up VHD" -Level "WARNING" Remove-Item -Path $VHDPath -Force -ErrorAction Stop } } catch { Write-EnhancedLog -Message "Cleanup after failure encountered additional errors: $($_.Exception.Message)" -Level "ERROR" } Handle-Error -ErrorRecord $_ return $false } } End { Write-EnhancedLog -Message "Completed New-CustomVMWithDifferencingDisk function" -Level "INFO" } } #EndRegion '.\Public\New-CustomVMWithDifferencingDisk.ps1' 142 #Region '.\Public\New-DifferencingVHDX.ps1' -1 function New-DifferencingVHDX { param( [string]$ParentPath, [string]$ChildPath ) Write-EnhancedLog -Message "Creating differencing VHDX at $ChildPath" -Level "INFO" try { New-VHD -Path $ChildPath -ParentPath $ParentPath -Differencing Write-EnhancedLog -Message "Differencing VHDX created successfully" -Level "INFO" return $true } catch { Write-EnhancedLog -Message "Failed to create differencing VHDX: $_" -Level "ERROR" return $false } } #EndRegion '.\Public\New-DifferencingVHDX.ps1' 18 #Region '.\Public\Parse-Size.ps1' -1 function Parse-Size { <# .SYNOPSIS Parses a size string and converts it to bytes. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$Size ) Begin { Write-EnhancedLog -Message "Starting Parse-Size function" -Level "INFO" Log-Params -Params @{ Size = $Size } } Process { try { Write-EnhancedLog -Message "Parsing size string: $Size" -Level "INFO" switch -regex ($Size) { '^(\d+)\s*KB$' { $result = [int64]$matches[1] * 1KB Write-EnhancedLog -Message "Parsed size: $Size to $result bytes" -Level "INFO" return $result } '^(\d+)\s*MB$' { $result = [int64]$matches[1] * 1MB Write-EnhancedLog -Message "Parsed size: $Size to $result bytes" -Level "INFO" return $result } '^(\d+)\s*GB$' { $result = [int64]$matches[1] * 1GB Write-EnhancedLog -Message "Parsed size: $Size to $result bytes" -Level "INFO" return $result } '^(\d+)\s*TB$' { $result = [int64]$matches[1] * 1TB Write-EnhancedLog -Message "Parsed size: $Size to $result bytes" -Level "INFO" return $result } default { Write-EnhancedLog -Message "Invalid size format: $Size" -Level "ERROR" throw "Invalid size format: $Size" } } } catch { Write-EnhancedLog -Message "An error occurred while parsing size: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ } } End { Write-EnhancedLog -Message "Exiting Parse-Size function" -Level "INFO" } } #EndRegion '.\Public\Parse-Size.ps1' 56 #Region '.\Public\Show-VMCreationMenu.ps1' -1 function Show-VMCreationMenu { Write-Host "`n=== VM Creation Options ===" -ForegroundColor Cyan Write-Host "1. Create VM with new VHDX disk" Write-Host "2. Create VM with differencing VHDX disk" Write-Host "`nPlease select an option (1 or 2):" -NoNewline $choice = Read-Host while ($choice -notin '1', '2') { Write-Host "Invalid selection. Please enter 1 or 2:" -ForegroundColor Yellow -NoNewline $choice = Read-Host } return $choice } #EndRegion '.\Public\Show-VMCreationMenu.ps1' 15 #Region '.\Public\Shutdown-DependentVMs.ps1' -1 # function Shutdown-DependentVMs { # [CmdletBinding()] # param ( # [Parameter(Mandatory = $true)] # [string]$VHDXPath # ) # Process { # try { # $dependentVMs = Get-DependentVMs -VHDXPath $VHDXPath # foreach ($vm in $dependentVMs) { # Write-EnhancedLog -Message "Shutting down VM: $($vm.Name)" -Level "INFO" # Stop-VM -Name $vm.Name -Force -ErrorAction Stop # } # } catch { # Write-EnhancedLog -Message "An error occurred while shutting down dependent VMs: $($_.Exception.Message)" -Level "ERROR" # Handle-Error -ErrorRecord $_ # } # } # } #EndRegion '.\Public\Shutdown-DependentVMs.ps1' 21 #Region '.\Public\Start-VMEnhanced.ps1' -1 function Start-VMEnhanced { <# .SYNOPSIS Starts the specified VM if it exists and is not already running. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$VMName ) Begin { Write-EnhancedLog -Message "Starting Start-VMEnhanced function" -Level "INFO" Log-Params -Params @{ VMName = $VMName } } Process { try { Write-EnhancedLog -Message "Validating if VM $VMName exists" -Level "INFO" if (-not (Validate-VMExists -VMName $VMName)) { Write-EnhancedLog -Message "VM $VMName does not exist. Exiting function." -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red) return } Write-EnhancedLog -Message "Checking if VM $VMName is already running" -Level "INFO" if (Validate-VMStarted -VMName $VMName) { Write-EnhancedLog -Message "VM $VMName is already running." -Level "INFO" -ForegroundColor ([ConsoleColor]::Yellow) } else { Write-EnhancedLog -Message "Starting VM $VMName" -Level "INFO" Start-VM -Name $VMName -ErrorAction Stop Write-EnhancedLog -Message "VM $VMName has been started successfully." -Level "INFO" -ForegroundColor ([ConsoleColor]::Green) } } catch { Write-EnhancedLog -Message "An error occurred while starting the VM $VMName. $($_.Exception.Message)" -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red) Handle-Error -ErrorRecord $_ } } End { Write-EnhancedLog -Message "Exiting Start-VMEnhanced function" -Level "INFO" } } #EndRegion '.\Public\Start-VMEnhanced.ps1' 43 #Region '.\Public\Validate-ISOAdded.ps1' -1 function Validate-ISOAdded { <# .SYNOPSIS Validates if the specified ISO file is added to the VM. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$VMName, [Parameter(Mandatory = $true)] [string]$InstallMediaPath ) Begin { Write-EnhancedLog -Message "Starting Validate-ISOAdded function" -Level "INFO" Log-Params -Params @{ VMName = $VMName; InstallMediaPath = $InstallMediaPath } } Process { try { Write-EnhancedLog -Message "Retrieving DVD drive information for VM: $VMName" -Level "INFO" $dvdDrive = Get-VMDvdDrive -VMName $VMName -ErrorAction SilentlyContinue if ($dvdDrive -and ($dvdDrive.Path -eq $InstallMediaPath)) { Write-EnhancedLog -Message "ISO is correctly added to VM: $VMName" -Level "INFO" -ForegroundColor Green return $true } else { Write-EnhancedLog -Message "ISO is not added to VM: $VMName" -Level "WARNING" -ForegroundColor Red return $false } } catch { Write-EnhancedLog -Message "An error occurred while validating ISO addition: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ return $false } } End { Write-EnhancedLog -Message "Exiting Validate-ISOAdded function" -Level "INFO" } } #EndRegion '.\Public\Validate-ISOAdded.ps1' 43 #Region '.\Public\Validate-VHDMount.ps1' -1 function Validate-VHDMount { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$VHDXPath ) Begin { Write-EnhancedLog -Message "Starting Validate-VHDMount function" -Level "INFO" Log-Params -Params @{ VHDXPath = $VHDXPath } } Process { try { Write-EnhancedLog -Message "Checking if the VHDX is mounted: $VHDXPath" -Level "INFO" $vhd = Get-VHD -Path $VHDXPath -ErrorAction SilentlyContinue if ($null -eq $vhd) { Write-EnhancedLog -Message "Get-VHD did not return any information for the path: $VHDXPath" -Level "INFO" -ForegroundColor Red return $false } Write-EnhancedLog -Message "Get-VHD output: $($vhd | Format-List | Out-String)" -Level "DEBUG" if ($vhd.Attached) { Write-EnhancedLog -Message "VHDX is mounted: $VHDXPath" -Level "INFO" -ForegroundColor Green return $true } else { Write-EnhancedLog -Message "VHDX is not mounted: $VHDXPath" -Level "INFO" -ForegroundColor Red return $false } } catch { Write-EnhancedLog -Message "An error occurred while validating VHD mount status: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ return $false } } End { Write-EnhancedLog -Message "Exiting Validate-VHDMount function" -Level "INFO" } } #EndRegion '.\Public\Validate-VHDMount.ps1' 43 #Region '.\Public\Validate-VMExists.ps1' -1 function Validate-VMExists { <# .SYNOPSIS Validates if a VM with the specified name exists. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$VMName ) Begin { Write-EnhancedLog -Message "Starting Validate-VMExists function" -Level "INFO" Log-Params -Params @{ VMName = $VMName } } Process { try { Write-EnhancedLog -Message "Checking existence of VM: $VMName" -Level "INFO" $vm = Get-VM -Name $VMName -ErrorAction Stop Write-EnhancedLog -Message "VM $VMName exists." -Level "INFO" -ForegroundColor ([ConsoleColor]::Green) return $true } catch { Write-EnhancedLog -Message "VM $VMName does not exist. $($_.Exception.Message)" -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red) Handle-Error -ErrorRecord $_ return $false } } End { Write-EnhancedLog -Message "Exiting Validate-VMExists function" -Level "INFO" } } #EndRegion '.\Public\Validate-VMExists.ps1' 34 #Region '.\Public\Validate-VMStarted.ps1' -1 function Validate-VMStarted { <# .SYNOPSIS Validates if the specified VM is started (running). #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$VMName ) Begin { Write-EnhancedLog -Message "Starting Validate-VMStarted function" -Level "INFO" Log-Params -Params @{ VMName = $VMName } } Process { try { Write-EnhancedLog -Message "Checking state of VM: $VMName" -Level "INFO" $vm = Get-VM -Name $VMName -ErrorAction Stop if ($vm.State -eq 'Running') { Write-EnhancedLog -Message "VM $VMName is running." -Level "INFO" return $true } else { Write-EnhancedLog -Message "VM $VMName is not running." -Level "WARNING" return $false } } catch { Write-EnhancedLog -Message "Failed to check the state of VM $VMName. $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ } } End { Write-EnhancedLog -Message "Exiting Validate-VMStarted function" -Level "INFO" } } #EndRegion '.\Public\Validate-VMStarted.ps1' 42 |