Skip to content

Instantly share code, notes, and snippets.

@jonathanelbailey
Last active December 20, 2023 19:28
Show Gist options
  • Save jonathanelbailey/c021fe5791d8baf42e7924215f2502cd to your computer and use it in GitHub Desktop.
Save jonathanelbailey/c021fe5791d8baf42e7924215f2502cd to your computer and use it in GitHub Desktop.
a powershell module that allows for the modification of ISO images.
# this function grabs the iso image from the user supplied http location.
function get-isoimage{
[CmdletBinding()]
param(
$uri,
$workingfolder
)
begin{
$file_name = $uri -replace "http://[\s\S]+\/([\s\S]+\.iso)$",'$1'
$out_file = Join-Path $workingfolder -ChildPath $file_name
}
process{
$web_req = measure-command {Invoke-WebRequest -Uri $uri -OutFile $out_file}
}
end{
Write-host "time:" + $web_req.Minutes + "min"
Write-Output gci $out_file
}
}
# this function creates the working directory that the image will be working out of.
function new-isoworkspace{
[CmdletBinding()]
param(
$imagepath,
$workingfolder
)
begin{
$mount_folder = Join-Path $workingfolder -ChildPath "mount"
}
process{
$mount_obj = Mount-DiskImage -ImagePath $imagepath -StorageType ISO
$drive = ($mount_obj | Get-Volume).DriveLetter + ":"
copy-item $drive\* $workingfolder -force -recurse
}
end{
Write-Output (gci $mount_folder)
}
}
# taken from https://gallery.technet.microsoft.com/scriptcenter/New-ISOFile-function-a8deeffd, created by Chris Wu
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
}
}
$iso_uri = 'http://jenkins.ovirt.org/job/ovirt-node-ng_ovirt-4.1_build-artifacts-el7-x86_64/lastSuccessfulBuild/artifact/exported-artifacts/ovirt-node-ng-installer-ovirt-4.1-2017031904.iso'
$working_folder = join-path $env:temp -childpath 'work'
get-isoimage -uri $iso_uri -workingfolder $working_folder
$iso_name = 'ovirt-node-ng-installer-ovirt-4.1-2017031904.iso'
$image_path = join-path $working_folder -childpath $iso_name
new-isoworkspace -imagepath $image_path -workingfoler $working_folder
$ks_uri = 'https://gist.githubusercontent.com/jonathanelbailey/b2ba868d39491f4e6a7497010b0f6b18/raw/'
$ks_regexp = 'inst\.ks=hd:LABEL=CentOS\\x207\\x20x86_64:\/interactive-defaults\.ks'
$isolinux_cfg = join-path \temp\work\mount -ChildPath "isolinux\isolinux.cfg"
$content = get-content $isolinux_cfg -raw
($content -replace $ks_regexp,"inst.ks=$ks_uri").replace("`r`n","`n")| Set-Content $isolinux_cfg
$new_iso_path = join-path $working_folder -childpath $iso_name
dir $mount_folder | new-isoimage -path $new_iso_path -title "Ovirt-Node-Next-4.1-x86_64"
@melMass
Copy link

melMass commented May 1, 2022

Hey thanks for this. A few bugs:

New-IsoWorkspace

  • Line 35 $workingfolder should be $mount_folder
  • You want to create the mount dir
  • the begin process end pattern does not really make sense here.
function New-IsoWorkspace {
	[CmdletBinding()]
	param(
		$imagepath,
		$workingfolder
	)
	$mount_folder = Join-Path $workingfolder -ChildPath "mount"
	mkdir $mount_folder | out-null
	$mount_obj = Mount-DiskImage -ImagePath $imagepath -StorageType ISO
	$drive = ($mount_obj | Get-Volume).DriveLetter + ":"
	copy-item $drive\* $mount_folder -force -recurse | out-null
	Write-Output (Get-ChildItem $mount_folder)
}

And here is the updated New-IsoFile function for PowerShell 6+:

  • CompilerParameters was replaced by CompilerOptions and simply expects a string:
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 {
		if (!('ISOFile' -as [type])) {
			Add-Type -CompilerOptions "/unsafe" -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
	}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment