├── README.md └── New-ISOFile.ps1 /README.md: -------------------------------------------------------------------------------- 1 | # New-ISOFile 2 | 3 | Create an ISO file from a source folder. 4 | 5 | Optionally speicify a boot image and media type. 6 | 7 | Based on original function by Chris Wu. 8 | 9 | https://gallery.technet.microsoft.com/scriptcenter/New-ISOFile-function-a8deeffd (link appears to be no longer valid.) 10 | 11 | # Changes: 12 | * Updated to work with PowerShell 7 13 | * Added a bit more error handling and verbose output. 14 | * Features removed to simplify code: 15 | * Clipboard support. 16 | * Pipeline input. 17 | -------------------------------------------------------------------------------- /New-ISOFile.ps1: -------------------------------------------------------------------------------- 1 | function New-ISOFile { 2 | <# 3 | .SYNOPSIS 4 | Create an ISO file from a source folder. 5 | 6 | .DESCRIPTION 7 | Create an ISO file from a source folder. 8 | Optionally speicify a boot image and media type. 9 | 10 | Based on original function by Chris Wu. 11 | https://gallery.technet.microsoft.com/scriptcenter/New-ISOFile-function-a8deeffd (link appears to be no longer valid.) 12 | 13 | Changes: 14 | - Updated to work with PowerShell 7 15 | - Added a bit more error handling and verbose output. 16 | - Features removed to simplify code: 17 | * Clipboard support. 18 | * Pipeline input. 19 | 20 | .PARAMETER source 21 | The source folder to add to the ISO. 22 | 23 | .PARAMETER destinationIso 24 | The ISO file to create. 25 | 26 | .PARAMETER bootFile 27 | Optional. Boot file to add to the ISO. 28 | 29 | .PARAMETER media 30 | Optional. The media type of the resulting ISO (BDR, CDR etc). Defaults to DVDPLUSRW_DUALLAYER. 31 | 32 | .PARAMETER title 33 | Optional. Title of the ISO file. Defaults to "untitled". 34 | 35 | .PARAMETER force 36 | Optional. Force overwrite of an existing ISO file. 37 | 38 | .INPUTS 39 | None. 40 | 41 | .OUTPUTS 42 | None. 43 | 44 | .EXAMPLE 45 | New-ISOFile -source c:\forIso\ -destinationIso C:\ISOs\testiso.iso 46 | 47 | Simple example. Create testiso.iso with the contents from c:\forIso 48 | 49 | .EXAMPLE 50 | New-ISOFile -source f:\ -destinationIso C:\ISOs\windowsServer2019Custom.iso -bootFile F:\efi\microsoft\boot\efisys.bin -title "Windows2019" 51 | 52 | Example building Windows media. Add the contents of f:\ to windowsServer2019Custom.iso. Use efisys.bin to make the disc bootable. 53 | 54 | .LINK 55 | 56 | .NOTES 57 | 01 Alistair McNair Initial version. 58 | 59 | #> 60 | [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact="Low")] 61 | Param 62 | ( 63 | [parameter(Mandatory=$true,ValueFromPipeline=$false)] 64 | [string]$source, 65 | [parameter(Mandatory=$true,ValueFromPipeline=$false)] 66 | [string]$destinationIso, 67 | [parameter(Mandatory=$false,ValueFromPipeline=$false)] 68 | [string]$bootFile = $null, 69 | [Parameter(Mandatory=$false,ValueFromPipeline=$false)] 70 | [ValidateSet("CDR","CDRW","DVDRAM","DVDPLUSR","DVDPLUSRW","DVDPLUSR_DUALLAYER","DVDDASHR","DVDDASHRW","DVDDASHR_DUALLAYER","DISK","DVDPLUSRW_DUALLAYER","BDR","BDRE")] 71 | [string]$media = "DVDPLUSRW_DUALLAYER", 72 | [Parameter(Mandatory=$false,ValueFromPipeline=$false)] 73 | [string]$title = "untitled", 74 | [Parameter(Mandatory=$false,ValueFromPipeline=$false)] 75 | [switch]$force 76 | ) 77 | 78 | begin { 79 | 80 | Write-Verbose ("Function start.") 81 | 82 | } # begin 83 | 84 | process { 85 | 86 | Write-Verbose ("Processing nested system " + $vmName) 87 | 88 | ## Set type definition 89 | Write-Verbose ("Adding ISOFile type.") 90 | 91 | $typeDefinition = @' 92 | public class ISOFile { 93 | public unsafe static void Create(string Path, object Stream, int BlockSize, int TotalBlocks) { 94 | int bytes = 0; 95 | byte[] buf = new byte[BlockSize]; 96 | var ptr = (System.IntPtr)(&bytes); 97 | var o = System.IO.File.OpenWrite(Path); 98 | var i = Stream as System.Runtime.InteropServices.ComTypes.IStream; 99 | 100 | if (o != null) { 101 | while (TotalBlocks-- > 0) { 102 | i.Read(buf, BlockSize, ptr); o.Write(buf, 0, bytes); 103 | } 104 | 105 | o.Flush(); o.Close(); 106 | } 107 | } 108 | } 109 | '@ 110 | 111 | ## Create type ISOFile, if not already created. Different actions depending on PowerShell version 112 | if (!('ISOFile' -as [type])) { 113 | 114 | ## Add-Type works a little differently depending on PowerShell version. 115 | ## https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/add-type 116 | switch ($PSVersionTable.PSVersion.Major) { 117 | 118 | ## 7 and (hopefully) later versions 119 | {$_ -ge 7} { 120 | Write-Verbose ("Adding type for PowerShell 7 or later.") 121 | Add-Type -CompilerOptions "/unsafe" -TypeDefinition $typeDefinition 122 | } # PowerShell 7 123 | 124 | ## 5, and only 5. We aren't interested in previous versions. 125 | 5 { 126 | Write-Verbose ("Adding type for PowerShell 5.") 127 | $compOpts = New-Object System.CodeDom.Compiler.CompilerParameters 128 | $compOpts.CompilerOptions = "/unsafe" 129 | 130 | Add-Type -CompilerParameters $compOpts -TypeDefinition $typeDefinition 131 | } # PowerShell 5 132 | 133 | default { 134 | ## If it's not 7 or later, and it's not 5, then we aren't doing it. 135 | throw ("Unsupported PowerShell version.") 136 | 137 | } # default 138 | 139 | } # switch 140 | 141 | } # if 142 | 143 | 144 | ## Add boot file to image 145 | if ($bootFile) { 146 | 147 | Write-Verbose ("Optional boot file " + $bootFile + " has been specified.") 148 | 149 | ## Display warning if Blu Ray media is used with a boot file. 150 | ## Not sure why this doesn't work. 151 | if(@('BDR','BDRE') -contains $media) { 152 | Write-Warning ("Selected boot image may not work with BDR/BDRE media types.") 153 | } # if 154 | 155 | if (!(Test-Path -Path $bootFile)) { 156 | throw ($bootFile + " is not valid.") 157 | } # if 158 | 159 | ## Set stream type to binary and load in boot file 160 | Write-Verbose ("Loading boot file.") 161 | 162 | try { 163 | $stream = New-Object -ComObject ADODB.Stream -Property @{Type=1} -ErrorAction Stop 164 | $stream.Open() 165 | $stream.LoadFromFile((Get-Item -LiteralPath $bootFile).Fullname) 166 | 167 | Write-Verbose ("Boot file loaded.") 168 | } # try 169 | catch { 170 | throw ("Failed to open boot file. " + $_.exception.message) 171 | } # catch 172 | 173 | 174 | ## Apply the boot image 175 | Write-Verbose ("Applying boot image.") 176 | 177 | try { 178 | $boot = New-Object -ComObject IMAPI2FS.BootOptions -ErrorAction Stop 179 | $boot.AssignBootImage($stream) 180 | 181 | Write-Verbose ("Boot image applied.") 182 | } # try 183 | catch { 184 | throw ("Failed to apply boot file. " + $_.exception.message) 185 | } # catch 186 | 187 | 188 | Write-Verbose ("Boot file applied.") 189 | 190 | } # if 191 | 192 | ## Build array of media types 193 | $mediaType = @( 194 | "UNKNOWN", 195 | "CDROM", 196 | "CDR", 197 | "CDRW", 198 | "DVDROM", 199 | "DVDRAM", 200 | "DVDPLUSR", 201 | "DVDPLUSRW", 202 | "DVDPLUSR_DUALLAYER", 203 | "DVDDASHR", 204 | "DVDDASHRW", 205 | "DVDDASHR_DUALLAYER", 206 | "DISK", 207 | "DVDPLUSRW_DUALLAYER", 208 | "HDDVDROM", 209 | "HDDVDR", 210 | "HDDVDRAM", 211 | "BDROM", 212 | "BDR", 213 | "BDRE" 214 | ) 215 | 216 | Write-Verbose ("Selected media type is " + $media + " with value " + $mediaType.IndexOf($media)) 217 | 218 | ## Initialise image 219 | Write-Verbose ("Initialising image object.") 220 | try { 221 | $image = New-Object -ComObject IMAPI2FS.MsftFileSystemImage -Property @{VolumeName=$title} -ErrorAction Stop 222 | $image.ChooseImageDefaultsForMediaType($mediaType.IndexOf($media)) 223 | 224 | Write-Verbose ("initialised.") 225 | } # try 226 | catch { 227 | throw ("Failed to initialise image. " + $_.exception.Message) 228 | } # catch 229 | 230 | 231 | ## Create target ISO, throw if file exists and -force parameter is not used. 232 | if ($PSCmdlet.ShouldProcess($destinationIso)) { 233 | 234 | if (!($targetFile = New-Item -Path $destinationIso -ItemType File -Force:$Force -ErrorAction SilentlyContinue)) { 235 | throw ("Cannot create file " + $destinationIso + ". Use -Force parameter to overwrite if the target file already exists.") 236 | } # if 237 | 238 | } # if 239 | 240 | 241 | ## Get source content from specified path 242 | Write-Verbose ("Fetching items from source directory.") 243 | try { 244 | $sourceItems = Get-ChildItem -LiteralPath $source -ErrorAction Stop 245 | Write-Verbose ("Got source items.") 246 | } # try 247 | catch { 248 | throw ("Failed to get source items. " + $_.exception.message) 249 | } # catch 250 | 251 | 252 | ## Add these to our image 253 | Write-Verbose ("Adding items to image.") 254 | 255 | foreach($sourceItem in $sourceItems) { 256 | 257 | try { 258 | $image.Root.AddTree($sourceItem.FullName, $true) 259 | } # try 260 | catch { 261 | throw ("Failed to add " + $sourceItem.fullname + ". " + $_.exception.message) 262 | } # catch 263 | 264 | } # foreach 265 | 266 | ## Add boot file, if specified 267 | if ($boot) { 268 | Write-Verbose ("Adding boot image.") 269 | $Image.BootImageOptions = $boot 270 | } 271 | 272 | ## Write out ISO file 273 | Write-Verbose ("Writing out ISO file to " + $targetFile) 274 | 275 | try { 276 | $result = $image.CreateResultImage() 277 | [ISOFile]::Create($targetFile.FullName,$result.ImageStream,$result.BlockSize,$result.TotalBlocks) 278 | } # try 279 | catch { 280 | throw ("Failed to write ISO file. " + $_.exception.Message) 281 | } # catch 282 | 283 | Write-Verbose ("File complete.") 284 | 285 | ## Return file details 286 | return $targetFile 287 | 288 | } # process 289 | 290 | end { 291 | Write-Verbose ("Function complete.") 292 | } # end 293 | 294 | } # function --------------------------------------------------------------------------------