DeployImage.psm1

function Convert-WIMtoVHD
{
[CmdletBinding()]
Param(
# Size of the VHD file
#
$Size=20GB,
# Obtain the default location of
# Virtual HardDisks in Hyper-V
#
$VHDPath='.\',
# Location of the WindowsImage (WIM)
# File to convert to VHD
#
$Wimfile='C:\Windows7\Install.wim',
# In the case of a single partition setup like
# USB Key or simple VHD SystemDrive and OSDrive
# Will be the same Letter
#
# Drive Letter being assigned to Boot Drive
#
$OSDrive='L',
# Netbios name of computer and VMName
#
$VM='Contoso-Win7',
# Index of Image in WIM file
#
$Index=1

)

If ($Vhdpath -eq '.\' -and (Get-Command Get-vmhost).count -ge 1)
    {
    $VHDPath=(Get-VMHost).VirtualHardDiskPath
    }

# Define VHD filename
#
$VHD="$VhdPath\$VM.vhd"

If ((Test-Path $VHD) -ne $false)
    {
        Return $NULL
    }
Else
    {
    # Create a new VHD
    #
    $Result=New-VHD -Path $Vhd -SizeBytes $Size -Dynamic

    # Mount the VHD and identify it's Disk Object
    #
    $Result=Mount-VHD -Path $vhd
    $Disk=Get-Vhd -Path $Vhd | Get-Disk

    # Create a new Partition Structure of style
    # MBR, Format and Partition System and OSDrive
    #
    New-PartitionStructure -Disk $disk -MBR -BootDrive $OSDrive -OSDrive $OsDrive

    # Expand the Windows Image for Nano Server to the OSDrive
    #
    Expand-WindowsImage �imagepath "$wimfile" �index $Index �ApplyPath "$OSDrive`:\"

    # Send the Boot files to the Disk Structure
    #
    Send-BootCode -BootDrive $OSDrive -OSDrive $OSDrive
    # Dismount the Completed VHD
    #
    Dismount-VHD $VHD
    # Return path of VHD
    #
    Return $VHD
    }
}

Function Copy-WithProgress
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        $Source,
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=1)]
        $Destination
        
    )

Import-Module BitsTransfer

if ( (Test-Path $Destination) -eq $false) 
{
    $null = New-Item -Path $Destination -ItemType Directory 
}

Start-BitsTransfer -Source $Source -Destination $Destination -Description "Backup" -DisplayName "Backup" 

}

<#
.Synopsis
   Copies supplied sample scripts from the DeployImage module
.DESCRIPTION
   Copy all sample PS1 files from DeployImage to the destination directory
.EXAMPLE
   Copies sample scripts to current directory
    
   Copy-DeployImageSample
 
.EXAMPLE
   Copies sample scripts to C:\Foo
    
   Copy-DeployImageSample -Destination C:\Foo
 
 
#>


Function Copy-DeployImageSample
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        $Destination='.\'
        
    )
$Modulepath=Split-path -path ((get-module -Name deployimage).path)
get-childitem -Path "$Modulepath\*.ps1" | copy-item -Destination $Destination
}
 
<#
.Synopsis
   Removes a Drive Letter from an assigned partition
.DESCRIPTION
   Removes a Drive Letter from an assigned partition
.EXAMPLE
   Remove L: from it's assigned partition, freeing it back to available drive letters
    
   Remove-DriveLetter -DriveLetter L
#>


Function Remove-DriveLetter 
{
[CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [string]$DriveLetter
    )

    Get-Volume -Drive $DriveLetter | Get-Partition | Remove-PartitionAccessPath -accesspath "$DriveLetter`:\"

    Do {
        $status=(Get-Volume -DriveLetter $DriveLetter -erroraction SilentlyContinue)
       }
    until ($Status -eq $NULL)
}


<#
.Synopsis
   Identifies if the Operating System is 32bit or 64bit
.DESCRIPTION
   If the Operating system is 64bit, it will return the string " (x86)" to append to a "Program Files" path
.EXAMPLE
   $Folder="C:\Program Files$(Get-Architecture)"
#>


function Get-ArchitectureString
{
$Arch=(Get-CimInstance -Classname win32_operatingsystem).OSArchitecture
if ($Arch -eq '64-Bit')
    {
    Return [string]' (x86)'
    }

}

<#
.Synopsis
   Tests for the existence of the Windows 10 ADK
.DESCRIPTION
   This Cmdlet will return a Boolean True if the Windows 10 ADK is installed. It depends upon the Get-ArchitectureString Cmdlet supplied within this module
.EXAMPLE
   $AdkInstalled = Test-WindowsADK
.EXAMPLE
   If (Test-WindowsADK -eq $TRUE)
    {
        Write-Output 'Windows 10 ADK is installed'
    }
 
#>


function Test-WindowsADK
{
(Test-Path -Path "C:\Program Files$(Get-ArchitectureString)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment") 
}

function Get-AttachedDisk
{
[CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [switch]$USB,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=2)]
        [switch]$GUI
    )

If ($USB) 
    { 
        $DiskType='USB' 
    }
    Else
    {
        $DiskType='SATA','SCSI','RAID'
    }


if ($GUI -and ((Get-CimInstance -Classname Win32_OperatingSystem).OperatingSystemSKU -ne 1))
    {
        Get-Disk | Where-Object { $DiskType -match $_.BusType } | Out-GridView -PassThru
    }
    Else
    {
        Get-Disk | Where-Object { $DiskType -match $_.BusType } 
    }
}

<#
.Synopsis
   Erase the Partition structure on a Disk
.DESCRIPTION
   When provided with a Disk object from "Get-Disk" it will target and cleanly remove all partitions. It addresses some of the limitations found with the Clear-Disk Cmdlet.
.EXAMPLE
   Erase partition structure on Disk Number 1
    
   $Disk=Get-Disk -Number 1
   Clear-DiskStructure -Disk $disk
.EXAMPLE
   Erase partition structure on all USB attached disks
    
   $DiskList=Get-Disk | Where { $_.BusType -eq 'USB'}
   Foreach ( $Disk in $Disklist )
   {
   Clear-DiskStructure -Disk $Disk
   }
 
#>


function Clear-DiskStructure
{
[CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        $Disk
        
    )
Get-Disk -Number ($Disk.number) | Get-Partition | Remove-partition -confirm:$false -erroraction SilentlyContinue
Clear-Disk -Number $Disk.Number -RemoveData -RemoveOEM -confirm:$false -ErrorAction SilentlyContinue
}

<#
.Synopsis
   Create a Partition structure, whether UEFI or MBR
.DESCRIPTION
   Creates a Partition structure when provided with a target disk from the GET-Disk Cmdlet including formatting and assigning drive letters.
.EXAMPLE
   Create a UEFI Partition structure on Disk 0, assign Drive Z: to the System Drive and Drive Y: to the OSDrive
 
   $Disk=Get-Disk -number 0
   New-PhysicalPartitionStructure -Disk $Disk -BootDrive Z -OSDrive Y
    
#>


function New-PartitionStructure
{
[CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        $Disk,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=1)]
        [switch]$MBR,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=2)]
        [switch]$USB,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=3)]
        [string]$BootDrive,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=4)]
        [string]$OSDrive
                
     )

    Clear-DiskStructure $Disk
    
    if ($MBR)
    {
    $Result=Initialize-Disk -Number $Disk.Number -PartitionStyle MBR -ErrorAction SilentlyContinue
        
            if ($USB)
            {
            $Partition=New-Partition -DiskNumber $Disk.Number -DriveLetter $OSDrive -UseMaximumSize -IsActive
            $Result=Format-Volume -Partition $Partition  -FileSystem FAT32 -NewFileSystemLabel 'Windows'
            }
            else
            {
            $Partition=New-Partition -DiskNumber $Disk.Number -DriveLetter $OSDrive -UseMaximumSize -IsActive
            $Result=Format-Volume -Partition $Partition  -FileSystem NTFS -NewFileSystemLabel 'Windows'
            }
    
    }
    Else
    {
    $Result=Initialize-Disk -Number $Disk.Number -PartitionStyle GPT

    $Partition=New-Partition -DiskNumber $Disk.Number -Size 128MB ; # Create Microsoft Basic Partition
    $Result=Format-Volume -Partition $Partition -FileSystem Fat32 -NewFileSystemLabel 'MSR'
    $Result=Set-Partition -DiskNumber $Disk.Number -PartitionNumber $Partition.PartitionNumber -GptType '{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}'

    $Partition=New-Partition -DiskNumber $Disk.Number -Size 300MB -DriveLetter $BootDrive ; # Create Microsoft Basic Partition and Set System as bootable
    $Result=Format-Volume -Partition $Partition  -FileSystem Fat32 -NewFileSystemLabel 'Boot'
    $Result=Set-Partition -DiskNumber $Disk.Number -PartitionNumber $Partition.PartitionNumber

    $Partition=New-Partition -DiskNumber $Disk.Number -DriveLetter $OSDrive -UseMaximumSize ; # Take remaining Disk space for Operating System
    $Result=Format-Volume -Partition $Partition  -FileSystem NTFS -NewFileSystemLabel 'Windows'
    }
    
}


<#
.Synopsis
   Set San Policy for a Windows To Go key
.DESCRIPTION
   Creates and injects the necessary Disk policy which protects Windows to Go from Internal Drives. Requires the drive letter of the target OSDrive.
.EXAMPLE
   Set Windows To Go key on Drive L with the proper San Policy
 
   Send-SanPolicy -OSDrive L
#>


Function Send-SanPolicy
{
[CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        $OSDrive
     )

$SanPolicyXML=@"
<?xml version='1.0' encoding='utf-8' standalone='yes'?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
  <settings pass="offlineServicing">
    <component
        xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        language="neutral"
        name="Microsoft-Windows-PartitionManager"
        processorArchitecture="x86"
        publicKeyToken="31bf3856ad364e35"
        versionScope="nonSxS"
        >
      <SanPolicy>4</SanPolicy>
    </component>
    <component
        xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        language="neutral"
        name="Microsoft-Windows-PartitionManager"
        processorArchitecture="amd64"
        publicKeyToken="31bf3856ad364e35"
        versionScope="nonSxS"
        >
      <SanPolicy>4</SanPolicy>
    </component>
  </settings>
</unattend>
"@


Add-content -path "$OSDrive`:\san-policy.xml" -Value $SanpolicyXML

Use-WindowsUnattend -UnattendPath "$OSDrive`:\san-policy.xml" -path "$OSdrive`:\" | Out-Null
}

<#
.Synopsis
   Creates an Unattend.XML file for injection into an O/S
.DESCRIPTION
   This Cmdlet will create an Unattend.XML file with suitable content. Depending upon the provided parameters it can inject the needed content or credentials for a Domain Join or be left to join a Workgroup Configuration.
.EXAMPLE
   Create Unattend file for a computer named TestPC with all defaults for Password
     
   New-UnattendXMLContent -computername TestPC
.EXAMPLE
 
 
#>

function New-UnattendXMLContent
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [string]$Computername,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=1)]
        [string]$Timezone='Eastern Standard Time',
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=2)]
        [string]$Owner='Nano Owner',
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=3)]
        [string]$Organization='Nano Organization',
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=4)]
        [string]$AdminPassword='P@ssw0rd',
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=5,
                   ParameterSetName='DomainOnline')]
        [Switch]$Online,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=6,
                   ParameterSetName='DomainOnline')]
        [string]$DomainName,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=7,
                   ParameterSetName='DomainOnline')]
        [string]$DomainAccount,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=8,
                   ParameterSetName='DomainOnline')]
        [string]$DomainPassword,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=9,
                   ParameterSetName='DomainOnline')]
        [string]$DomainOU,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=10)]
        [string]$OfflineBlob,
                [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=11)]
        [switch]$SkipOOBE
                        
     )

$UnattendXML=@"
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
    <settings pass="specialize">
        <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <ComputerName>$Computername</ComputerName>
            <RegisteredOrganization>$Organization</RegisteredOrganization>
            <RegisteredOwner>$Owner</RegisteredOwner>
            <TimeZone>$Timezone</TimeZone>
        </component>
"@


If($JoinDomain -eq 'Online')
{
$UnattendXML=$UnattendXML+@"
     
        <component name="Microsoft-Windows-UnattendedJoin" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
             <Identification>
                 <Credentials>
                     <Domain>$DomainName</Domain>
                     <Password>$Domainpassword</Password>
                     <Username>$Domainaccount</Username>
                 </Credentials>
                 <JoinDomain>$DomainName</JoinDomain>
                 <MachineObjectOU>$DomainOU</MachineObjectOU>
                 <UnsecureJoin>False</UnsecureJoin>
             </Identification>
         </component>
"@

}

if($Network)
{
$UnattendXML=$UnattendXML+@"
          <Interfaces>
                <Interface wcm:action="add">
                    <Ipv4Settings>
                        <DhcpEnabled>false</DhcpEnabled>
                    </Ipv4Settings>
                    <Identifier>Local Area Connection</Identifier>
                    <UnicastIpAddresses>
                        <IpAddress wcm:action="add" wcm:keyValue="1">$IPv4/$Mask</IpAddress>
                    </UnicastIpAddresses>
                    <Routes>
                        <Route wcm:action="add">
                            <Identifier>0</Identifier>
                            <Prefix>0.0.0.0/0</Prefix>
                            <Metric>20</Metric>
                            <NextHopAddress>$Gateway</NextHopAddress>
                        </Route>
                    </Routes>
                </Interface>
            </Interfaces>
"@

}

$UnattendXML=$UnattendXML+@"
     
    </settings>
    <settings pass="oobeSystem">
        <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <UserAccounts>
                <AdministratorPassword>
                    <Value>$Adminpassword</Value>
                    <PlainText>true</PlainText>
                </AdministratorPassword>
            </UserAccounts>
          <AutoLogon>
                <Password>
                  <Value>$Adminpassword</Value>
                  <PlainText>true</PlainText>
               </Password>
            <Username>administrator</Username>
            <LogonCount>1</LogonCount>
            <Enabled>true</Enabled>
          </AutoLogon>
         <RegisteredOrganization>$Organization</RegisteredOrganization>
            <RegisteredOwner>$Owner</RegisteredOwner>
"@


If ($SkipOOBE)
{
$UnattendXML=$UnattendXML+@"
            <OOBE>
                <HideEULAPage>true</HideEULAPage>
                <SkipMachineOOBE>true</SkipMachineOOBE>
            </OOBE>
         
"@

}

$UnattendXML=$UnattendXML+@"
 
    </component>
    </settings>
"@


If ($OfflineBlob)
{
$UnattendXML=$UnattendXML+@"
 
    <settings pass="offlineServicing">
        <component name="Microsoft-Windows-UnattendedJoin" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <OfflineIdentification>
                <Provisioning>
                    <AccountData>$OfflineBlob</AccountData>
                </Provisioning>
            </OfflineIdentification>
        </component>
    </settings>
"@

}


$UnattendXML=$UnattendXML+@"
 
    <cpi:offlineImage cpi:source="" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
</unattend>
"@



Return $UnattendXML

}

Function Send-UnattendXML
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [string]$OSDrive,
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=1)]
        [string]$UnattendData
    )
        $Filename="$((Get-random).ToString()).xml"
        
        Remove-item -Path $Filename -force -ErrorAction SilentlyContinue
        New-Item -ItemType File -Path $Filename
        Add-Content -Path $Filename -Value $UnattendData
        Copy-Item -Path $filename -Destination "$OSDrive`:\Windows\System32\Sysprep\unattend.xml"
        Remove-item -Path $Filename -force -ErrorAction SilentlyContinue

}

function Send-BootCode
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [string]$BootDrive,
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=1)]
        [string]$OSDrive,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=2)]
        [switch]$USB
    
    )
    $oldpref=$ErrorActionPreference
    $ErrorActionPreference='SilentlyContinue'

    if ($USB)
    {
        & "$($env:windir)\system32\bootsect.exe" /nt60 "$OSDrive`:" > $null
    }
    else
    {
    & "$($env:windir)\system32\bcdboot" "$OSDrive`:\Windows" /s "$BootDrive`:" /f ALL > $Null
    }
    $ErrorActionPreference=$oldpref
}

function New-NanoServerWIM

{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [string]$Mediapath,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=1)]
        [string]$Destination='C:\NanoTemp',
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=2)]
        [switch]$Compute=$True,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=3)]
        [switch]$Clustering=$True,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=4)]
        [switch]$Guest=$True,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=5)]
        [switch]$OEMDrivers=$True,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=6)]
        [switch]$Storage=$False,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=7)]
        [switch]$Defender=$True,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=9)]
        [switch]$DNS=$False,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=10)]
        [switch]$DSC=$True,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=11)]
        [switch]$IIS=$False,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=12)]
        [switch]$SCVMM=$False,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=13)]
        [switch]$NPDS=$False,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=14)]
        [switch]$DCB=$False,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=15)]
        [switch]$Ramdisk=$False,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=16)]
        $Edition=1,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=17)]
        [switch]$Secure=$False,
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true,
                   Position=18)]
        [switch]$ShieldedVM=$False
        
        
        

        

     )

    Begin
    {
    }
 
    Process
    { 
        Remove-Item -Path $Destination -Force -Recurse -ErrorAction SilentlyContinue
        New-Item -ItemType Directory -Path $Destination -Force | Out-Null
        New-Item -ItemType Directory -Path "$Destination\Mount" -Force | Out-Null

        Copy-Item -Path "$($MediaPath)NanoServer\Nanoserver.wim" -Destination $Destination | Out-Null
        Set-ItemProperty -Path "$($Destination)\Nanoserver.wim" -Name IsReadOnly -Value $False | Out-Null
        
        Mount-WindowsImage -ImagePath "$Destination\Nanoserver.wim" -Index $Edition -path "$Destination\Mount"| Out-Null
        
        If ($Compute) # Hyper-V Role
        {
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\Microsoft-NanoServer-Compute-Package.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\en-us\Microsoft-NanoServer-Compute-Package_en-us.cab" -Path "$Destination\Mount\" | Out-NULL
        }

        If ($Containers) # Windows Containers
        {
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\Microsoft-NanoServer-Containers-Package.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\en-us\Microsoft-NanoServer-Containers-Package_en-us.cab" -Path "$Destination\Mount\" | Out-NULL
        }
        If ($DCB) # Data Center Bridging
        {
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\Microsoft-NanoServer-DCB-Package.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\en-us\Microsoft-NanoServer-DCB-Package_en-us.cab" -Path "$Destination\Mount\" | Out-NULL
        }
        If ($Defender) # Windows Defender
        {
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\Microsoft-NanoServer-Defender-Package.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\en-us\Microsoft-NanoServer-Defender-Package_en-us.cab" -Path "$Destination\Mount\" | Out-NULL
        }
        If ($DNS) # DNS Server
        {
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\Microsoft-NanoServer-DNS-Package.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\en-us\Microsoft-NanoServer-DNS-Package_en-us.cab" -Path "$Destination\Mount\" | Out-NULL
        }
        If ($DSC) # Desired State Configuration
        {
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\Microsoft-NanoServer-DSC-Package.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\en-us\Microsoft-NanoServer-DSC-Package_en-us.cab" -Path "$Destination\Mount\" | Out-NULL
        }
        If ($Clustering) # Failover Clustering
        {
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\Microsoft-NanoServer-FailoverCluster-Package.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\en-us\Microsoft-NanoServer-FailoverCluster-Package_en-us.cab" -Path "$Destination\Mount\" | Out-NULL
        }
        If ($Guest) # Hyper-V Guest Driver Integration
        {
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\Microsoft-NanoServer-Guest-Package.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\en-us\Microsoft-NanoServer-Guest-Package_en-us.cab" -Path "$Destination\Mount\" | Out-NULL
        }
        Else
        {
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\Microsoft-NanoServer-Host-Package.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\en-us\Microsoft-NanoServer-Host-Package_en-us.cab" -Path "$Destination\Mount\" | Out-NULL
        }
        If ($IIS) # IIS Server
        {
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\Microsoft-NanoServer-IIS-Package.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\en-us\Microsoft-NanoServer-IIS-Package_en-us.cab" -Path "$Destination\Mount\" | Out-NULL
        }
        If ($NPDS) # Network Performance Diagnostics Service
        {
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\Microsoft-NanoServer-NPDS-Package.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\en-us\Microsoft-NanoServer-NPDS-Package_en-us.cab" -Path "$Destination\Mount\" | Out-NULL
        }
        If ($OEMDrivers) # OEM Drivers
        {
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\Microsoft-NanoServer-OEM-Drivers-Package.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\en-us\Microsoft-NanoServer-OEM-Drivers-Package_en-us.cab" -Path "$Destination\Mount\" | Out-NULL
        }
        If ($Storage) # File Server and Storage Components
        {
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\Microsoft-NanoServer-Storage-Package.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\en-us\Microsoft-NanoServer-Storage-Package_en-us.cab" -Path "$Destination\Mount\" | Out-NULL
        }
        
        # Reverse Forwarders are now a default option
        #
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\Microsoft-OneCore-ReverseForwarders-Package.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\en-us\Microsoft-OneCore-ReverseForwarders-Package_en-us.cab" -Path "$Destination\Mount\" | Out-NULL
        
        If ($Ramdisk) # Package to allow from boot from WIM to Ramdisk
        {
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\Microsoft-NanoServer-BootFromWim-Package.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\en-us\Microsoft-NanoServer-BootFromWim-Package_en-us.cab" -Path "$Destination\Mount\" | Out-NULL
        }
        If ($Secure) # Package to enable Secure Boot
        {
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\Microsoft-NanoServer-SecureStartup-Package.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\en-us\Microsoft-NanoServer-SecureStartup-Package_en-us.cab" -Path "$Destination\Mount\" | Out-NULL
        }
        If ($ShieldedVM -and $Edition -eq 2) # Package to enabled a Shielded - Only for Datacenter
        {
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\Microsoft-NanoServer-ShieldedVM-Package.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\en-us\Microsoft-NanoServer-ShieldedVM-Package_en-us.cab" -Path "$Destination\Mount\" | Out-NULL
        }
        If ($SCVMM) # System Center VMM components
        {
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\Microsoft-Windows-Server-SCVMM-Compute-Package.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\en-us\Microsoft-Windows-Server-SCVMM-Compute-Package_en-us.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\Microsoft-Windows-Server-SCVMM-Package.cab" -Path "$Destination\Mount\" | Out-NULL
        Add-WindowsPackage -PackagePath "$($MediaPath)NanoServer\Packages\en-us\Microsoft-Windows-Server-SCVMM-Package_en-us.cab" -Path "$Destination\Mount\" | Out-NULL
        }

        New-Item -Path "$Destination\Mount\Windows\Setup\Scripts" -Force -ItemType Directory | Out-Null
        New-Item -Path "$Destination\Mount\Windows\Setup\Scripts\SetupComplete.cmd" -Value $SetupComplete -Force -ItemType File | Out-Null

        Dismount-WindowsImage -Path "$Destination\Mount" -Save | Out-Null
        
        Remove-Item -Path "$Destination\NanoCustom.wim" -ErrorAction SilentlyContinue | Out-Null
        Copy-Item -path "$Destination\NanoServer.wim" -destination "$Destination\NanoCustom.wim" | Out-Null
        Return "$Destination\NanoCustom.wim"


    }     
End
        {
        }
}

function New-WindowsPEWim
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true)]
        $WinPETemp='C:\TempPE',
        [Parameter(Mandatory=$false,
                   ValueFromPipelineByPropertyName=$true)]
        $Destination='C:\PeWim'

    )

    Begin
    {
    }
 
    Process
    {
        $Env:WinPERoot="C`:\Program Files$(Get-ArchitectureString)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment" 

        $WinADK="$($Env:WinPERoot)\amd64"

        Remove-item -Path $WinPETemp -Recurse -Force -ErrorAction SilentlyContinue | Out-Null
        New-Item -ItemType Directory -Path $WinPETemp -Force | Out-Null
        Copy-Item -Path "$WinAdk\Media" -Destination $WinPETemp -Recurse -Force | Out-Null
        New-Item -ItemType Directory -Path "$WinPETemp\Media\Sources" -Force | Out-Null
        Copy-Item -path "$WinAdk\en-us\winpe.wim" -Destination "$WinPETemp\Media\Sources\boot.wim" | Out-Null
        New-Item -ItemType Directory -Path "$WinPETemp\Mount" -Force | Out-Null

        Mount-WindowsImage -ImagePath "$WinPETemp\Media\Sources\boot.wim" -Index 1 -path "$WinPETemp\Mount" | Out-Null
        
        Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\WinPE-WMI.cab" -Path "$WinPeTemp\Mount" -IgnoreCheck | Out-Null
        Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\en-us\WinPE-WMI_en-us.cab" -Path "$WinPeTemp\Mount" -IgnoreCheck | Out-Null
        Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\WinPE-NetFx.cab" -Path "$WinPeTemp\Mount" -IgnoreCheck | Out-Null
        Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\en-us\WinPE-NetFx_en-us.cab" -Path "$WinPeTemp\Mount" -IgnoreCheck | Out-Null
        Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\WinPE-Scripting.cab" -Path "$WinPeTemp\Mount" -IgnoreCheck | Out-Null
        Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\en-us\WinPE-Scripting_en-us.cab" -Path "$WinPeTemp\Mount" -IgnoreCheck | Out-Null
        Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\WinPE-PowerShell.cab" -Path "$WinPeTemp\Mount" -IgnoreCheck | Out-Null
        Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\en-us\WinPE-PowerShell_en-us.cab" -Path "$WinPeTemp\Mount" -IgnoreCheck | Out-Null
        Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\WinPE-DismCmdlets.cab" -Path "$WinPeTemp\Mount" -IgnoreCheck | Out-Null
        Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\en-us\WinPE-DismCmdlets_en-us.cab" -Path "$WinPeTemp\Mount" -IgnoreCheck | Out-Null
        Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\WinPE-EnhancedStorage.cab" -Path "$WinPeTemp\Mount" -IgnoreCheck | Out-Null
        Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\en-us\WinPE-EnhancedStorage_en-us.cab" -Path "$WinPeTemp\Mount" -IgnoreCheck | Out-Null
        Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\WinPE-StorageWMI.cab" -Path "$WinPeTemp\Mount" -IgnoreCheck | Out-Null
        Add-WindowsPackage -PackagePath "$($WinAdk)\Winpe_OCS\en-us\WinPE-StorageWMI_en-us.cab" -Path "$WinPeTemp\Mount" -IgnoreCheck | Out-Null
        
        # Custom PowerShell Script to launch after Wpeinit.exe
        # This is hardcoded presently to automatically
        # Start the DeployImage module
        # from the WinPE media for Easy Server Deployment
        $PowerShellScript=@'
Set-ExecutionPolicy -executionpolicy Bypass
$DriveLetter=(Get-Volume | Where { $_.DriveType -eq 'CD-ROM' -and $_.FileSystemLabel -eq 'DVD_ROM'}).DriveLetter
Set-Location ($DriveLetter+':\DeployImage\')
Import-Module ($DriveLetter+':\DeployImage\DeployImage.Psd1')
'@


        # Carriage Return (Ascii13) and Linefeed (Ascii10)
        # the characters at the end of each line in a Here String
        $CRLF=[char][byte]13+[char][byte]10

        $PowerShellCommand=$PowerShellScript.replace($CRLF,';')

        $PowerShellStart='powershell.exe -executionpolicy bypass -noexit -command "'+$PowerShellCommand+'"'        
        Add-Content -Path "$WinPEtemp\Mount\Windows\System32\Startnet.cmd" -Value $PowerShellStart
        
        Dismount-WindowsImage -path "$WinPETemp\Mount" -Save | Out-Null
        
        New-Item -Path $Destination -ItemType Directory -Force | Out-Null
        Copy-Item -path "$WinPETemp\Media\Sources\boot.wim" -destination "$Destination\" | Out-Null
        Remove-Item -Path "$Destination\Custom.wim" -erroraction SilentlyContinue | Out-Null
        Rename-Item -Path "$Destination\Boot.wim" -NewName 'Custom.wim' | Out-Null
        Remove-item -Path $WinPETemp -Recurse -Force -ErrorAction SilentlyContinue | Out-Null
        Return "$Destination\Custom.wim"

        }
    End
    {
    }
}