Parse Resource "Column" from Net Share command

364 views Asked by At

Our environment is in the process of removing a bunch of Windows 2003 servers, and in an attempt to pull permissions on shares that exist, I've been trying to come up with a solution to script it out.

The kicker to this however is that WMI is not working on these servers, so I cannot do a GWMI Win32_Share. Given the age of the servers as well, more modern powershell commands do not work.

What I can do, is a Get-ACL, however I need to provide the paths for the shares. The only way I can see to getting the full paths for the shares is through Net Share or a Get-ItemProperty HKLM , of which I've had no luck.

If anyone smarter than I out there knows a way to parse out just the "resources" column of a net share, you'd be doing me a huge favor.

2

There are 2 answers

7
Doug Maurer On

You can probably achieve the desired result with a bit of regex

Powershell V1 compatible

switch -regex (net share){
    '^(\S+)\s+(\w:.+?)(?=\s{2,})' {
        New-Object PSObject -Property @{
            ShareName = $matches.1
            Resource  = $matches.2
        }
    }
}

Powershell 3.0+

switch -regex (net share){
    '^(\S+)\s+(\w:.+?)(?=\s{2,})' {
        [PSCustomObject]@{
            ShareName = $matches.1
            Resource  = $matches.2
        }
    }
}

On powershell 5.1 you can use ConvertFrom-String with "training" data. It can be real sample data or generic. It may take some adjusting for your specific environment but this worked well in testing.

$template = @'
{Share*:ADMIN$}       {Path: C:\Windows}                      {Note:Remote Admin}
{Share*:Admin}        {Path: E:\Admin}                        {Note:Default share}
{Share*:Apps}         {Path: D:\Apps}                         {Note:test}
'@

net share | Select-Object -Skip 4 | Select-Object -SkipLast 2 |
    ConvertFrom-String -TemplateContent $template -OutVariable ShareList

Any output shown should now be contained in the variable $ShareList

$Sharelist | Get-Member -MemberType NoteProperty


   TypeName: System.Management.Automation.PSCustomObject

Name  MemberType   Definition              
----  ----------   ----------              
Note  NoteProperty string Note=Remote Admin
Path  NoteProperty string Path=C:\Windows  
Share NoteProperty string Share=ADMIN$ 

You could also use psexec to get the information remotely and apply either proposed solution.

switch -regex (.\PsExec.exe -nobanner \\$RemoteComputer cmd "/c net share"){
    '^(\S+)\s+(\w:.+?)(?=\s{2,})' {
        [PSCustomObject]@{
            ShareName = $matches.1
            Resource  = $matches.2
        }
    }
}

or

$template = @'
{Share*:ADMIN$}       {Path: C:\Windows}                      {Note:Remote Admin}
{Share*:Admin}        {Path: E:\Admin}                        {Note:Default share}
{Share*:Apps}         {Path: D:\Apps}                         {Note:Test}
'@

.\PsExec.exe -nobanner \\$RemoteComputer cmd "/c net share" |
    Select-Object -Skip 4 | Select-Object -SkipLast 2 |
        ConvertFrom-String -TemplateContent $template -OutVariable ShareList
0
mklement0 On

Unfortunately, robust parsing of net share output is non-trivial and requires a two-pass approach:

  • Collect all share names only from the output of net share.
  • For each share name $shareName, call net share $shareName and parse the share-specific properties into a custom object.

The problem with trying to parse the output from net share only is that values that are too long may be truncated (with ...) or cause line-wrapping; that is, you cannot robustly get the full values; by contrast, net share $shareName uses a list format in which the values are not subject to truncation.

The code below implements the two-pass approach:

Note:

  • I've verified that it works in PowerShell v2; I don't have access to v1 anymore.

  • As per the output from net share $shareName, the property name representing the local target path is Path, not Resource as in the output from net share.

  • If you run without elevation (not as admin), the following properties cannot be queried due to lack of permissions: Users, Caching, Permission; the code quietly ignores them.

# Run `net share` and collect its output lines in an array.
$lines = net share

# First pass: 
# Extract all share *names* from the lines.
# Note: Assumes that 4 header rows and a trailing status row can be ignored.
$shareNames = switch -regex ($lines[4..($lines.Count-2)]) { 
   # Extract the first whitespace-separated token,
   # but ignore wrapped lines (for overly long names / paths), which
   # start with whitespace.
   '^\S' { (-split $_)[0] }
}

# Second pass:
# Call `net share <name>` for each name and parse the list of properties into
# a [pscustomobject] instance.
# Note: 
#  * When runnning *without elevation*, querying the following fields 
#    fails due to lack of permissions, which is quietly ignored here:
#      Users, Caching, Permission
#  * Assumes that property names are either one or two words and followed
#    by at least two spaces.
foreach ($shareName in $shareNames) {
  Write-Verbose -vb "Processing $shareName..."
  # Initialize a hashtable to store the properties.
  # Note: In PSv3+, you can use [ordered] @{} to define the properties in predictable order.
  $properties = @{}
  # Parse the property lines and add them to the hashtable.
  switch -regex (net share $shareName 2>$null) {
     '^(\w+ (?:\w+)?) {2,}(.*)' {
        $properties[$Matches[1]] = ($Matches[2]).Trim()
     }
  }
  # Convert the hashtable to a custom object.
  # Note: In PSv3+, you can simplify to [pscustomobject] $properties
  New-Object pscustomobject $properties
}