private/Software/Install-MicrosoftHyperV.ps1

<#
.SYNOPSIS
Enables Hyper-V on Windows 11.
 
.DESCRIPTION
Checks the current Hyper-V optional feature state and enables Hyper-V when
needed using Enable-WindowsOptionalFeature.
 
.OUTPUTS
System.Management.Automation.PSCustomObject
 
.EXAMPLE
Install-MicrosoftHyperV
 
Enables Hyper-V on Windows 11 if it is not already enabled.
 
.NOTES
Author: David Segura
Company: Recast Software
This function is supported only on Windows 11 and requires Administrator rights.
Change Summary:
- Initial implementation.
 
.LINK
https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/quick-start/enable-hyper-v
#>

function Install-MicrosoftHyperV {
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    [OutputType([pscustomobject])]
    param ()

    if (-not $IsWindows) {
        throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Install-MicrosoftHyperV is supported only on Windows 11."
    }

    $os = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction SilentlyContinue
    if (-not $os -or $os.Caption -notlike '*Windows 11*') {
        throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Install-MicrosoftHyperV is supported only on Windows 11."
    }

    $currentPrincipal = [Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
    if (-not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
        throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Install-MicrosoftHyperV requires Administrator rights. Re-run PowerShell as Administrator and try again."
    }

    $featureName = 'Microsoft-Hyper-V-All'
    if ($global:OSDeployModule -and $global:OSDeployModule.Software.hyperv -and $global:OSDeployModule.Software.hyperv.featurename) {
        $featureName = [string]$global:OSDeployModule.Software.hyperv.featurename
    }

    $getFeatureCommand = Get-Command -Name 'Get-WindowsOptionalFeature' -ErrorAction SilentlyContinue
    $enableFeatureCommand = Get-Command -Name 'Enable-WindowsOptionalFeature' -ErrorAction SilentlyContinue
    if (-not $getFeatureCommand -or -not $enableFeatureCommand) {
        throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Get-WindowsOptionalFeature and Enable-WindowsOptionalFeature are required but were not found."
    }

    $featureState = Get-WindowsOptionalFeature -Online -FeatureName $featureName -ErrorAction Stop

    if ($featureState.State -in @('Enabled', 'EnablePending')) {
        Write-Host "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Hyper-V is already enabled (state: $($featureState.State))." -ForegroundColor Green
        return [pscustomobject]@{
            FeatureName    = $featureName
            PreviousState  = $featureState.State
            CurrentState   = $featureState.State
            WasEnabled     = $false
            SkippedInstall = $false
            RestartNeeded  = ($featureState.RestartNeeded -eq $true)
        }
    }

    if ($PSCmdlet.ShouldProcess($featureName, 'Enable Hyper-V optional feature')) {
        $enableResult = Enable-WindowsOptionalFeature -Online -FeatureName $featureName -All -NoRestart -ErrorAction Stop
        $updatedState = (Get-WindowsOptionalFeature -Online -FeatureName $featureName -ErrorAction Stop).State

        Write-Host "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Hyper-V enable operation completed (state: $updatedState)." -ForegroundColor Green

        if ($enableResult.RestartNeeded -eq $true) {
            Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] A system restart is required before Hyper-V will be operational."
        }

        return [pscustomobject]@{
            FeatureName    = $featureName
            PreviousState  = $featureState.State
            CurrentState   = $updatedState
            WasEnabled     = $true
            SkippedInstall = $false
            RestartNeeded  = ($enableResult.RestartNeeded -eq $true)
        }
    }

    [pscustomobject]@{
        FeatureName    = $featureName
        PreviousState  = $featureState.State
        CurrentState   = $featureState.State
        WasEnabled     = $false
        SkippedInstall = $true
        RestartNeeded  = ($featureState.RestartNeeded -eq $true)
    }
}