modules/Initialize-DiskFromWIM.psm1

Set-StrictMode -Version "Latest"


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


function Initialize-DiskFromWIM {
    [OutputType([CimInstance])]
    [OutputType("Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_Disk")]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [CimInstance]
        [ValidateCimInstance("MSFT_Disk", "root\Microsoft\Windows\Storage")]
        [Alias("Disk")]
            $InputObject,
        [Parameter(Mandatory = $true)]
        [string]
            $WIMPath,
        [Parameter(Mandatory = $true)]
        [int]
            $ImageIndex,
        [Parameter(Mandatory = $false)]
        [ValidateSet("GPT", "MBR")]
        [string]
            $PartitionStyle = "GPT",
        [Parameter(Mandatory = $false)]
        [AllowEmptyString()]
        [string]
            $UnattendXMLContents,
        [Parameter(Mandatory = $false)]
        [Switch]
            $CreateRecoveryPartition
    )

    Assert-RunningAsAdmin

    # Any error should make us stop.
    $ErrorActionPreference = "Stop"

    $target_disk = $InputObject

    Set-Variable -Option "Constant" -Name "GPT_PARTITION_TYPE_ESP"      -Value "{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}"
    Set-Variable -Option "Constant" -Name "GPT_PARTITION_TYPE_MSR"      -Value "{e3c9e316-0b5c-4db8-817d-f92df00215ae}"
    Set-Variable -Option "Constant" -Name "GPT_PARTITION_TYPE_RECOVERY" -Value "{de94bba4-06d1-4d40-a16a-bfd50179d6ac}"

    Set-Variable -Option "Constant" -Name "MSR_PARTITION_SIZE"      -Value 16MB
    Set-Variable -Option "Constant" -Name "BOOT_PARTITION_SIZE"     -Value 350MB
    Set-Variable -Option "Constant" -Name "RECOVERY_PARTITION_SIZE" -Value 800MB

    #region Create partitions
    $target_disk | Initialize-Disk -PartitionStyle $PartitionStyle

    if ($PartitionStyle -eq "GPT") {
        $ESP = $target_disk | New-Partition -AssignDriveLetter -Size $BOOT_PARTITION_SIZE -GptType $GPT_PARTITION_TYPE_ESP
        $ESP | Format-Volume -FileSystem "FAT32" -NewFileSystemLabel "System" | Out-Null

        $target_disk | New-Partition -AssignDriveLetter -Size $MSR_PARTITION_SIZE -GptType $GPT_PARTITION_TYPE_MSR | Out-Null

        $system_drive_letter = $ESP.DriveLetter + ":\"
        Write-Verbose "ESP drive letter: $system_drive_letter"

        if ($CreateRecoveryPartition -eq $true) {
            $recovery_partition = $target_disk | New-Partition -AssignDriveLetter -Size $RECOVERY_PARTITION_SIZE
        }
    } else {
        $system_partition = $target_disk | New-Partition -AssignDriveLetter -Size $BOOT_PARTITION_SIZE -MbrType "IFS" -IsActive
        $system_partition | Format-Volume -FileSystem "NTFS" -NewFileSystemLabel "System" | Out-Null

        $system_drive_letter = $system_partition.DriveLetter + ":\"
        Write-Verbose "MBR System drive letter: $system_drive_letter"

        if ($CreateRecoveryPartition -eq $true) {
            $recovery_partition = $target_disk | New-Partition -AssignDriveLetter -Size $RECOVERY_PARTITION_SIZE -MbrType "IFS"
        }
    }

    $recovery_partition | Format-Volume -FileSystem "NTFS" -NewFileSystemLabel "Recovery" | Out-Null
    $recovery_partition | Set-Partition -NoDefaultDriveLetter $true -IsHidden $true
    $recovery_drive_letter = $recovery_partition.DriveLetter + ":\"

    # We didn't pick the correct parition type using the -GptType parameter when calling New-Partition because
    # then the call to Set-Partition above (-NoDefaultDriveLetter $true -IsHidden $true) would fail.
    # So we create the partition, then set it to hidden etc. and then set the partition type to recovery.
    if ($PartitionStyle -eq "GPT") {
        $recovery_partition | Set-Partition -GptType $GPT_PARTITION_TYPE_RECOVERY
    }

    $windows_partition = $target_disk | New-Partition -AssignDriveLetter -UseMaximumSize
    $windows_partition | Format-Volume -FileSystem "NTFS" -NewFileSystemLabel "Windows" | Out-Null

    $windows_drive_letter = $windows_partition.DriveLetter + ":\"
    Write-Verbose "Windows drive letter: $windows_drive_letter"
    #endregion

    Expand-WindowsImage -ImagePath $WIMPath -Index $ImageIndex -ApplyPath $windows_drive_letter | Out-Null

    #region BCDBoot
    $target_Windows_path = Join-Path -Path $windows_drive_letter -ChildPath "Windows"
    $BCDBoot_path = Join-Path -Path $Env:SystemRoot -ChildPath "System32\bcdboot.exe"
    $BCDBoot_args = "$target_Windows_path /S $system_drive_letter"
    if ($PartitionStyle -eq "MBR") {
        $BCDBoot_args += " /F BIOS"
    }
    else {
        $BCDBoot_args += " /F UEFI"
    }

    Start-Process -Wait -WindowStyle "Hidden" -FilePath $BCDBoot_path -ArgumentList $BCDBoot_args
    #endregion

    if ($UnattendXMLContents -ne "") {
        $Panther_path = Join-Path -Path $target_Windows_path -ChildPath "Panther\"
        New-Item -ItemType "Directory" -Path $Panther_path | Out-Null

        $unattendXML_path = Join-Path -Path $Panther_path -ChildPath "unattend.xml"
        Set-Content -Path $unattendXML_path -Value $UnattendXMLContents
    }

    #region ReAgentc
    if ($CreateRecoveryPartition -eq $true) {
        $source_WinRE_WIM     = Join-Path -Path $target_Windows_path   -ChildPath "System32\Recovery\WinRE.wim"
        $dest_WinRE_directory = Join-Path -Path $recovery_drive_letter -ChildPath "Recovery\WindowsRE"

        $ReAgentc_path = Join-Path -Path $Env:SystemRoot -ChildPath "System32\ReAgentc.exe"
        $ReAgentc_args = "/setreimage /path $dest_WinRE_directory /target $target_Windows_path"

        New-Item -ItemType "Directory" -Path $dest_WinRE_directory | Out-Null
        Copy-Item -Path $source_WinRE_WIM -Destination $dest_WinRE_directory

        Start-Process -Wait -WindowStyle "Hidden" -FilePath $ReAgentc_path -ArgumentList $ReAgentc_args
    }
    #endregion

    Write-Output $target_disk
}