3

I am trying to create a configuration file to define exclusions for an image capture as described in DISM Configuration List and WimScript.ini Files. The exclusion rules seem to be ignored when I pass my configuration file using the -ConfigFilePath parameter of the New-WindowsImage cmdlet, but I notice they do work when I pass the same configuration file to the equivalent DISM /Capture-Image command. What's going on here?

For example, if I create this directory structure...

  • Source\
    • Exclude all files\
      • 1.dat
      • 2.dat
      • 3.dat
      • 4.dat
      • 5.dat
    • Exclude even files\
      • 1.dat
      • 2.dat
      • 3.dat
      • 4.dat
      • 5.dat
    • Exclude no files\
      • 1.dat
      • 2.dat
      • 3.dat
      • 4.dat
      • 5.dat

...and create this wimscript.ini file...

[ExclusionList]
\Exclude all files\*
\Exclude even files\2.dat
\Exclude even files\4.dat

...then after running this command...

PS> New-WindowsImage -ImagePath 'CapturedByDismModule.wim' -CapturePath 'Source' -Name 'CapturedByDismModule' -ConfigFilePath 'wimscript.ini'

...CapturedByDismModule.wim contains everything under the Source directory. Nothing was excluded.

If, however, I run this command...

PS> Dism.exe /Capture-Image /ImageFile:CapturedByDismExe.wim /CaptureDir:Source /Name:CapturedByDismExe /ConfigFile:wimscript.ini

...then CapturedByDismExe.wim contains this image...

  • [CapturedByDismExe]
    • Exclude all files\
    • Exclude even files\
      • 1.dat
      • 3.dat
      • 5.dat
    • Exclude no files\
      • 1.dat
      • 2.dat
      • 3.dat
      • 4.dat
      • 5.dat

The exclusions worked as expected. So why don't the same parameters passed to New-WindowsImage produce the same image?

Here are the relevant version numbers of my system:

Component Version Command
Windows 10 1809 Build 17763.379 winver.exe
Dism.exe 10.0.17763.1 [System.Diagnostics.FileVersionInfo]::GetVersionInfo((Get-Command -Name 'Dism.exe').Path).ProductVersion
PowerShell 5.1.17763.316 $PSVersionTable.PSVersion.ToString()
DISM module 3.0 (Get-Command -Name 'New-WindowsImage').Module.Version.ToString()
DISM module 10.0.17763.1 [System.Diagnostics.FileVersionInfo]::GetVersionInfo((Get-Command -Name 'New-WindowsImage').DLL).ProductVersion

1 Answers1

2

This appears to be a bug in the DISM PowerShell module.

Through...ways I found that the Add-WindowsImage, New-WindowsCustomImage, and New-WindowsImage cmdlets implement the exclusion behavior by registering a callback method with the underlying API that is then called once for each directory or file to be captured. Through the callback the cmdlet is passed the full path of each candidate directory or file in the capture directory and returns a value indicating if it should be included or excluded. The cmdlet determines this by matching the path against the exclusion rules it parsed from the configuration file.

To transform each candidate object's absolute path into one that can be matched by the paths in the exclusion rules, the cmdlet strips from the beginning of that absolute path the number of characters equal to the length of the capture path passed to the cmdlet. (Got that?) This works correctly if the capture path is an absolute path, but, unfortunately, will likely end up failing to exclude anything if it's not.

To illustrate, if everything in the question takes place in a C:\Test directory and C:\Test\Source is passed for the -CapturePath parameter, then for the first candidate file it evaluates as follows...

   Candidate absolute path: C:\Test\Source\Exclude all files\1.dat
              Capture path: C:\Test\Source
Candidate transformed path:               \Exclude all files\1.dat
 Applicable exclusion rule:               \Exclude all files\*
    Exclusion rule matched: Yes

Pass Source for the -CapturePath parameter, like I did, and it instead looks like this...

   Candidate absolute path: C:\Test\Source\Exclude all files\1.dat
              Capture path: Source
Candidate transformed path:       t\Source\Exclude all files\1.dat
 Applicable exclusion rule:               \Exclude all files\*
    Exclusion rule matched: No! (transformed path prefixed with "t\Source")

Note that the cmdlets' rudimentary method of getting a capture-relative path from an absolute path also causes problems if you so much as specify an absolute -CapturePath with a trailing backslash...

   Candidate absolute path: C:\Test\Source\Exclude all files\1.dat
              Capture path: C:\Test\Source\
Candidate transformed path:                Exclude all files\1.dat
 Applicable exclusion rule:               \Exclude all files\*
    Exclusion rule matched: No! (transformed path prefixed with "\")

Passing a capture path of C:\Test\Source\ to New-WindowsImage results in another WIM file with nothing excluded, whereas Dism.exe handles it correctly.

TL;DR Since the cmdlets aren't resolving the -CapturePath parameter to a normalized, absolute path, the workaround, of course, is to do so yourself. Thus, if I capture with this command instead...

PS> New-WindowsImage -ImagePath 'CapturedByDismModule.wim' -CapturePath (Get-Item -Path 'Source').FullName -Name 'CapturedByDismModule' -ConfigFilePath 'wimscript.ini'

...the exclusions work as expected. Of course, passing an absolute path literal to -CapturePath works just as well.