Skip to content

Instantly share code, notes, and snippets.

@Ioan-Popovici
Last active January 4, 2019 20:52
Show Gist options
  • Save Ioan-Popovici/e5add7586e7c825743b8710a354bc149 to your computer and use it in GitHub Desktop.
Save Ioan-Popovici/e5add7586e7c825743b8710a354bc149 to your computer and use it in GitHub Desktop.
Injects packages into a Windows Image image using PowerShell DISM CommandLets.
<#
.SYNOPSIS
Injects packages into a WIM.
.DESCRIPTION
Injects packages into a Windows Image image using PowerShell DISM CommandLets.
.EXAMPLE
Update-OfflineWindowsImage.ps1
.NOTES
Created by Ioan Popovici
Requirements
ADK Windows 10, Windows 8 or higher.
.LINK
https://SCCM.Zone/Update-OfflineWindowsImage-CREDIT
.LINK
https://SCCM.Zone/Update-OfflineWindowsImage
.LINK
https://SCCM.Zone/Update-OfflineWindowsImage-CHANGELOG
.LINK
https://SCCM.Zone/Update-OfflineWindowsImage-GIT
.LINK
https://SCCM.Zone/Issues
.COMPONENT
Windows Servicing
.FUNCTIONALITY
Injects packages into a WIM
#>
##*=============================================
##* VARIABLE DECLARATION
##*=============================================
#region VariableDeclaration
## Get script path and name
[String]$ScriptPath = [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Definition)
[String]$ScriptName = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Definition)
## Set Paths and Image Index
[String]$MountPath = (Join-Path -Path $ScriptPath -ChildPath '\Mount')
[String]$ScratchPath = (Join-Path -Path $ScriptPath -ChildPath '\Scratch')
[String]$UpdatesPath = (Join-Path -Path $ScriptPath -ChildPath '\Updates')
[String]$LogPath = (Join-Path -Path $ScriptPath -ChildPath 'DISM.log')
## Set Environment Path in order to use the latest DISM and System32. This is set for current session only, no need to remove it afterwards.
$Env:Path = 'C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM;C:\Windows\System32'
#endregion
##*=============================================
##* END VARIABLE DECLARATION
##*=============================================
##*=============================================
##* SCRIPT BODY
##*=============================================
#region ScriptBody
## Create Mount directory if it does not exist
If ((Test-Path $MountPath) -eq $False) {
New-Item -Path $MountPath -Type Directory | Out-Null
}
## Create Scratch directory if it does not exist
If ((Test-Path $ScratchPath) -eq $False) {
New-Item -Path $ScratchPath -Type Directory | Out-Null
}
## Prompt for WIM to service, don't allow null input
Do {
[Array]$ImageFileInfo = Get-ChildItem -Path $ScriptPath -Filter '*.wim' | Select-Object -Property `
@{Label='Name';Expression={($_.Name)}},
@{Label='Size (GB)';Expression={'{0:N2}' -f ($_.Length / 1GB)}},
@{Label='Path';Expression={($_.FullName)}} | Out-GridView -PassThru -Title 'Choose image to service. Do not use multiple selection!'
}
While ($ImageFileInfo.Length -eq 0)
# Set image file name and path, process only the first selection
[String]$ImageFile = ($ImageFileInfo | Select-Object -First 1).Name
[String]$ImagePath = ($ImageFileInfo | Select-Object -First 1).Path
## Prompt for windows version, don't allow null input
Do {
[Array]$ImageIndexInfo = Get-WindowsImage -ImagePath $ImagePath | Select-Object -First 1 -Property `
@{Label='Index';Expression={($_.ImageIndex)}},
@{Label='Name';Expression={($_.ImageName)}},
@{Label='Description';Expression={($_.ImageDescription)}},
@{Label='Size (GB)';Expression={'{0:N2}' -f ($_.ImageSize / 1GB)}} | Out-GridView -PassThru -Title 'Choose Windows version. Do not use multiple selection!'
}
While ($ImageIndexInfo.Length -eq 0)
# Set image name and index, process only the first selection
[String]$ImageName = ($ImageIndexInfo | Select-Object -First 1).Name
[Int]$ImageIndex = ($ImageIndexInfo | Select-Object -First 1).Index
## Prompt for updates to apply , don't allow null input
Do {
[Array]$Updates = Get-ChildItem -Path $UpdatesPath | Select-Object -Property `
@{Label='Name';Expression={($_.Name)}},
@{Label='Size (MB)';Expression={'{0:N2}' -f ($_.Length / 1MB)}},
@{Label='Path';Expression={($_.FullName)}} | Out-GridView -PassThru -Title 'Choose updates to apply. Use CTRL for multiple selection.'
}
While ($Updates.Length -eq 0)
## Set Backup path and Backup WIM file
[String]$BackupImagePath = ($ImagePath -replace '.{3}$')+'bkp'
Write-Host "`n`n`n`n`n`nBacking up $ImagePath to $BackupImagePath ..." -ForegroundColor 'Yellow' -BackgroundColor 'Black'
$Operation = Copy-Item -Path $ImagePath -Destination $BackupImagePath -Force
## Mount WIM
Write-Host "`nMounting Image..." -ForegroundColor 'Yellow' -BackgroundColor 'Black'
$Operation = Mount-WindowsImage -ImagePath $ImagePath -Index $ImageIndex -Path $MountPath -ScratchDirectory $ScratchPath -LogPath $LogPaths
# Display mounted image status
$Operation = Get-WindowsImage -Mounted -ScratchDirectory $ScratchPath -LogPath $LogPath | Out-String
Write-Host "`nMounted Image Info:`n $Operation" -ForegroundColor 'Yellow' -BackgroundColor 'Black'
## Add packages to the image
Write-Host "Servicing Image...`n" -ForegroundColor 'Yellow' -BackgroundColor 'Black'
$Updates | ForEach-Object {
Try {
Write-Host "Adding $($_.Name) to the Image." -ForegroundColor 'Yellow' -BackgroundColor 'Black'
$Operation = Add-WindowsPackage -Path $MountPath -PackagePath $_.Path -ScratchDirectory $ScratchPath -LogPath $LogPath
Write-Host "Adding Package to the Image - Successful!" -ForegroundColor 'Green' -BackgroundColor 'Black'
}
Catch {
Write-Host "Adding Package to the Image - Failed!" -ForegroundColor 'Red' -BackgroundColor 'Black'
}
}
## Unmount and save servicing changes to the image
Write-Host "`nCommitting Changes and Dismounting Image..." -ForegroundColor 'Yellow' -BackgroundColor 'Black'
$Operation = Dismount-WindowsImage -Path $MountPath -ScratchDirectory $ScratchPath -LogPath $LogPath -Save -CheckIntegrity
## Set Export path and name
[String]$ExportImagePath = ($ImagePath -replace '.{4}$')+'_Serviced.wim'
## Export only selected index also removing unnecessary resource files created during servicing
Write-Host "`nExporting Serviced Image..." -ForegroundColor 'Yellow' -BackgroundColor 'Black'
$Operation = Export-WindowsImage -SourceImagePath $ImagePath -SourceIndex $ImageIndex -DestinationImagePath $ExportImagePath -DestinationName $ImageName -ScratchDirectory $ScratchPath -LogPath $LogPath
## Wait for keypress
Write-Host "Press any key to continue ..."
$WaitforKeyPress = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
#endregion
##*=============================================
##* END SCRIPT BODY
##*=============================================
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment