DarkLite1
DarkLite1

Reputation: 14705

Get DFS Server name and local path from UNC path

I'm working on Windows Server 2012 which comes by default with various DFS CmdLets.

What I'm trying to do is get the ComputerName and the local path of a specific UNC share, that is hosted by other DFS servers (Win Srv 2008 R2) on the domain.

An example:

Get-DfsnFolderTarget -Path '\\domain.net\share\folder\'

Expected result:

ComputerName = 'Server1'
Path         = 'E\Home\folder'

I'm not really a network engineer, but I can't seem to find a way to retrieve this information based on the UNC path. Every time I try the CmdLet above I get an error:

Get-DfsnFolderTarget : Cannot get DFS folder properites on "\\domain.net\share\folder\"
At line:1 char:1
+ Get-DfsnFolderTarget -Path '\\domain.net\share\folder\'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (MSFT_DfsNamespaceFolderTarget:ROOT\Microsoft\...aceFolderTarget) [Ge 
   t-DfsnFolderTarget], CimException
    + FullyQualifiedErrorId : Windows System Error 1168,Get-DfsnFolderTarget

Get-DfsnFolderTarget : The requested object could not be found.
At line:1 char:1
+ Get-DfsnFolderTarget -Path '\\domain.net\share\folder\'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (MSFT_DfsNamespaceFolderTarget:ROOT\Microsoft\...aceFolderTarget) [Ge 
   t-DfsnFolderTarget], CimException
    + FullyQualifiedErrorId : MI RESULT 6,Get-DfsnFolderTarget

When trying a Test-Path \\domain.net\share\folder\ it does say that it can be found. So I don't really understand.

I'm trying to retrieve this information (ComputerName and local path): enter image description here

Upvotes: 4

Views: 21895

Answers (1)

DarkLite1
DarkLite1

Reputation: 14705

I solved my own problem by hours of Googling and tweaking some stuff. This is what I ended up with for anyone else interested:

Function Get-DFSDetails {
<# 
    .SYNOPSIS   
        Gets DFS details for a UNC path.
    
    .DESCRIPTION
        The Get-DFSDetails CmdLet gets DFS details like DFS Server name, DFS Share name and the local path on the DFS Server for a specific UNC path.
    
    .PARAMETER Credentials 
        PowerShell credential object used to connect to the DFS Server to retrieve the local path on the server.
    
    .PARAMETER Path 
        Specifies a UNC path for the folder.
    
    .EXAMPLE
        Get-DFSDetails -Path '\\domain.net\HOME\Bob' -Credentials $Credentials
        Gets the DFS details for the UNC path '\\domain.net\HOME\Bob'
    
        Path         : \\domain.net\HOME\Bob
        ComputerName : SERVER1.DOMAIN.NET
        ComputerPath : E:\HOME\Bob
        ShareName    : HOME
    
    .EXAMPLE
        '\\domain.net\HOME\Mike', '\\domain.net\HOME\Jake' | Get-DFSDetails -Credentials $Credentials
        Gets the DFS details for the UNC paths '\\domain.net\HOME\Mike' and '\\domain.net\HOME\Jake'
    
        Path         : \\domain.net\HOME\Mike
        ComputerName : SERVER1.DOMAIN.NET
        ComputerPath : E:\HOME\Mike
        ShareName    : HOME 
            
        Path         : \\domain.net\HOME\Jake
        ComputerName : SERVER2.DOMAIN.NET
        ComputerPath : E:\HOME\Jake
        ShareName    : HOME    
    
    .NOTES
        CHANGELOG
        2015/10/27 Function born #>
    
    [CmdLetBinding()]
    Param (
        [Parameter(Mandatory, Position=0)]
        [PSCredential]$Credentials,
        [Parameter(Mandatory, ValueFromPipeline, Position=1)]
        [ValidateScript({
            if (Test-Path -LiteralPath $_ -PathType Container) {$true}
            else {throw "Could not find path '$_'"}
        })]
        [String[]]$Path
    )
    
    Begin {
$signature = @'
using System;
using System.Collections.Generic;
using System.Management.Automation;
using System.Runtime.InteropServices;
    
public class Win32Api
{
    [DllImport("netapi32.dll", SetLastError = true)]
    private static extern int NetApiBufferFree(IntPtr buffer);
    
    [DllImport("Netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern int NetDfsGetClientInfo
    (
    [MarshalAs(UnmanagedType.LPWStr)] string EntryPath,
    [MarshalAs(UnmanagedType.LPWStr)] string ServerName,
    [MarshalAs(UnmanagedType.LPWStr)] string ShareName,
    int Level,
    ref IntPtr Buffer
    );
    
    public struct DFS_INFO_3
    {
        [MarshalAs(UnmanagedType.LPWStr)]
        public string EntryPath;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string Comment;
        public UInt32 State;
        public UInt32 NumberOfStorages;
        public IntPtr Storages;
    }
    public struct DFS_STORAGE_INFO
    {
        public Int32 State;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string ServerName;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string ShareName;
    }
    
    public static List<PSObject> NetDfsGetClientInfo(string DfsPath)
    {
        IntPtr buffer = new IntPtr();
        List<PSObject> returnList = new List<PSObject>();
    
        try
        {
            int result = NetDfsGetClientInfo(DfsPath, null, null, 3, ref buffer);
    
            if (result != 0)
            {
                throw (new SystemException("Error getting DFS information"));
            }
            else
            {
                DFS_INFO_3 dfsInfo = (DFS_INFO_3)Marshal.PtrToStructure(buffer, typeof(DFS_INFO_3));
    
                for (int i = 0; i < dfsInfo.NumberOfStorages; i++)
                {
                    IntPtr storage = new IntPtr(dfsInfo.Storages.ToInt64() + i * Marshal.SizeOf(typeof(DFS_STORAGE_INFO)));
    
                    DFS_STORAGE_INFO storageInfo = (DFS_STORAGE_INFO)Marshal.PtrToStructure(storage, typeof(DFS_STORAGE_INFO));
    
                    PSObject psObject = new PSObject();
    
                    psObject.Properties.Add(new PSNoteProperty("State", storageInfo.State));
                    psObject.Properties.Add(new PSNoteProperty("ServerName", storageInfo.ServerName));
                    psObject.Properties.Add(new PSNoteProperty("ShareName", storageInfo.ShareName));
    
                    returnList.Add(psObject);
                }
            }
        }
        catch (Exception e)
        {
            throw(e);
        }
        finally
        {
            NetApiBufferFree(buffer);
        }
        return returnList;
    }
}
'@
    
        if (-not ('Win32Api' -as [Type])) {
            Add-Type -TypeDefinition $signature
        }
    }
    
    Process {
        foreach ($P in $Path) {
            Try {
                # State 6 notes that the DFS path is online and active
                $DFS = [Win32Api]::NetDfsGetClientInfo($P) | Where-Object { $_.State -eq 6 } | 
                    Select-Object ServerName, ShareName
    
                $SessionParams = @{
                    Credential    = $Credentials
                    ComputerName  = $DFS.ServerName
                    SessionOption = New-CimSessionOption -Protocol Dcom
                }
                $CimParams = @{
                    CimSession = New-CimSession @SessionParams
                    ClassName  = 'Win32_Share'
                }
                $LocalPath = Get-CimInstance @CimParams | Where-Object Name -EQ $DFS.ShareName | 
                    Select-Object -ExpandProperty Path
    
                [PSCustomObject][Ordered]@{
                    Path         = $P
                    ComputerName = $DFS.ServerName
                    ComputerPath = $LocalPath + ($P -split $DFS.ShareName, 2)[1]
                    ShareName    = $DFS.ShareName
                }
            }
            Catch {
                Write-Error $Error[0].Exception.Message
                $Global:Error.Remove($Global:Error[0])
            }
        }
    }
}

Upvotes: 9

Related Questions