windowspowershellscriptingpowershell-4.0system-administration

Unique Volume ID path of a volume seemingly corrupted while tryig to create a new junction with New-Item; How to fix, bypass or work around?


I am writing a PowerShell script where-in I create a directory junction.

During the development of the script I ran the New-Item command, saw the junction entry was created without looking closer at it. It turns out that the link/junction is created and corrupted at the same time, or so it appears. Here's what I mean...

I want to create absolute junctions (as opposed to relative ones) such that the links are independant of the drive letters assigned. Therefore, I use volume IDs reported by mountvol and its PS counterpart Get-Volume with some minor PS gymnastics (still learning PS). The script needs a reference of the current assigned drive letter to obtain the volume ID.

(Get-Volume | Where-Object {$_.DriveLetter -eq 'c'}).Path

or

(Get-WmiObject win32_volume | Select-Object DriveLetter, DeviceID | Where-Object {$_.DriveLetter -eq 'c:' }).DeviceID

achieves the desired volume path (with subtle differences - notice the colon in one and lack there of in the other). Also the dot notation used is avaiable only PS version 3 and up, I beleive.

Microsoft documentation I can dig describes these as "DOS device paths"

I really do not know how much of this is "DOS" and how much of this is "Windows" and where the line is ever drawn. I also know that Windows has a disk reference schema with \DEVICE\HardDiskX... but PS documentation on device cmdlets is very dry (try get-help Get-NtDeviceProperty) where I thought device path for a volume would be a device property, so, anyway, I did not come across this notation very much other than where I already know it occurs (for eg. eventlogs and diskpart).

So, continuing ahead with what is available, having received the volumeID path via the above cmdlets, irrespective of how you run the New-Item cmdlet, piping, parantethicals or direct string supply of the volumeID path; the New-Item command seemingly corrupts the 'Target' or 'Value' from:

\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee}\Some_Path

to

\??\\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee}\Some_Path

Here are the various ways I invoke New-Item, all with the same result:

New-Item -ItemType "Junction" -Path "Path_and_name_of_junction" -Value "\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee}\Some_Path"

or paths and names in variables like so:

New-Item -ItemType "Junction" -Path $Path_and_name_of_junction -Value $TargetVolumeIDandPath

Junction is created and works fine when used like so:

New-Item -ItemType "Junction" -Path "C:\Junction\Name" -Value "N:\Target\Directory"

It also works fine if the above paths are stored in variables, or if they are supplied by PS cmdlet objects:

New-Item -ItemType "Junction" -Path $PathAndNameOfJunction -Value $TargetVolumeIDandPath

Not knowing what is going on, and why New-Item behaves this way, I tried the following (so far as I can remember - tried few things):

How else can I acheive a junction creation with or without New-Item, in PowerShell that will create an absolute junction link pointing to a volume and path in that volume independant of the Windows assigned drive letter using the above mentioned volumeID path that are available from both mountvol or Get-Volume, OR even \DEVICE\HARDDISKX\ style referencing? Any ideas and assistance in the right directions is really appreciated.

Thanks in Advance.

Edits: Also test Join-Path and Test-Path with the "DOS Device Paths":

PS N:\> Join-Path -Path "path" -ChildPath "childpath"
path\childpath
PS N:\> Join-Path -Path "\\?\Volume{2c8a5b49-09f5-44f9-92ce-d5e8d0c6bf42}\" -ChildPath "childpath"
\\?\Volume{2c8a5b49-09f5-44f9-92ce-d5e8d0c6bf42}\childpath
PS N:\> Join-Path -Path "\\?\Volume{2c8a5b49-09f5-44f9-92ce-d5e8d0c6bf42}\" -ChildPath "Program Files"
\\?\Volume{2c8a5b49-09f5-44f9-92ce-d5e8d0c6bf42}\Program Files
PS N:\> Join-Path -Path $PathProgsVolUid -ChildPath "Program Files"
\\?\Volume{2c8a5b49-09f5-44f9-92ce-d5e8d0c6bf42}\Program Files
PS N:\> Test-Path (Join-Path -Path "path" -ChildPath "childpath")
False
PS N:\> Test-Path (Join-Path -Path "\\?\Volume{2c8a5b49-09f5-44f9-92ce-d5e8d0c6bf42}\" -ChildPath "childpath")
False
PS N:\> Test-Path (Join-Path -Path "\\?\Volume{2c8a5b49-09f5-44f9-92ce-d5e8d0c6bf42}\" -ChildPath "Program Files")
True
PS N:\> Test-Path (Join-Path -Path $PathProgsVolUid -ChildPath "Program Files")
True
PS N:\> Test-Path $PathProgsVolUid
Test-Path : Cannot retrieve the dynamic parameters for the cmdlet. Cannot process argument because the value of
argument "path" is not valid. Change the value of the "path" argument and run the operation again.
At line:1 char:1
+ Test-Path $PathProgsVolUid
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Test-Path], ParameterBindingException
    + FullyQualifiedErrorId : GetDynamicParametersException,Microsoft.PowerShell.Commands.TestPathCommand

PS N:\> Test-Path "\\?\Volume{2c8a5b49-09f5-44f9-92ce-d5e8d0c6bf42}\"
Test-Path : Cannot retrieve the dynamic parameters for the cmdlet. Cannot process argument because the value of
argument "path" is not valid. Change the value of the "path" argument and run the operation again.
At line:1 char:1
+ Test-Path "\\?\Volume{2c8a5b49-09f5-44f9-92ce-d5e8d0c6bf42}\"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Test-Path], ParameterBindingException
    + FullyQualifiedErrorId : GetDynamicParametersException,Microsoft.PowerShell.Commands.TestPathCommand

PS N:\> Test-Path "\\?\Volume{2c8a5b49-09f5-44f9-92ce-d5e8d0c6bf42}"
Test-Path : Cannot retrieve the dynamic parameters for the cmdlet. Cannot process argument because the value of
argument "path" is not valid. Change the value of the "path" argument and run the operation again.
At line:1 char:1
+ Test-Path "\\?\Volume{2c8a5b49-09f5-44f9-92ce-d5e8d0c6bf42}"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Test-Path], ParameterBindingException
    + FullyQualifiedErrorId : GetDynamicParametersException,Microsoft.PowerShell.Commands.TestPathCommand


Solution

  • See: https://winaero.com/create-symbolic-link-windows-10-powershell/

    mklink is a built-in command in cmd.exe and not available directly in PowerShell. However, you can call it from PowerShell by using cmd /c mklink.

    Get-ChildItem D:\MainFolder-Copy -Directory | Foreach {Get-ChildItem (Join-Path C:\MainFolder $_.Name) -Directory} | Foreach {cmd /c mklink /j ($_.FullName -replace 'C:\\MainFolder','D:MainFolder-Copy') $_.FullName}
    

    Or in PowerShell you can use New-Item

    New-Item -ItemType Junction -Path "Link" -Target "Target"
    

    Access Volume Shadow Copy (VSS) snapshots from PowerShell and create a junction using the volume letter specifier: Accessing Volume Shadow Copy (VSS) Snapshots from powershell

    $s1 = (Get-WmiObject -List Win32_ShadowCopy).Create ("C:\", "ClientAccessible")
    $s2 = Get-WmiObject Win32_ShadowCopy | Where-Object { $_.ID -eq $s1.ShadowID }
    $d = $s2.DeviceObject + "\"
    cmd /c mklink /d C:shadowcopy "$d"