Skip to content

Instantly share code, notes, and snippets.

@mattcargile
Forked from jborean93/Get-SmbShareInfo.ps1
Last active March 28, 2023 21:43
Show Gist options
  • Save mattcargile/cbf141c6473ae89fe9187389c5ed9984 to your computer and use it in GitHub Desktop.
Save mattcargile/cbf141c6473ae89fe9187389c5ed9984 to your computer and use it in GitHub Desktop.
Enumerates shares on a remote host
# Provides a nicely formatted table for the output.
<#
ComputerName Name Type FreeSpace TotalSpace
------------ ---- ---- --------- ----------
remotecomputer Name1 Disk 1.14 TB 10.00 TB
remotecomputer backup12345678 Disk 12.43 TB 60.00 TB
remotecomputer ipc$ Ipc, Spec…
remotecomputer Name4567890123 Disk 3.80 GB 3.80 TB
remotecomputer Name34567890123 Disk 4.85 TB 5.00 TB
remotecomputer Name2345678 Disk 911.32 GB 2.00 TB
remotecomputer Name12345 Disk 972.80 GB 972.80 GB
remotecomputer c$ Special
remotecomputer admin$ Special
#>
Install-Module EZOut,PowerShellHumanizer
$writeFormatViewSplat = @{
TypeName = 'Win32Share.NativeMethods'
Width = @(15,25,10,10,10)
Property = 'ComputerName', 'Name', 'Type', 'FreeSpace', 'TotalSpace'
VirtualProperty = @{
FreeSpace= { [Humanizer.ByteSizeExtensions]::Humanize($_.TotalFreeBytes, '0.00') }
TotalSpace={ [Humanizer.ByteSizeExtensions]::Humanize($_.TotalBytes, '0.00') }
}
}
Write-FormatView @writeFormatViewSplat | Out-FormatData | Add-FormatData
# Copyright: (c) 2020, Jordan Borean (@jborean93) <jborean93@gmail.com>
# MIT License (see LICENSE or https://opensource.org/licenses/MIT)
function Get-SmbShareRemote {
<#
.SYNOPSIS
Enumerate shares on a remote host.
.DESCRIPTION
Enumerate shares on a remote host and returns the name, type, and special remark for those shares.
.PARAMETER ComputerName
[String] The host to enumerate the shares for. Can be accepted as pipeline input by value.
.OUTPUTS
[PSCustomObject]@{
ComputerName = [String]'The computer the share relates to'
Name = [String]'The name of the share'
Path = [string]'\\ComputerName\Name\'
Type = [Win32Share.ShareType] An flag enum of the share properties, can be
Disk = Disk drive share
PrintQueue = Print queue share
CommunicationDevice = Communication device share
Ipc = Interprocess communication share
Temporary = A temporary share
Special = Typically a special/admin share like IPC$, C$, ADMIN$
Remark = [String]'More info on the share'
TotalBytes = [System.Nullable[int]]
TotalFreeBytes = [System.Nullable[int]]
FreeBytesAvailableToUser = [System.Nullable[int]]
}
.LINK
https://gist.github.com/jborean93/017d3d890ae8d33276a08d3f5cc7eb45
.EXAMPLE
Get-SmbShareInfo -ComputerName some-host
#>
[Alias('gsmbr')]
[CmdletBinding(DefaultParameterSetName='ComputerName')]
param (
[Parameter(Mandatory, ParameterSetName='ComputerName', Position=0)]
[Alias('cn')]
[string[]]
$ComputerName,
# Computer Name Input Object
[Parameter(ValueFromPipeline, ParameterSetName='Pipeline')]
[string]
$InputObject,
# Name Of Share
[Parameter(ParameterSetName ='ComputerName', Position=1)]
[Parameter(ParameterSetName='Pipeline')]
[SupportsWildcards()]
[Alias('nm')]
[string[]]
$Name
)
begin {
<#Check if loaded to make dot-source testing easier#>
if(-not ('Win32Share.NativeMethods' -as [type])){
Add-Type -ErrorAction 'Stop' -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
namespace Win32Share
{
public class NativeHelpers
{
[StructLayout(LayoutKind.Sequential)]
public struct SHARE_INFO_1
{
[MarshalAs(UnmanagedType.LPWStr)] public string shi1_netname;
public ShareType shi1_type;
[MarshalAs(UnmanagedType.LPWStr)] public string shi1_remark;
}
}
public class NativeMethods
{
[DllImport("Netapi32.dll")]
public static extern UInt32 NetApiBufferFree(
IntPtr Buffer);
[DllImport("Netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern Int32 NetShareEnum(
string servername,
UInt32 level,
ref IntPtr bufptr,
UInt32 prefmaxlen,
ref UInt32 entriesread,
ref UInt32 totalentries,
ref UInt32 resume_handle);
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool GetDiskFreeSpaceEx(
string lpDirectoryName,
ref UInt64 lpFreeBytesAvailableToCaller,
ref UInt64 lptotalNumberOfBytes,
ref UInt64 lpTotalNumberOfFreeBytes
);
}
[Flags]
public enum ShareType : uint
{
Disk = 0,
PrintQueue = 1,
CommunicationDevice = 2,
Ipc = 3,
Temporary = 0x40000000,
Special = 0x80000000,
}
}
'@
}
$PSBoundParameters['PSC'] = $PSCmdlet
function GetSmbInf ($ComputerName, $Name, [System.Management.Automation.PSCmdlet]$PSC) {
$buffer = [IntPtr]::Zero
$read = 0
$total = 0
$resume = 0
$res = [Win32Share.NativeMethods]::NetShareEnum(
$ComputerName,
1, # SHARE_INFO_1
[ref]$buffer,
([UInt32]"0xFFFFFFFF"), # MAX_PREFERRED_LENGTH
[ref]$read,
[ref]$total,
[ref]$resume
)
if ($res -ne 0) {
$exp = [System.ComponentModel.Win32Exception]$res
$er = [System.Management.Automation.ErrorRecord]::new(
$exp,
'Win32Share.NativeMethods.GetSmbInf.RemoteException',
[System.Management.Automation.ErrorCategory]::NotSpecified,
$ComputerName
)
$er.ErrorDetails = "Failed to enum share for '$ComputerName': $($exp.Message)"
$PSC.WriteError( $er )
return
}
try {
$entryPtr = $buffer
for ($i = 0; $i -lt $total; $i++) {
$shareInfo = [System.Runtime.InteropServices.Marshal]::PtrToStructure($entryPtr,
[Type]([Win32Share.NativeHelpers+SHARE_INFO_1]))
$netNm = $shareInfo.shi1_netname
if ($Name) {
$isLike = $false
foreach ($nm in $Name) {
if ($netNm -like $nm) {
$isLike = $true
continue
}
}
if (-not $isLike) {
$entryPtr = [IntPtr]::Add($entryPtr, [System.Runtime.InteropServices.Marshal]::SizeOf($shareInfo))
continue
}
}
$shTyp = $shareInfo.shi1_type
# API below requires an ending backslash
$shrPath = "\\$ComputerName\$netNm\"
$freeBytesAvailableToCaller = 0
[System.Nullable[UInt64]]$freeBytesAvailableToCallerNull = $null
$totalNumberOfBytes = 0
[System.Nullable[UInt64]]$totalNumberOfBytesNull = $null
$totalNumberOfFreeBytes = 0
[System.Nullable[UInt64]]$totalNumberOfFreeBytesNull = $null
$lastWin32Error = 0
if (($shTyp -bor [Win32Share.ShareType]::Disk) -eq [Win32Share.ShareType]::Disk) {
$dskRes = [Win32Share.NativeMethods]::GetDiskFreeSpaceEx(
$shrPath,
[ref]$freeBytesAvailableToCaller,
[ref]$totalNumberOfBytes,
[ref]$totalNumberOfFreeBytes
)
if ($dskRes) {
$freeBytesAvailableToCallerNull = $freeBytesAvailableToCaller
$totalNumberOfBytesNull = $totalNumberOfBytes
$totalNumberOfFreeBytesNull = $totalNumberOfFreeBytes
}
else {
# https://stackoverflow.com/questions/17918266/winapi-getlasterror-vs-marshal-getlastwin32error
$lastWin32Error = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
$exp = [System.ComponentModel.Win32Exception]$lastWin32Error
$er = [System.Management.Automation.ErrorRecord]::new(
$exp,
'Win32Share.NativeMethods.GetSmbInf.ShareException',
[System.Management.Automation.ErrorCategory]::NotSpecified,
$shrPath
)
$er.ErrorDetails = "Failed to get disk space on '$shrPath' for '$ComputerName': $($exp.Message)"
$PSC.WriteError( $er )
}
}
[PSCustomObject]@{
PSTypeName = 'Win32Share.NativeMethods' # Used in Formatter
ComputerName = $ComputerName
Path = $shrPath
Name = $netNm
Type = $shTyp
Remark = $shareInfo.shi1_remark
TotalBytes = $totalNumberOfBytesNull
TotalFreeBytes = $totalNumberOfFreeBytesNull
FreeBytesAvailableToUser = $freeBytesAvailableToCallerNull
}
$entryPtr = [IntPtr]::Add($entryPtr, [System.Runtime.InteropServices.Marshal]::SizeOf($shareInfo))
}
} finally {
$null = [Win32Share.NativeMethods]::NetApiBufferFree($buffer)
}
}
foreach ($compNm in $ComputerName) {
$PSBoundParameters['ComputerName'] = $compNm
GetSmbInf @PSBoundParameters
}
}
process {
if ($InputObject) {
$PSBoundParameters['ComputerName'] = $InputObject
$null = $PSBoundParameters.Remove( 'InputObject')
GetSmbInf @PSBoundParameters
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment