Powershell : Get DFS info from folder

The request was, without accessing to the server, only with a list of DFS path (around 200 path), try to get by script the DFS information such as right clicking on the folder then DFS Tab.


As I have 200 DFS path and multiple ReparsePoint inside I won’t do it manually.
Also please note that I didn’t have the permission to use DFSN module for Powershell.

So the idea was as following:

  • The script should take a csv file as input with the DFS path
  • The script should found out which folder have ReparsePoint
  • The script should extract the DFS Tab information and export it to a csv file

So let’s start:

1. Input
2. Script
2.1. Function : Write-InFile
2.2. Function : StartAnalyse
2.3. Function : Get-DFSDetails
2.4. Execute
2.5. The whole script

Input

As mentionned below, the input is going to be a csv with all the DFS path. Simple csv file, just one column call name and the path below.

Script

  • Function : Write-InFile

Nothing very complicated, a function which write in a file.
It take two parameters, the message you want to write in file and the path where the file is.

Function Write-InFile([string]$message, [string]$filepath){
    Add-Content $filepath "$message"
}
  • Function : StartAnalyse

This function is going to parse your DFS to find out the ReparsePoint. I haven’t Recurse in that case, because if it was the case, it will go inside some folders with ReparsePoint which wasn’t my goal. If it didn’t find a ReparsePoint, it will go inside folders until it find one. If it find one, it will get the DFS information through another function.
There are two parameters for this function, $dfs which is use for the export file, and $path which is the actual parsed folder.

Function StartAnalyse{
    param( [string]$path, [string]$dfs )

    $item = Get-ChildItem -Path $path
        $item | ForEach-Object{

        if($_.Attributes -band [System.IO.FileAttributes]::ReparsePoint){
        Write-Host $_.FullName
        Try {
            Get-DFSDetails -Path $_.FullName -strdfs $dfs
        }
         Catch {
                Write-Error $Error[0].Exception.Message
                $Global:Error.Remove($Global:Error[0])
                Write-InFile -message "$dfs;$path;;;$($Error[0].Exception.Message);" -filepath "$folderpath"
            }

    }
    else{
        StartAnalyse -path $_.FullName -dfs $dfs
    }
   }
}
  • Function : Get-DFSDetails

That’s the one you have been waiting for.
Firstable let’s thanks Darklite1, he made the function to get this details in the following stack overflow post. I have edited a bit (remove things I didn’t needed).
The in parameters you will find the $Path which is the folder where you have your ReparsePoint. And I have added $strdfs which is use for generating the report.

Function Get-DFSDetails {
    [CmdLetBinding()]
    Param (
        [Parameter(Mandatory, ValueFromPipeline, Position=0)]
        [ValidateScript({
            if (Test-Path -LiteralPath $_ -PathType Container) {$true}
            else {throw "Could not find path '$_'"}
        })]
        [String[]]$Path,
        [Parameter(Mandatory, Position=0)]
        [String]$strdfs
    )

    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 NetDfsGetClientInfo(string DfsPath)
    {
        IntPtr buffer = new IntPtr();
        List returnList = new List();

        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 {
                $DFS = [Win32Api]::NetDfsGetClientInfo($P) | Where-Object { $_.State -eq 6 } | 
                    Select-Object ServerName, ShareName

                Write-InFile -message "$strdfs;$Path;$($DFS.ServerName);\\$($DFS.ServerName)\$($DFS.ShareName);;" -filepath "$folderpath"


            }
            Catch {
                Write-Error $Error[0].Exception.Message
                $Global:Error.Remove($Global:Error[0])
                Write-InFile -message "$strdfs;$Path;;;;$($Error[0].Exception.Message)" -filepath "$folderpath"
            }
        }
    }
}
  • Execute

How to call the functions above.

#Script
$folderpath = "Path\To\Output\ReportDFS.csv";

$csv = Import-Csv -Path "Path\To\Input.csv" -Delimiter ";"

Write-InFile -message "DFS;DFSPath;Server;ServerPath;Error;" -filepath "$folderpath"

foreach($csvI in $csv){
    $dfs = $csvI.Name

    StartAnalyse -path $dfs -dfs $dfs
}
  • The whole script
Function Write-InFile([string]$message, [string]$filepath){
    Add-Content $filepath "$message"
}

Function Get-DFSDetails {
    [CmdLetBinding()]
    Param (
        [Parameter(Mandatory, ValueFromPipeline, Position=0)]
        [ValidateScript({
            if (Test-Path -LiteralPath $_ -PathType Container) {$true}
            else {throw "Could not find path '$_'"}
        })]
        [String[]]$Path,
        [Parameter(Mandatory, Position=0)]
        [String]$strdfs
    )

    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 NetDfsGetClientInfo(string DfsPath)
    {
        IntPtr buffer = new IntPtr();
        List returnList = new List();

        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 {
                $DFS = [Win32Api]::NetDfsGetClientInfo($P) | Where-Object { $_.State -eq 6 } | 
                    Select-Object ServerName, ShareName

                Write-InFile -message "$strdfs;$Path;$($DFS.ServerName);\\$($DFS.ServerName)\$($DFS.ShareName);;" -filepath "$folderpath"


            }
            Catch {
                Write-Error $Error[0].Exception.Message
                $Global:Error.Remove($Global:Error[0])
                Write-InFile -message "$strdfs;$Path;;;;$($Error[0].Exception.Message)" -filepath "$folderpath"
            }
        }
    }
}

Function StartAnalyse{
    param( [string]$path, [string]$dfs )

    $item = Get-ChildItem -Path $path
        $item | ForEach-Object{

        if($_.Attributes -band [System.IO.FileAttributes]::ReparsePoint){
        Write-Host $_.FullName
        Try {
            Get-DFSDetails -Path $_.FullName -strdfs $dfs
        }
         Catch {
                Write-Error $Error[0].Exception.Message
                $Global:Error.Remove($Global:Error[0])
                Write-InFile -message "$dfs;$path;;;$($Error[0].Exception.Message);" -filepath "$folderpath"
            }

    }
    else{
        StartAnalyse -path $_.FullName -dfs $dfs
    }
   }
}

#Script
$folderpath = "Path\To\Output\ReportDFS.csv";

$csv = Import-Csv -Path "Path\To\Input.csv" -Delimiter ";"

Write-InFile -message "DFS;DFSPath;Server;ServerPath;Error;" -filepath "$folderpath"

foreach($csvI in $csv){
    $dfs = $csvI.Name

    StartAnalyse -path $dfs -dfs $dfs
}