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
):
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])
}
}
}
}