modules/Initialize-WindowsVM.psm1

Set-StrictMode -Version Latest


. "$PSSCriptRoot\..\detail\Assert-RunningAsAdmin.ps1"
. "$PSSCriptRoot\..\detail\Invoke-WithResource.ps1"


function Initialize-WindowsVM {
    [OutputType([HyperV.Tools.VM])]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName = "WIM x VM x ImageName")]
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName = "WIM x VM x ImageIndex")]
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName = "ISO x VM x ImageName")]
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName = "ISO x VM x ImageIndex")]
        [HyperV.Tools.VM]
        [Alias("VM")]
            $InputObject,
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName = "WIM x VMName x ImageName")]
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName = "WIM x VMName x ImageIndex")]
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName = "ISO x VMName x ImageName")]
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName = "ISO x VMName x ImageIndex")]
        [string]
            $VMName,
        [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "WIM x VM x ImageName")]
        [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "WIM x VM x ImageIndex")]
        [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "WIM x VMName x ImageName")]
        [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "WIM x VMName x ImageIndex")]
        [string]
            $WIMPath,
        [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "ISO x VM x ImageName")]
        [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "ISO x VM x ImageIndex")]
        [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "ISO x VMName x ImageName")]
        [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "ISO x VMName x ImageIndex")]
        [string]
            $ISOPath,
        [Parameter(Mandatory = $true, Position = 2, ParameterSetName = "WIM x VM x ImageName")]
        [Parameter(Mandatory = $true, Position = 2, ParameterSetName = "ISO x VM x ImageName")]
        [Parameter(Mandatory = $true, Position = 2, ParameterSetName = "WIM x VMName x ImageName")]
        [Parameter(Mandatory = $true, Position = 2, ParameterSetName = "ISO x VMName x ImageName")]
        [string]
            $ImageName,
        [Parameter(Mandatory = $true, Position = 2, ParameterSetName = "WIM x VM x ImageIndex")]
        [Parameter(Mandatory = $true, Position = 2, ParameterSetName = "ISO x VM x ImageIndex")]
        [Parameter(Mandatory = $true, Position = 2, ParameterSetName = "WIM x VMName x ImageIndex")]
        [Parameter(Mandatory = $true, Position = 2, ParameterSetName = "ISO x VMName x ImageIndex")]
        [int]
            $ImageIndex,
        [Parameter(Mandatory = $false, Position = 3)]
        [AllowEmptyString()]
        [string]
            $UnattendXMLContents
    )

    Assert-RunningAsAdmin
    
    $parameters_to_pass_through = [hashtable]$PSBoundParameters

    #region Handle VM / VMName parameters
    if ($PSCmdlet.ParameterSetName -like "* x VM x *") {
        $VM = $InputObject
    } elseif ($PSCmdlet.ParameterSetName -like "* x VMName x *") {
        $VM = Hyper-V\Get-VM -Name $VMName
    } else {
        throw "Unknown parameter set name: $($PSCmdlet.ParameterSetName)"
    }
    
    $parameters_to_pass_through.Remove("InputObject")
    $parameters_to_pass_through.Remove("VMName")
    #endregion

    #region Handle ISOPath parameter and prepare ScriptBlocks for `Invoke-WithResource`
    if ($PSCmdlet.ParameterSetName -like "ISO x *") {
        $mount_ISO_block = {
            [Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssignments', '')]
            $mounted_ISO = Mount-DiskImage -ImagePath $ISOPath
            $mounted_ISO_drive_letter = ($mounted_iso | Get-Volume | Select-Object -ExpandProperty "DriveLetter") + ":\"

            [Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssignments', '')]
            $WIMPath = Join-Path -Path $mounted_ISO_drive_letter -ChildPath "sources\install.wim"
        }
        $dismount_ISO_block = {
            $mounted_ISO | Dismount-DiskImage | Out-Null
            Remove-Variable -Name "mounted_ISO"
        }
    }

    $parameters_to_pass_through.Remove("ISOPath")
    $parameters_to_pass_through.Remove("WIMPath")
    #endregion

    Invoke-WithResource -InitBlock $mount_ISO_block -CleanupBlock $dismount_ISO_block -BodyBlock {
        initialize_Windows_VM -VM $VM -WIMPath $WIMPath @parameters_to_pass_through
    }

    return $VM
}

function initialize_Windows_VM {
    [CmdletBinding(PositionalBinding = $false)]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [HyperV.Tools.VM]
        [Alias("VM")]
            $InputObject,
        [Parameter(Mandatory = $true)]
        [string]
            $WIMPath,
        [Parameter(Mandatory = $true, ParameterSetName = "ImageName")]
        [string]
            $ImageName,
        [Parameter(Mandatory = $true, ParameterSetName = "ImageIndex")]
        [int]
            $ImageIndex,
        [Parameter(Mandatory = $false)]
        [AllowEmptyString()]
        [string]
            $UnattendXMLContents
    )

    $VM = $InputObject

    $HDD_count = $VM.HardDrives.Count

    if ($HDD_count -ne 1) {
        throw "Virtual Machine must have exactly 1 hard drive but has $HDD_count drives."
    }

    $VHD_path = $VM.HardDrives[0].Path

    $image_info = Get-WindowsImage -ImagePath $WIMPath

    if ($PSCmdlet.ParameterSetName -eq "ImageName") {
        $image_index = $image_info | Where-Object -Property "ImageName" -eq $ImageName |
                                     Select-Object -ExpandProperty "ImageIndex"
        
        if ($null -eq $image_index) {
            throw "Image '$ImageName' not found in WIM file."
        }
    } elseif ($PSCmdlet.ParameterSetName -eq "ImageIndex") {
        $image_index = $image_info | Where-Object -Property "ImageIndex" -eq $ImageIndex |
                                     Select-Object -ExpandProperty "ImageIndex"

        if ($null -eq $image_index) {
            throw "Image #'$ImageIndex' not found in WIM file."
        }
    } else {
        throw "Unknown parameter set name: $($PSCmdlet.ParameterSetName)"
    }

    Initialize-VHDFromWIM -Path $VHD_path -WIMPath $WIMPath -ImageIndex $image_index -UnattendXMLContents $UnattendXMLContents
}