Create an ISO file with PowerShell!

I received a request at work to transfer 5+gb from the internal network, to a server in the DMZ.  Our DMZ networks are locked down, so I cannot copy the files directly.  I have the trial version of UltraISO that I’ve used for tasks like this in the past, but it’s limited to 300mb in the trial version.  Previously, we’ve used a “swing VMDK” that we’d mount on an internal VM, copy the data to it, take this VMDK and download it to my PC with something like WinSCP, then upload it to a DMZ datastore, mount it to the VM, etc.  This can get messy, especially if you don’t remember to remove and delete the “swing VMDK”.

I decided to do some research online to see if I could use something like 7-zip or another tool to create the ISO.  I happened upon a function that someone wrote for PowerShell to create an ISO via Microsoft’s Technet Script Center. I took the function, read through it to verify it wasn’t going to do anything malicious (which you should ALWAYS do before just blatantly running someone else’s code) and decided to try it.

Here’s the function itself:

function New-IsoFile  
{  
  <# .Synopsis Creates a new .iso file .Description The New-IsoFile cmdlet creates a new .iso file containing content from chosen folders .Example New-IsoFile "c:\tools","c:Downloads\utils" This command creates a .iso file in $env:temp folder (default location) that contains c:\tools and c:\downloads\utils folders. The folders themselves are included at the root of the .iso image. .Example New-IsoFile -FromClipboard -Verbose Before running this command, select and copy (Ctrl-C) files/folders in Explorer first. .Example dir c:\WinPE | New-IsoFile -Path c:\temp\WinPE.iso -BootFile "${env:ProgramFiles(x86)}\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\efisys.bin" -Media DVDPLUSR -Title "WinPE" This command creates a bootable .iso file containing the content from c:\WinPE folder, but the folder itself isn't included. Boot file etfsboot.com can be found in Windows ADK. Refer to IMAPI_MEDIA_PHYSICAL_TYPE enumeration for possible media types: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366217(v=vs.85).aspx .Notes NAME: New-IsoFile AUTHOR: Chris Wu LASTEDIT: 03/23/2016 14:46:50 #>  
  
  [CmdletBinding(DefaultParameterSetName='Source')]Param( 
    [parameter(Position=1,Mandatory=$true,ValueFromPipeline=$true, ParameterSetName='Source')]$Source,  
    [parameter(Position=2)][string]$Path = "$env:temp\$((Get-Date).ToString('yyyyMMdd-HHmmss.ffff')).iso",  
    [ValidateScript({Test-Path -LiteralPath $_ -PathType Leaf})][string]$BootFile = $null, 
    [ValidateSet('CDR','CDRW','DVDRAM','DVDPLUSR','DVDPLUSRW','DVDPLUSR_DUALLAYER','DVDDASHR','DVDDASHRW','DVDDASHR_DUALLAYER','DISK','DVDPLUSRW_DUALLAYER','BDR','BDRE')][string] $Media = 'DVDPLUSRW_DUALLAYER', 
    [string]$Title = (Get-Date).ToString("yyyyMMdd-HHmmss.ffff"),  
    [switch]$Force, 
    [parameter(ParameterSetName='Clipboard')][switch]$FromClipboard 
  ) 
 
  Begin {  
    ($cp = new-object System.CodeDom.Compiler.CompilerParameters).CompilerOptions = '/unsafe' 
    if (!('ISOFile' -as [type])) {  
      Add-Type -CompilerParameters $cp -TypeDefinition @' 
public class ISOFile  
{ 
  public unsafe static void Create(string Path, object Stream, int BlockSize, int TotalBlocks)  
  {  
    int bytes = 0;  
    byte[] buf = new byte[BlockSize];  
    var ptr = (System.IntPtr)(&bytes);  
    var o = System.IO.File.OpenWrite(Path);  
    var i = Stream as System.Runtime.InteropServices.ComTypes.IStream;  
  
    if (o != null) { 
      while (TotalBlocks-- > 0) {  
        i.Read(buf, BlockSize, ptr); o.Write(buf, 0, bytes);  
      }  
      o.Flush(); o.Close();  
    } 
  } 
}  
'@  
    } 
  
    if ($BootFile) { 
      if('BDR','BDRE' -contains $Media) { Write-Warning "Bootable image doesn't seem to work with media type $Media" } 
      ($Stream = New-Object -ComObject ADODB.Stream -Property @{Type=1}).Open()  # adFileTypeBinary 
      $Stream.LoadFromFile((Get-Item -LiteralPath $BootFile).Fullname) 
      ($Boot = New-Object -ComObject IMAPI2FS.BootOptions).AssignBootImage($Stream) 
    } 
 
    $MediaType = @('UNKNOWN','CDROM','CDR','CDRW','DVDROM','DVDRAM','DVDPLUSR','DVDPLUSRW','DVDPLUSR_DUALLAYER','DVDDASHR','DVDDASHRW','DVDDASHR_DUALLAYER','DISK','DVDPLUSRW_DUALLAYER','HDDVDROM','HDDVDR','HDDVDRAM','BDROM','BDR','BDRE') 
 
    Write-Verbose -Message "Selected media type is $Media with value $($MediaType.IndexOf($Media))" 
    ($Image = New-Object -com IMAPI2FS.MsftFileSystemImage -Property @{VolumeName=$Title}).ChooseImageDefaultsForMediaType($MediaType.IndexOf($Media)) 
  
    if (!($Target = New-Item -Path $Path -ItemType File -Force:$Force -ErrorAction SilentlyContinue)) { Write-Error -Message "Cannot create file $Path. Use -Force parameter to overwrite if the target file already exists."; break } 
  }  
 
  Process { 
    if($FromClipboard) { 
      if($PSVersionTable.PSVersion.Major -lt 5) { Write-Error -Message 'The -FromClipboard parameter is only supported on PowerShell v5 or higher'; break } 
      $Source = Get-Clipboard -Format FileDropList 
    } 
 
    foreach($item in $Source) { 
      if($item -isnot [System.IO.FileInfo] -and $item -isnot [System.IO.DirectoryInfo]) { 
        $item = Get-Item -LiteralPath $item 
      } 
 
      if($item) { 
        Write-Verbose -Message "Adding item to the target image: $($item.FullName)" 
        try { $Image.Root.AddTree($item.FullName, $true) } catch { Write-Error -Message ($_.Exception.Message.Trim() + ' Try a different media type.') } 
      } 
    } 
  } 
 
  End {  
    if ($Boot) { $Image.BootImageOptions=$Boot }  
    $Result = $Image.CreateResultImage()  
    [ISOFile]::Create($Target.FullName,$Result.ImageStream,$Result.BlockSize,$Result.TotalBlocks) 
    Write-Verbose -Message "Target image ($($Target.FullName)) has been created" 
    $Target 
  } 
} 

With that, I was able to create a variable for my source data, and use get-childitem to get that location and pipe that to creating the ISO. See below:

$source_dir = "Z:\Install\App123"
get-childitem "$source_dir" | New-ISOFile -path e:\iso\app123.iso
Even though the data was only about 5.4gb, there was over 120k files.  When I ran the script, I saw my PS window was “thinking” and when I glanced at my e:\iso folder, it had created the ISO file…  I saw the file was at 0k, so I said to myself, “This is going to error out!”
But I couldn’t have been more wrong!  It exported a file at 5.4gb!  I uploaded the new ISO to an ISO folder on a datastore accessible by the DMZ hosts, mounted it to my VM and BOOM, all my data was there and I was able to copy it!

 

I hope someone else finds this as useful as I did!

 

Ben Liebowitz, VCP, vExpert
NJ VMUG Leader
Share This:

34 thoughts on “Create an ISO file with PowerShell!

  1. Strongly Agree with “you should ALWAYS do before just blatantly running someone else’s code” thanks for mentioning it. Awesome work.

  2. IMGBurn can also create an ISO from a folder or list of files.

    Rather than uploading ISOs to datastores if it is a one-time thing, I open the console to the VM with VRMC and you can mount a local ISO direct from your computer. Once done, just unmount the iso or close the VRMC window and nothing left behind to clean up.

    1. Yeah, I’ve done it that way too. I have an ISO folder on our file server that I share via NFS and present to my VMware hosts. This lets me put the ISOs on a network drive and still access them via my hosts. 🙂

  3. Hi Ben,

    nice Tool! I’m sitting in the Homeoffice and have to get some files into my VMs but they dont have internet access or some other local stuff. (Its an cutted development environment)

    So i “ISO” the files i need, and bind the iso to the host where i can just use it as a CD.

    Nice sharing!

    but, there is a little grammar Error and could newbies think it wont work:

    You call the Function with “New-ISOFile -path …

    The Function itself is New-IsoFile.

    1. I see how that could be confusing. I didn’t create the function and that’s just how I named MY script, you can save it with whatever name you wish. 🙂

  4. Thank you very much for this!
    It works, but I cannot boot from the created ISO file. I copied the content from a windows 10 x64 iso into a local folder, added the autounattend.xml and executed your script to create the ISO file. It doesn’t get detected in the VM as bootable media. What might the error be?

    1. This script won’t create a boot able iso. I’m not sure what has to be done to do that. Sorry.

  5. Hi! What PS version should I have installed? On PS 6.2.2 the script doesn’t work. There’s error:
    Add-Type : A parameter cannot be found that matches parameter name ‘CompilerParameters’.
    At E:\work\MSPV\New_ISOfile.psm1:99 char:16
    + Add-Type -CompilerParameters $cp -TypeDefinition @’
    + ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidArgument: (:) [Add-Type], ParameterBindingException
    + FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.Commands.AddTypeCommand
    Unable to find type [ISOFile].
    At E:\work\MSPV\New_ISOfile.psm1:157 char:5
    + [ISOFile]::Create($Target.FullName,$Result.ImageStream,$Result.Bl …
    + ~~~~~~~~~
    + CategoryInfo : InvalidOperation: (ISOFile:TypeName) [], RuntimeException
    + FullyQualifiedErrorId : TypeNotFound

    Note: I’ve saved your script as .psm1 file and referencing to it from main script using Import-Module for modularity sake. ) I’m on Windows 10 x64 Pro if that’s matter.

    1. Sorry, I don’t have a lot of experience with .psm1 files. My script was saved as .ps1 and I was able to execute it in various PS versions. For me, most of the time, it was run on a Windows 2016 server running PS version 5.

  6. Hi Ben,

    Nice Post! Thank you so much for sharing this valuable post on Create an ISO file with PowerShell!. The code is working great! Keep sharing… Keep Sharing…

    Cheers,
    Abhay

  7. Hi Ben,

    Awesome Post! Thank you so much for sharing this informative post on creating an iso file with powershell. And it really works great & creates a standard ISO file. And I also want to suggest to add a section like this —> https://bit.ly/createiso .Keep up the Good Work… Keep Sharing…
    Hope it helps 🙂

    Thanks,
    Abhay

  8. It looks great, create the iso and all, but when I try to use theiso inside a VM, I get an error saying it’s not ISO 9660 standard, is there a way to fix and make it standard |?

  9. Hi Ben,
    Thank you for sharing such a nice tool,
    I am not expert in PowerShell, so I could not get any result when I apply below two line of command
    after I created the Skript and saved as CreatesIsoImage.ps1
    Could you please help me how to make this script run in a way that it will create an iso file form the given path.
    $source_dir=$source_dir=”D:\Zenon\V8.00.53129″
    Get-ChildItem “$source_dir” | C:\Users\Guendogdu\Documents\PowerShellScripts\CreatesIsoImage.ps1 -path D:\ISO\V8.00.53129.iso
    Here in my example D is portable SSD which is connected through USB 3.0 port.

    Br
    Volkan Gündogdu

    1. You have to have the function New-IsoFile code at the top of the script for the New-ISOFile command to work.

      $source_dir = “D:\Zenon\V8.00.53129”
      get-childitem “$source_dir” | New-ISOFile -path d:\ISO\V8.00.53129.iso

  10. I can’t get the code to work…
    Im on standard Windows 10.
    I’ve created a file “New-IsoFile.ps1″ at my desktop”.
    Originally I tryed to write the execure code in the bottom of the script to just point to my E-drive where my dvds show up, but could not get that to work.
    Now I’ve made a seperate script file on my desktop called “Convert E-Drive to ISO.ps1” with the execute code in, but I can’t figure out how to run it like that.
    Running the execute code in PS terminal produces an error:
    —————–
    New-IsoFile : The term ‘New-IsoFile’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path
    was included, verify that the path is correct and try again.
    At C:\Users\power\OneDrive\Skrivebord\Convert E-Drive to ISO.ps1:4 char:31
    + get-childitem “$source_dir” | New-IsoFile -path e:\iso\app123.iso
    + ~~~~~~~~~~~
    + CategoryInfo : ObjectNotFound: (New-IsoFile:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException
    ————————-
    I’ve never done powershell before and I’m honestly a bit lost.
    Is it possible to make a runnable script that will just grab the contents of the E-drive and create an .ISO-file at the same location as the script? I belive that would indeed be ease of use. 😉

    1. First, I would combine the function and the execution of the function into the same script and save it.

      function New-IsoFile
      {
      <# .Synopsis Creates a new .iso file .Description The New-IsoFile cmdlet creates a new .iso file containing content from chosen folders .Example New-IsoFile "c:\tools","c:Downloads\utils" This command creates a .iso file in $env:temp folder (default location) that contains c:\tools and c:\downloads\utils folders. The folders themselves are included at the root of the .iso image. .Example New-IsoFile -FromClipboard -Verbose Before running this command, select and copy (Ctrl-C) files/folders in Explorer first. .Example dir c:\WinPE | New-IsoFile -Path c:\temp\WinPE.iso -BootFile "${env:ProgramFiles(x86)}\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\efisys.bin" -Media DVDPLUSR -Title "WinPE" This command creates a bootable .iso file containing the content from c:\WinPE folder, but the folder itself isn't included. Boot file etfsboot.com can be found in Windows ADK. Refer to IMAPI_MEDIA_PHYSICAL_TYPE enumeration for possible media types: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366217(v=vs.85).aspx .Notes NAME: New-IsoFile AUTHOR: Chris Wu LASTEDIT: 03/23/2016 14:46:50 #>

      [CmdletBinding(DefaultParameterSetName=’Source’)]Param(
      [parameter(Position=1,Mandatory=$true,ValueFromPipeline=$true, ParameterSetName=’Source’)]$Source,
      [parameter(Position=2)][string]$Path = “$env:temp\$((Get-Date).ToString(‘yyyyMMdd-HHmmss.ffff’)).iso”,
      [ValidateScript({Test-Path -LiteralPath $_ -PathType Leaf})][string]$BootFile = $null,
      [ValidateSet(‘CDR’,’CDRW’,’DVDRAM’,’DVDPLUSR’,’DVDPLUSRW’,’DVDPLUSR_DUALLAYER’,’DVDDASHR’,’DVDDASHRW’,’DVDDASHR_DUALLAYER’,’DISK’,’DVDPLUSRW_DUALLAYER’,’BDR’,’BDRE’)][string] $Media = ‘DVDPLUSRW_DUALLAYER’,
      [string]$Title = (Get-Date).ToString(“yyyyMMdd-HHmmss.ffff”),
      [switch]$Force,
      [parameter(ParameterSetName=’Clipboard’)][switch]$FromClipboard
      )

      Begin {
      ($cp = new-object System.CodeDom.Compiler.CompilerParameters).CompilerOptions = ‘/unsafe’
      if (!(‘ISOFile’ -as [type])) {
      Add-Type -CompilerParameters $cp -TypeDefinition @’
      public class ISOFile
      {
      public unsafe static void Create(string Path, object Stream, int BlockSize, int TotalBlocks)
      {
      int bytes = 0;
      byte[] buf = new byte[BlockSize];
      var ptr = (System.IntPtr)(&bytes);
      var o = System.IO.File.OpenWrite(Path);
      var i = Stream as System.Runtime.InteropServices.ComTypes.IStream;

      if (o != null) {
      while (TotalBlocks– > 0) {
      i.Read(buf, BlockSize, ptr); o.Write(buf, 0, bytes);
      }
      o.Flush(); o.Close();
      }
      }
      }
      ‘@
      }

      if ($BootFile) {
      if(‘BDR’,’BDRE’ -contains $Media) { Write-Warning “Bootable image doesn’t seem to work with media type $Media” }
      ($Stream = New-Object -ComObject ADODB.Stream -Property @{Type=1}).Open() # adFileTypeBinary
      $Stream.LoadFromFile((Get-Item -LiteralPath $BootFile).Fullname)
      ($Boot = New-Object -ComObject IMAPI2FS.BootOptions).AssignBootImage($Stream)
      }

      $MediaType = @(‘UNKNOWN’,’CDROM’,’CDR’,’CDRW’,’DVDROM’,’DVDRAM’,’DVDPLUSR’,’DVDPLUSRW’,’DVDPLUSR_DUALLAYER’,’DVDDASHR’,’DVDDASHRW’,’DVDDASHR_DUALLAYER’,’DISK’,’DVDPLUSRW_DUALLAYER’,’HDDVDROM’,’HDDVDR’,’HDDVDRAM’,’BDROM’,’BDR’,’BDRE’)

      Write-Verbose -Message “Selected media type is $Media with value $($MediaType.IndexOf($Media))”
      ($Image = New-Object -com IMAPI2FS.MsftFileSystemImage -Property @{VolumeName=$Title}).ChooseImageDefaultsForMediaType($MediaType.IndexOf($Media))

      if (!($Target = New-Item -Path $Path -ItemType File -Force:$Force -ErrorAction SilentlyContinue)) { Write-Error -Message “Cannot create file $Path. Use -Force parameter to overwrite if the target file already exists.”; break }
      }

      Process {
      if($FromClipboard) {
      if($PSVersionTable.PSVersion.Major -lt 5) { Write-Error -Message ‘The -FromClipboard parameter is only supported on PowerShell v5 or higher’; break }
      $Source = Get-Clipboard -Format FileDropList
      }

      foreach($item in $Source) {
      if($item -isnot [System.IO.FileInfo] -and $item -isnot [System.IO.DirectoryInfo]) {
      $item = Get-Item -LiteralPath $item
      }

      if($item) {
      Write-Verbose -Message “Adding item to the target image: $($item.FullName)”
      try { $Image.Root.AddTree($item.FullName, $true) } catch { Write-Error -Message ($_.Exception.Message.Trim() + ‘ Try a different media type.’) }
      }
      }
      }

      End {
      if ($Boot) { $Image.BootImageOptions=$Boot }
      $Result = $Image.CreateResultImage()
      [ISOFile]::Create($Target.FullName,$Result.ImageStream,$Result.BlockSize,$Result.TotalBlocks)
      Write-Verbose -Message “Target image ($($Target.FullName)) has been created”
      $Target
      }
      }

      $source_dir = “Z:\Install\App123”
      get-childitem “$source_dir” | New-ISOFile -path e:\iso\app123.iso

      Then, open a powershell window, and execute the file:
      .\New-IsoFile.ps1

  11. Thanks for sharing. The original link is dead now but this was really helpful.
    Also someone suggested creating bootable ISOs,
    This will do that. (Windows boot ISOs in my case)
    Instructions in the script comments up the top.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.