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