28

I am trying to get a list of folders that are shared on a file share. At the moment I have two test folders:

\\MYPC\Test1

\\MYPC\Test2

This is the code I have at the moment:

$FileServer = Read-Host "Enter file server to search"
$FolderList = Get-ChildItem -Path $FileServer

Write-Host $FolderList

But this comes up with "cannot find the path". I can see examples of how to do this for \\Server\Share as the directory, but is it possible to just search the \\Server?

The Woo
  • 477

14 Answers14

32

Try this:

get-WmiObject -class Win32_Share -computer dc1.krypted.com

Ref: List Shares in Windows w/ PowerShell

Tamerz
  • 444
  • 4
  • 6
24

Powershell is not able too use SMB protocol in order to list the shares on a remote computer. There's only one way of enumerating shares remotely from the command line that I know of, and thats with net view:

C:\Users\mark.henderson>net view \\enetsqnap01
Shared resources at \\enetsqnap01

Share name Type Used as Comment


Backups Disk CallRecordings Disk Download Disk System default share home Disk Home homes Disk System default share Installs Disk Justin Disk Copy of files from Justin laptop michael Disk Multimedia Disk System default share Network Recycle Bin 1 Disk [RAID5 Disk Volume: Drive 1 2 3 4] Public Disk System default share Qsync Disk Qsync Recordings Disk System default share Sales Disk Sales Documents SalesMechanix Disk Server2012 Disk Windows Server 2012 Install Media Usb Disk System default share VMWareTemplates Disk Web Disk System default share The command completed successfully.

This is not particularly parsable on its own, but, you can throw it into an array to process the data line by line:

$sharedFolders = (NET.EXE VIEW \\enetsqnap01) 

You now have an array, and starting at $sharedFolders[7] you have your shares. You could then split on something like a double space - unlikely to appear in a share name itself, and should work unless your share name is very long, only leaving a single space between the share name and the type field:

$sharedFolders[7].split('  ')[0]
Backups

You could process these by using a ForEach and some conditional logic. It wouldn't be perfect, but it should work for most use cases.

For brevity, to just output the filenames to the console:

(net view \\enetsqnap01) | % { if($_.IndexOf(' Disk ') -gt 0){ $_.Split('  ')[0] } }
19

If you want to find the shares of the local machine you can just do Get-SmbShare:

> Get-SmbShare

Name                          ScopeName                     Path                          Description
----                          ---------                     ----                          -----------
ADMIN$                        *                             C:\WINDOWS                    Remote Admin
C$                            *                             C:\                           Default share
5

Expanding on Mark Henderson's answer:

$Servers = ( Get-ADComputer -Filter { DNSHostName -Like '*' }  | Select -Expand Name )
foreach ($Server in $Servers)
{
    (net view $Server) | % { if($_.IndexOf(' Disk ') -gt 0){ $_.Split('      ')[0] } } | out-file C:\file_shares\$Server.txt
}
DingoCC
  • 51
  • 1
  • 3
3

Thanks to Mark Henderson for his solution. I've added a wrapper function to help make this function call more PowerShell friendly. I've used a different approach to breaking up the data (more complex, not better); that can easily be switched around based on preference.

clear-host
function Get-SharedFolder {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$ComputerName 
        ,
        [Parameter(Mandatory = $false)]
        [switch]$GetItem
        ,
        [Parameter(Mandatory = $false)]
        [string[]]$ColumnHeadings = @('Share name','Type','Used as','Comment')  #I suspect these differ depending on OS language?  Therefore made customisable
        ,
        [Parameter(Mandatory = $false)]
        [string]$ShareName = 'Share name' #tell us which of the properties relates to the share name
        #,
        #[Parameter(Mandatory = $false)]
        #[string[]]$Types = @('Disk') # again, likely differs with language.  Also there may be other types to include?
    )
    begin {
        [psobject[]]$Splitter = $ColumnHeadings | %{
            $ColumnHeading = $_
            $obj = new-object -TypeName PSObject -Property @{
                Name = $ColumnHeading
                StartIndex = 0
                Length = 0
            }
            $obj | Add-Member -Name Initialise -MemberType ScriptMethod {
                param([string]$header)
                process {
                    $_.StartIndex = $header.indexOf($_.Name)
                    $_.Length = ($header -replace ".*($($_.Name)\s*).*",'$1').Length
                }
            }
            $obj | Add-Member -Name GetValue -MemberType ScriptMethod {
                param([string]$line)
                process {
                    $line -replace ".{$($_.StartIndex)}(.{$($_.Length)}).*",'$1'
                }
            }
            $obj | Add-Member -Name Process -MemberType ScriptMethod {
                param([psobject]$obj,[string]$line)
                process {
                    $obj | Add-Member -Name $_.Name -MemberType NoteProperty -Value ($_.GetValue($line))
                }
            }
            $obj
        }
    }
    process {
        [string[]]$output = (NET.EXE VIEW $ComputerName)
        [string]$headers = $output[4] #find the data's heading row
        $output = $output[7..($output.Length-3)] #keep only the data rows
        $Splitter | %{$_.Initialise($headers)}
        foreach($line in $output) { 
            [psobject]$result = new-object -TypeName PSObject -Property @{ComputerName=$ComputerName;}
            $Splitter | %{$_.Process($result,$line)}
            $result | Add-Member '_ShareNameColumnName' -MemberType NoteProperty -Value $ShareName
            $result | Add-Member 'Path' -MemberType ScriptProperty -Value {("\\{0}\{1}" -f $this.ComputerName,$this."$($this._ShareNameColumnName)")}
            $result | Add-Member 'Item' -MemberType ScriptProperty -Value {Get-Item ($this.Path)}
            $result | Add-Member -MemberType MemberSet -Name PSStandardMembers -Value ([System.Management.Automation.PSMemberInfo[]]@(New-Object System.Management.Automation.PSPropertySet(‘DefaultDisplayPropertySet’,[string[]](@('ComputerName','Path') + $ColumnHeadings))))
            $result
        }
    }
}

[string[]]$myServers = 'myServer1','myServer2' #amend this line to get the servers you're interested in
[psobject[]]$shares = $myServers | Get-SharedFolder
write-host 'List of Shares' -ForegroundColor Cyan
$shares  | ft -AutoSize
write-host 'Shares as Get-Item output' -ForegroundColor Cyan
$shares  | select -expand Item
1

On Windows 8 or higher and Windows Server 2012 or higher, you can use Get-SmbShare from the SmbShare module.

JamieSee
  • 181
0

Here's a PowerShell one liner that uses net view to enumerate all remote shares a user can see - doesn't mean they have access.

net view | Where {$_ -like "\\*"} | %{$comp = $_.Split(" ")[0]; net view $comp | Where {$_ -like "*Disk*"} | %{$share = $_.Split(" ")[0]; $fullpath = Join-Path $comp $share; $fullpath}}

If you want to see if they have (at least) read access, you can run:

Net view | Where {$_ -like "\\*"} | %{$comp = $_.Split(" ")[0]; net view $comp | Where {$_ -like "*Disk*"} | %{$share = $_.Split(" ")[0]; $fullpath = Join-Path $comp $share; [PSCustomObject]@{Path=$fullpath;HasAccess=$(Test-Path $fullpath -ErrorAction SilentlyContinue)}}}

If you need the output saved you can always pipe it to Export-CSV by throwing the following on after the last bracket:

| Export-CSV "\\path\to\file.csv" -NoTypeInformation

The whole thing is not perfect when net view throws an error but I wrote it based on the comments here and it works pretty well and is helpful for what I need so I thought I'd share. :)

0

Not a real answer.

Just some tidbits. And several things that did NOT work.

Did not work: get-WmiObject

PS C:\> get-WmiObject -class Win32_Share -computer myserver.example.com

Did not work: get-fileshare

https://docs.microsoft.com/en-us/powershell/module/storage/get-fileshare

Did not work: get-smbshare

https://docs.microsoft.com/en-us/powershell/module/smbshare/get-smbshare

Worked: Cygwin/Mobaxterm

Moba (https://mobaxterm.mobatek.net/) will access file shares just fine.

"list all available shares for that server" mode Both these forms work:

$ ls //myserver.example.com/
a b c

$ ls '\myserver.example.com' a b c

"list the contents of a SPECIFIC share on that server" mode Both these forms work:

$ ls //myserver.example.com/a
foo bar baz

$ ls '\myserver.example.com\a' foo bar baz

Advantages: Moba does NOT differentiate between the "list all available shares for that server" mode and the "list the contents of a SPECIFIC share on that server" mode. -- Which seems to be major obstacle in PowerShell.

Drawbacks:

  • Extra install
  • Not PowerShell. Purely text. Not an objectlike .NET/Windows way of doing this at all.

Worked: net view

Advantages:

  • preinstalled on Windows

Drawbacks: Not PowerShell. Purely text. Not an objectlike .NET/Windows way of doing this at all.

0

I keep hearing "No you can't get a list of shared folders from a server using get-WmiObject. Yet for me, I have been successful in using get-WmiObject to collect a list of all shared folders off my servers from my workstation using the instructions above (see image attached). If I export to a csv, I get a whole lot of other information beside just the names and where the shares live.

shared folders

0

I use the following based on Dingo's answer.

Create a file called get_shares.ps1 with this content:

#$Servers = ( Get-ADComputer -Filter { DNSHostName -Like '*' }  | Select -Expand Name )
$Servers = @('server-01', 'server-02')
foreach ($Server in $Servers)
{
    (net view $Server /all) | %{
        if($_.IndexOf(' Disk ') -gt 0)
        {
            $shareName = $_.Split('      ')[0]
            Write-Host "\\$Server\$shareName"
        }
    }
}

Then run it using:

powershell -executionpolicy bypass ./get_shares.ps1

It produces output such as:

\\server-01\data
\\server-01\some_hidden_share$
\\server-02\c$
\\server-02\logs
Fidel
  • 502
0

Here is a simple function I'm using :

    function Get-RemoteShare {
    param (
        [string]$RemoteHost,
        [switch]$All
    )
$RemoteShareArray = @()
if ($All.IsPresent) { $Result = net view \\$RemoteHost "/ALL" }
else { $Result = net view \\$RemoteHost }

$Result | ForEach-Object {
    if (($_ -match 'dis') -or ($_ -match 'ip')) {

        $ResultItems = $_ -split '\s+'
        $Name = $ResultItems[0]

        if ($ResultItems[2] -match ':') {
            $NetworkDrive = $ResultItems[2]
            $Informations = $ResultItems[3..($ResultItems.Length - 1)] -join " "
        } else {
            $NetworkDrive = $null
            $Informations = $ResultItems[2..($ResultItems.Length - 1)] -join " "
        }

        $RemoteShareArray += [PSCustomObject]@{
            Name = $Name
            NetworkDrive = $NetworkDrive
            Informations = $Informations
        }
    }
}
$RemoteShareArray

}

Here is an example of use :

PS C:\Users\Admin> Get-RemoteShare -RemoteHost 10.9.0.1 -All

Name NetworkDrive Informations


arcure_ajilink gemarcur public arcure_ajilink-se Gemarcur Public arcure_ajjis Gemarcur Public arcure_guyomard Gemarcur Public arcure_vigreux Z: Gemarcur Public IPC$ IPC Service (Samba 4.9.5-Debian) print$ Printer Drivers scans_ajilink-lc Gemarcur Public Scans scans_ajilink-se Gemarcur Public Scans scans_guyomard Gemarcur Public Scans scans_vigreux Gemarcur Public Scans

DjiPih
  • 11
0

When SMB doesn't work because you're trying to access SMB shares on a non-Windows system, like Nutanix AFS.

$TypeDefinition=@"
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Collections.Generic;

public class GetNetShares {

[DllImport("Netapi32.dll", SetLastError = true)] static extern int NetApiBufferFree(IntPtr Buffer);

[DllImport("Netapi32.dll", CharSet = CharSet.Unicode)] private static extern int NetShareEnum( StringBuilder ServerName, int level, ref IntPtr bufPtr, uint prefmaxlen, ref int entriesread, ref int totalentries, ref int resume_handle);

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct SHARE_INFO_1 { public string shi1_netname; public uint shi1_type; public string shi1_remark; public SHARE_INFO_1(string sharename, uint sharetype, string remark) { this.shi1_netname = sharename; this.shi1_type = sharetype; this.shi1_remark = remark; } public override string ToString() { return shi1_netname; } }

const uint MAX_PREFERRED_LENGTH = 0xFFFFFFFF; const int NERR_Success = 0;

private enum NetError : uint { NERR_Success = 0, NERR_BASE = 2100, NERR_UnknownDevDir = (NERR_BASE + 16), NERR_DuplicateShare = (NERR_BASE + 18), NERR_BufTooSmall = (NERR_BASE + 23), }

private enum SHARE_TYPE : uint { STYPE_DISKTREE = 0, STYPE_PRINTQ = 1, STYPE_DEVICE = 2, STYPE_IPC = 3, STYPE_SPECIAL = 0x80000000, }

public SHARE_INFO_1[] EnumNetShares(string Server) { List<SHARE_INFO_1> ShareInfos = new List<SHARE_INFO_1>(); int entriesread = 0; int totalentries = 0; int resume_handle = 0; int nStructSize = Marshal.SizeOf(typeof(SHARE_INFO_1)); IntPtr bufPtr = IntPtr.Zero; StringBuilder server = new StringBuilder(Server); int ret = NetShareEnum(server, 1, ref bufPtr, MAX_PREFERRED_LENGTH, ref entriesread, ref totalentries, ref resume_handle); if (ret == NERR_Success) { IntPtr currentPtr = bufPtr; for (int i = 0; i < entriesread; i++) { SHARE_INFO_1 shi1 = (SHARE_INFO_1)Marshal.PtrToStructure(currentPtr, typeof(SHARE_INFO_1)); ShareInfos.Add(shi1); currentPtr += nStructSize; }

  NetApiBufferFree(bufPtr);
  return ShareInfos.ToArray();
}
else {
  ShareInfos.Add(new SHARE_INFO_1(ret.ToString(),10,&quot;ERROR&quot;));
  return ShareInfos.ToArray();
}

} } "@ Add-Type -TypeDefinition $TypeDefinition

$x=[GetNetShares]::new() $x.EnumNetShares("<computer name>")

<# Errorlist: 5 : The user does not have access to the requested information. 124 : The value specified for the level parameter is not valid. 87 : The specified parameter is not valid. 234 : More entries are available. Specify a large enough buffer to receive all entries. 8 : Insufficient memory is available. 2312: A session does not exist with the computer name. 2351: The computer name is not valid. 2221: Username not found. 53 : Hostname could not be found. #>

0

I did a modification on the script initially posted by "Slogmeister Extraordinaire" on this question. It is now wrapped in a PowerShell function. As I am no C# programmer I did the adjustments with the returned objects.

Customized script

function Get-SMBSharesViaSMB ($Target) {
# Target may be an IPAddress or a Hostname
# Script initially posted by "Slogmeister Extraordinaire" on this question

$TypeDefinition=@" using System; using System.Runtime.InteropServices; using System.Text; using System.Collections.Generic;

public class GetNetShares {

[DllImport("Netapi32.dll", SetLastError = true)] static extern int NetApiBufferFree(IntPtr Buffer);

[DllImport("Netapi32.dll", CharSet = CharSet.Unicode)] private static extern int NetShareEnum( StringBuilder ServerName, int level, ref IntPtr bufPtr, uint prefmaxlen, ref int entriesread, ref int totalentries, ref int resume_handle);

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct SHARE_INFO_1 { public string ShareName; public uint ShareType; public string Comment; public SHARE_INFO_1(string sharename, uint sharetype, string remark) { this.ShareName = sharename; this.ShareType = sharetype; this.Comment = remark; } public override string ToString() { return ShareName; } }

const uint MAX_PREFERRED_LENGTH = 0xFFFFFFFF; const int NERR_Success = 0;

private enum NetError : uint { NERR_Success = 0, NERR_BASE = 2100, NERR_UnknownDevDir = (NERR_BASE + 16), NERR_DuplicateShare = (NERR_BASE + 18), NERR_BufTooSmall = (NERR_BASE + 23), }

private enum SHARE_TYPE : uint { STYPE_DISKTREE = 0, STYPE_PRINTQ = 1, STYPE_DEVICE = 2, STYPE_IPC = 3, STYPE_SPECIAL = 0x80000000, }

public SHARE_INFO_1[] EnumNetShares(string Server) { List<SHARE_INFO_1> ShareInfos = new List<SHARE_INFO_1>(); int entriesread = 0; int totalentries = 0; int resume_handle = 0; int nStructSize = Marshal.SizeOf(typeof(SHARE_INFO_1)); IntPtr bufPtr = IntPtr.Zero; StringBuilder server = new StringBuilder(Server); int ret = NetShareEnum(server, 1, ref bufPtr, MAX_PREFERRED_LENGTH, ref entriesread, ref totalentries, ref resume_handle); if (ret == NERR_Success) { IntPtr currentPtr = bufPtr; for (int i = 0; i < entriesread; i++) { SHARE_INFO_1 shi1 = (SHARE_INFO_1)Marshal.PtrToStructure(currentPtr, typeof(SHARE_INFO_1)); ShareInfos.Add(shi1); currentPtr += nStructSize; }

  NetApiBufferFree(bufPtr);
  return ShareInfos.ToArray();
}
else {
  ShareInfos.Add(new SHARE_INFO_1(ret.ToString(),10,&quot;ERROR&quot;));
  return ShareInfos.ToArray();
}

} } "@

function IsValidIPv4Address ($ip) { return ($ip -match "^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$" -and [bool]($ip -as [ipaddress])) }

if ("GetNetShares" -as [type]) {Write-Verbose "Already registered GetNetShares. Skipping registration."} else {Add-Type -TypeDefinition $TypeDefinition}

$x=[GetNetShares]::new() $returncodes = $x.EnumNetShares($Target)

Return Errors

$returncodes | where {$."ShareType" -eq "10"} | foreach { $returnedErrorCode = $."ShareName" switch ($returnedErrorCode) { "5" {Write-Error "The user does not have access to the requested information. (Code: $($returnedErrorCode))"} "124" {Write-Error "The value specified for the level parameter is not valid. (Code: $($returnedErrorCode))"} "87" {Write-Error "The specified parameter is not valid. (Code: $($returnedErrorCode))"} "234" {Write-Error "More entries are available. Specify a large enough buffer to receive all entries. (Code: $($returnedErrorCode))"} "8" {Write-Error "Insufficient memory is available. (Code: $($returnedErrorCode))"} "2312" {Write-Error "A session does not exist with the computer name. (Code: $($returnedErrorCode))"} "2351" {Write-Error "The name is not valid. (Code: $($returnedErrorCode))"} "2221" {Write-Error "Username not found. (Code: $($returnedErrorCode))"} "53" { if (IsValidIPv4Address $Target) { Write-Error "No response from target (Code: $($returnedErrorCode))" } else { Write-Error "No response from target. Validate Hostname and DNS resolution. (Code: $($returnedErrorCode))" } } DEFAULT {Write-Error "Unknown Error. (Code: $($returnedErrorCode))"} } }

Return objects on success

$returncodes | where {$_."ShareType" -ne "10"} }

Examples

Current domain

> Get-SMBSharesViaSMB $env:USERDNSDOMAIN
ShareName  ShareType Comment
---------  --------- -------
ADMIN$    2147483648 Remote Admin
C$        2147483648 Default share
DNS$               0
IPC$      2147483651 Remote IPC
NETLOGON           0 Logon server share
print$             0 Printer Drivers
SYSVOL             0 Logon server share

DNS name "fritz.box" (Example did not answer)

> Get-SMBSharesViaSMB fritz.box
Get-SMBSharesViaSMB : No response from target. Validate Hostname and DNS resolution. (Code: 53)
In Zeile:1 Zeichen:1
+ Get-SMBSharesViaSMB fritz.box
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-SMBSharesViaSMB

IP-Address

> Get-SMBSharesViaSMB 192.168.0.1
ShareName  ShareType Comment
---------  --------- -------
ADMIN$    2147483648 Remote Admin
C$        2147483648 Default share
Hidden$            0
IPC$      2147483651 Remote IPC

Localhost (no target defined)

> Get-SMBSharesViaSMB
ShareName  ShareType Comment
---------  --------- -------
ADMIN$    2147483648 Remote Admin
C$        2147483648 Default share
D$        2147483648 Default share
IPC$      2147483651 Remote IPC

Easy to customize

You can make simple adjustments to the results when changing the second last line that is right after the comment Return objects on success.

Here is an example that will include the full path when changing the mentioned line:

$returncodes | where {$_."ShareType" -ne "10"} | Select *, @{Name="FullPath";Expression={"\\" + $Target + "\" + $_.ShareName}}`

The result will look like this:

> Get-SMBSharesViaSMB server.local
ShareName  ShareType Comment             FullPath
---------  --------- -------             --------
ADMIN$    2147483648 Remote Admin        \\server.local\ADMIN$
C$        2147483648 Default share       \\server.local\C$
IPC$      2147483651 Remote IPC          \\server.local\IPC$
NETLOGON           0 Logon server share  \\server.local\NETLOGON
SYSVOL             0 Logon server share  \\server.local\SYSVOL
An-dir
  • 114
0

Windows Resource Kit tool: rmtshare.

Either run under id with administrator permissions on the remote server or make an ipc$ connection to remote server.

rmtshare \\servername