New-VHDfromWim.ps1

function New-VHDfromWim {
  <#
      .SYNOPSIS
      Creates a VHD from a WIM-Image
 
      .DESCRIPTION
      This simple Function converts a WIM-Image into a fully functional VHD. It uses the DISM- and
      Hyper-V Modules.
 
      .EXAMPLE
      New-VHDfromWim -isoImagePath Value -vhdPath Value -diskSize Value -disktype Value -partitionStyle Value
      Describe what this call does
 
      .EXAMPLE
      New-VHDfromWim -Edition Value
      Describe what this call does
 
      .EXAMPLE
      New-VHDfromWim -ImageIndex Value
      Describe what this call does
 
      .NOTES
      Author: Holger Voges
      Version: 1.0
      Date: 18.03.2017
 
      .LINK
      https://www.netz-weise-it.training/
  #>


  param(
    # Path to the Windows Iso Image
    [ValidateScript({ If ( Test-Path -Path $_ -PathType Leaf )
          { $true }
          Else { Throw "$_ is not a valid File." } 
    })]
    [parameter(mandatory=$true)]
    [string]$ImagePath,

    # The Image-Name to install
    [parameter(mandatory=$true,
    ParameterSetName='ImageByName')]
    [string]$Edition,

    # The Index of the Image to install. You can either type the name or the index of the Image.
    [parameter(mandatory=$true,
    ParameterSetName='ImageByIndex')]
    [Alias('Index')]
    [int]$ImageIndex,

    # The Path including the filename of the VHD to create
    [ValidateScript({ If ( Test-Path -Path $_ -PathType Leaf )
          { Throw "File $_ already exists. Please delete the file or chose another Path." }
          Else { $true }                   
    })]
    [parameter(mandatory=$true)]
    [string]$vhdPath,

    # The Size of the VHD
    [int64]$diskSize = 60GB,

    # The type of the disk. Fixed uses the full disksize of the vhd at disk-creation.
    [Validateset('Dynamic','Fixed')]
    [string]$disktype = 'Dynamic',

    # The Disk Partitionstyle. Use GPT for Generation 2 VMs.
    [ValidateSet('GPT','MBR')]
    [string]$partitionStyle = 'GPT',
    
    # Path to a MOF-File for inital DSC configuration
    [ValidateScript({ If ( Test-Path -Path $_ -PathType Leaf )
          { $true }
          Else { Throw "$_ is not a valid File." } 
    })]
    [string]$DSCMofPath,
  
    # Path to an unattend.xml for inital Sysprep Configuration
    [ValidateScript({ If ( Test-Path -Path $_ -PathType Leaf )
          { $true }
          Else { Throw "$_ is not a valid File." } 
    })]
    [string]$UnattendPath,
    
    # Enable RDP-Protocol
    [switch]$EnableRDP,
    
    # Add Runonce-Script, will be started for the administrator Account at first Logon
    [ValidateScript({ If ( Test-Path -Path $_ -PathType Leaf )
          { $true }
          Else { Throw "$_ is not a valid File." } 
    })]
    [string]$RunOnceUserScript,
    
    # Add Runonce-Script, will be started once at first Computer startup
    [ValidateScript({ If ( Test-Path -Path $_ -PathType Leaf )
          { $true }
          Else { Throw "$_ is not a valid File." } 
    })]
    [string]$RunOnceComputerScript        
  )

  If ( (get-item -path $Imagepath).Extension -eq '.iso' )
  {
    Write-Verbose "Iso-Image: $ImagePath"
    $iso = Mount-DiskImage -ImagePath $ImagePath -StorageType ISO -PassThru
    $DSCConfigPath = 'Windows\System32\Configuration'
    $SysprepPath = 'Windows\System32\sysprep'
    # Workaround, as ISO-Drive might be not available elsewise
    $null = Get-PSDrive 
    $IsoDriveletter = (Get-Volume -DiskImage $iso).DriveLetter + ':'
    $wimPath = join-path $IsoDriveletter 'sources\install.wim'
  }
  Elseif ( (get-item -path $Imagepath).Extension -eq '.wim' )
  {
    $wimPath = $ImagePath
  }
  Else
  {
    Throw 'Das Image muß ein Iso-File oder ein gültiges Wim-File sein'
  }
  
  $imageToInstall = get-windowsimage -ImagePath $wimPath | Where-Object { $_.ImageName -eq "$Edition" -or $_.ImageIndex -eq $ImageIndex }
  If (-not $imageToInstall)
  {
    'Wrong Image Name. Please choose of the following:'
    get-windowsimage -ImagePath $wimPath
    If ( $iso )
    {
      Dismount-DiskImage -ImagePath $iso.ImagePath
    }
    break
  }
  $imageIndex = $imageToInstall.ImageIndex

  $vhd = New-VHD -Path $vhdPath -SizeBytes $diskSize -Dynamic -ErrorAction Stop
  $vhd = $vhd | Mount-VHD -Passthru
  
  Switch ( $partitionStyle )
  {
    'GPT' { $Partitions = New-GPTDisk -vhd $vhd -disktype $disktype }
    'MBR' { $Partitions = New-MBRDisk -vhd $vhd -disktype $disktype  }
  } 
  
  $logPath = Expand-WindowsImage -ImagePath $wimPath -Index $imageIndex[0] -ApplyPath ($Partitions.Data.DriveLetter + ':\')
  $bcdexe = join-Path -Path ( $Partitions.Data.DriveLetter + ':\' ) -ChildPath 'Windows\System32\bcdboot.exe'
  Write-Verbose "BCD-Command: $bcdexe"
  $ResultMessage = & $bcdexe "$($Partitions.Data.DriveLetter):\Windows" /s ($Partitions.Boot.DriveLetter + ':' )  /f ALL
  
  # Registry aus VHD mounten
  # $VhdRegistry = "$($Partitions.Data.DriveLetter):\windows\system32\config\System"
  
  $RegPath = Mount-VHDRegistry -DriveLetter $Partitions.Data.DriveLetter -Tree System
  Switch ( $PSBoundParameters )
  { 
    { $PSBoundParameters.EnableRDP } { Enable-RDP -RegPath $regPath }
    # ToDo: Call Function
    { $PSBoundParameters.RunOnceUserScript } { 'Implement Code here' }
    # ToDo: Call Function
    { $PSBoundParameters.RunOnceComputerScript } { 'Implement Code here' }
    # ToDo: Call Function
    { $PSBoundParameters.UnattendPath } { Add-File -FilePath $UnattendPath -OsDrive $Partitions.Data.DriveLetter -TargetPath $sysPrepPath }
    { $PSBoundParameters.DSCMofPath } { Add-File -FilePath $DSCMofPath -OsDrive $Partitions.Data.DriveLetter -TargetPath $DSCConfigPath -NewFileName pending.mof }
    # { $PSBoundParameters.DSCMofPath } { Add-MOF -DSCMofPath C:\temp\localhost.mof -OsDrive $Partitions.Data.DriveLetter }
  }
  Dismount-VHDRegistry
 
  # Systempartition konfigurieren
  Write-Verbose -Message $Partitions.Boot.Driveletter
  Switch ( $partitionStyle ) 
  {
    'GPT' { Set-Partition -DriveLetter $Partitions.Boot.Driveletter -GptType $PartitionType.EFI }
    # 'MBR' { Set-Partition -DriveLetter $Partitions.Boot.Driveletter -IsActive $true }
  }
  
  Write-Verbose "Dismounting Disk $($Vhd.Disknumber)" 
  Dismount-VHD -DiskNumber $vhd.DiskNumber
  Write-Verbose "Dismount Iso $($iso.ImagePath)"
  Dismount-DiskImage -ImagePath $iso.ImagePath
}