Public/Install-WindowsFromWim.ps1

function Install-WindowsFromWim
{
    <#
    .Synopsis
    Populate a Disk it from a WIM
    .DESCRIPTION
    This command will Format the disk and install Windows from a WIM/ISO
    You must supply the path to a valid WIM/ISO. You should also
    include the index number for the Windows Edition to install.
    .EXAMPLE
    Install-WindowsFromWim -DiskNumber 0 -WimPath d:\Source\install.wim -NoRecoveryTools -DiskLayout UEFI
    Installs Windows to Disk Number 0 with no Recovery Partition from Index 1
    .EXAMPLE
    Install-WindowsFromWim -DiskNumber 0 -WimPath d:\Source\install.wim -index 3 -force -DiskLayout UEFI
    Installs Windows to Disk Number 0 from with recoery partition from index 3 and overwrits any existing data.
    #>

    [CmdletBinding(SupportsShouldProcess = $true,
        PositionalBinding = $false,
        ConfirmImpact = 'Medium')]
    Param
    (
        # Disk number, disk must exist
        [Parameter(Position = 0, Mandatory,
            HelpMessage = 'Disk Number based on Get-Disk')]
        [ValidateNotNullorEmpty()]
        [ValidateScript( {
                if (Get-Disk -Number $_)
                {
                    $true
                }
                else
                {
                    Throw "Disk number $_ does not exist."
                }
            })]
        [string]$DiskNumber,

        # Specifies whether to build the image for BIOS (MBR), UEFI (GPT), or WindowsToGo (MBR).
        # Generation 1 VMs require BIOS (MBR) images. Generation 2 VMs require UEFI (GPT) images.
        # Windows To Go images will boot in UEFI or BIOS
        [Parameter(Mandatory = $true)]
        [Alias('Layout')]
        [string]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('BIOS', 'UEFI', 'WindowsToGo')]
        $DiskLayout,

        # Skip creating the Recovery Environment Tools Partition.
        [switch]$NoRecoveryTools,

        # System (boot loader) Partition Size (Default : 260MB)
        [int]$SystemSize,

        # MS Reserved Partition Size (Default : 128MB)
        [int]$ReservedSize,

        # Recovery Tools Partition Size (Default : 905MB)
        [int]$RecoverySize,

        # Force the overwrite of existing files
        [switch]$force,

        # Path to WIM or ISO used to populate VHDX
        [parameter(Position = 1, Mandatory = $true,
            HelpMessage = 'Enter the path to the WIM/ISO file')]
        [ValidateScript( {
                Test-Path -Path (Get-FullFilePath -Path $_ )
            })]
        [string]$SourcePath,

        # Index of image inside of WIM (Default 1)
        [int]$Index = 1,

        # Path to file to copy inside of VHD(X) as C:\unattent.xml
        [ValidateScript( {
                if ($_)
                {
                    Test-Path -Path $_
                }
                else
                {
                    $true
                }
            })]
        [string]$Unattend,

        # Native Boot does not have the boot code inside the VHD(x) it must exist on the physical disk.
        [switch]$NativeBoot,

        # Features to turn on (in DISM format)
        [ValidateNotNullOrEmpty()]
        [string[]]$Feature,

        # Feature to remove (in DISM format)
        [ValidateNotNullOrEmpty()]
        [string[]]$RemoveFeature,

        # Feature Source path. If not provided, all ISO and WIM images in $sourcePath searched
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                (Test-Path -Path $(Resolve-Path $_) -or ($_ -eq 'NONE') )
            })]
        [string]$FeatureSource,

        # Feature Source index. If the source is a .wim provide an index Default =1
        [int]$FeatureSourceIndex = 1,

        # Path to drivers to inject
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                foreach ($Path in $_)
                {
                    Test-Path -Path $(Resolve-Path $Path)
                }
            })]
        [string[]]$Driver,

        # Add payload for all removed features
        [switch]$AddPayloadForRemovedFeature,

        # Path of packages to install via DSIM
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                foreach ($Path in $_)
                {
                    Test-Path -Path $(Resolve-Path $Path)
                }
            })]
        [string[]]$Package,
        # Files/Folders to copy to root of Windows Drive (to place files in directories mimic the direcotry structure off of C:\)
        [ValidateNotNullOrEmpty()]
        [ValidateScript( {
                foreach ($Path in $_)
                {
                    Test-Path -Path $(Resolve-Path $Path)
                }
            })]
        [string[]]$filesToInject

    )
    $SourcePath = $SourcePath | Get-FullFilePath

    if ($pscmdlet.ShouldProcess("[$($MyInvocation.MyCommand)] : Overwrite partitions on disk [$DiskNumber] with content of [$SourcePath]",
            "Overwrite partitions on disk [$DiskNumber] with contentce of [$SourcePath]? ",
            'Overwrite WARNING!'))
    {
        if ((Get-Disk -Number $DiskNumber | Get-Partition -ErrorAction SilentlyContinue) -Or $force -Or $pscmdlet.ShouldContinue('Are you sure? Any existin data will be lost!', 'Warning'))
        {
            $ParametersToPass = @{ }
            foreach ($key in ('Whatif', 'Verbose', 'Debug'))
            {
                if ($PSBoundParameters.ContainsKey($key))
                {
                    $ParametersToPass[$key] = $PSBoundParameters[$key]
                }
            }

            $InitializeDiskPartitionParam = @{
                'DiskNumber' = $DiskNumber
                'force'      = $true
                'DiskLayout' = $DiskLayout
            }
            if ($NoRecoveryTools)
            {
                $InitializeDiskPartitionParam.add('NoRecoveryTools', $true)
            }
            if ($Dynamic)
            {
                $InitializeDiskPartitionParam.add('Dynamic', $true)
            }
            if ($SystemSize) { $InitializeDiskPartitionParam.add('SystemSize', $SystemSize) }
            if ($ReservedSize) { $InitializeDiskPartitionParam.add('ReservedSize', $ReservedSize) }
            if ($RecoverySize) { $InitializeDiskPartitionParam.add('RecoverySize', $RecoverySize) }

            $SetDiskPartitionParam = @{
                'SourcePath' = $SourcePath
                'DiskNumber' = $DiskNumber
                'Index'      = $Index
                'force'      = $true
                'Confirm'    = $false
            }
            if ($Unattend)
            {
                $SetDiskPartitionParam.add('Unattend', $Unattend)
            }
            if ($NativeBoot)
            {
                $SetDiskPartitionParam.add('NativeBoot', $NativeBoot)
            }
            if ($Feature)
            {
                $SetDiskPartitionParam.add('Feature', $Feature)
            }
            if ($RemoveFeature)
            {
                $SetDiskPartitionParam.add('RemoveFeature', $RemoveFeature)
            }
            if ($FeatureSource)
            {
                $SetDiskPartitionParam.add('FeatureSource', $FeatureSource)
            }
            if ($FeatureSourceIndex)
            {
                $SetDiskPartitionParam.add('FeatureSourceIndex', $FeatureSourceIndex)
            }
            if ($AddPayloadForRemovedFeature)
            {
                $SetDiskPartitionParam.add('AddPayloadForRemovedFeature', $AddPayloadForRemovedFeature)
            }
            if ($Driver)
            {
                $SetDiskPartitionParam.add('Driver', $Driver)
            }
            if ($Package)
            {
                $SetDiskPartitionParam.add('Package', $Package)
            }
            if ($filesToInject)
            {
                $SetDiskPartitionParam.add('filesToInject', $filesToInject)
            }
            Write-Verbose -Message "[$($MyInvocation.MyCommand)] : InitializeDiskPartitionParam"
            Write-Verbose -Message ($InitializeVHDPartitionParam | Out-String)
            Write-Verbose -Message "[$($MyInvocation.MyCommand)] : SetDiskPartitionParam"
            Write-Verbose -Message ($SetDiskPartitionParam | Out-String)
            Write-Verbose -Message "[$($MyInvocation.MyCommand)] : ParametersToPass"
            Write-Verbose -Message ($ParametersToPass | Out-String)

            Try
            {
                Initialize-DiskPartition @InitializeDiskPartitionParam @ParametersToPass
                Set-DiskPartition @SetDiskPartitionParam @ParametersToPass
            }
            Catch
            {
                throw "$($_.Exception.Message) at $($_.Exception.InvocationInfo.ScriptLineNumber)"
            }
        }
    }
}