The Admin’s First Steps: Discovering Shares

The Admin’s First Steps: Discovering Shares

  • Comments 1
  • Likes

Summary: Richard Siddaway talks about using Windows PowerShell to discover shares on remote systems.

Hey, Scripting Guy! Question Hey, Scripting Guy! I’ve just starting using Windows PowerShell to administer my systems, and I’ve been asked to discover the shares on some remote servers. How can I do that?

—OL

Hey, Scripting Guy! Answer Hello OL,

Honorary Scripting Guy, Richard Siddaway, here today—filling in for my good friend, The Scripting Guy. You’re in luck because today I’ve got a solution to that issue as part of my series of posts about how an administrator can start making productive use of Windows PowerShell.

If you ask an IT-related question, you’ll often get the answer, “It depends.”

In the case of asking how you can discover the shares that are enabled on a remote machine, the full answer is, “It depends on the version of Windows you are running. If you have Windows 8 or Windows Server 2012, you get a shiny new module to play with that does the hard work for you. If you are using, or targeting, earlier versions of Windows, you will have to use WMI.”

Windows 8 and Windows Server 2012 ship with PowerShell 3.0 and a suite of extra modules. Many of these modules are created by using the “cmdlets-over-objects” technology, which means that you take a WMI class, wrap it in XML, and you get a PowerShell module. Details of how to create your own CDXML modules are provided in my book, PowerShell and WMI.

You can easily tell if your module has been constructed in this manner because it will have a .cdxml extension. In many instances, the WMI classes that are used for this process are new to Windows 8 and Windows Server 2012, and they are not available on earlier versions of Windows—even if you install Windows PowerShell 3.0.

The cmdlets are in a module called SmbShare. If you use Get-SmbShare, you see this output on a Windows 8-based computer:

£> Get-SmbShare | Format-Table -AutoSize

Name   ScopeName Path       Description

----   --------- ----       -----------

ADMIN$ *         C:\Windows Remote Admin

C$     *         C:\        Default share

D$     *         D:\        Default share

IPC$   *                    Remote IPC

Users  *         C:\Users

This is for the local machine, but more importantly, you need to be able to administer your remote servers. Let’s look at the parameters that are available with Get-SmbShare:

£> Get-Command  Get-SmbShare  -Syntax

Get-SmbShare [[-Name] <string[]>] [[-ScopeName] <string[]>] [-Scoped <bool[]>] [-Special <bool[]>]

[-ContinuouslyAvailable <bool[]>] [-ShareState <ShareState[]>] [-FolderEnumerationMode <FolderEnumerationMode[]>] [-CachingMode <CachingMode[]>] [-ConcurrentUserLimit <uint32[]>] [-AvailabilityType <AvailabilityType[]>] [-CaTimeout<uint32[]>] [-EncryptData <bool[]>] [-IncludeHidden] [-CimSession <CimSession[]>] [-ThrottleLimit <int>] [-AsJob][<CommonParameters>]

You will notice one glaring omission. There isn’t a ComputerName parameter. One of the quirks of CDXML-based modules is that you need to use CIM sessions to access remote machines (or run the command over a Windows PowerShell remoting session).

$sess = New-CimSession -ComputerName Win12R2

Get-SmbShare -CimSession $sess

$sess | Remove-CimSession

You will automatically get PSComputerName output as an additional property. If you try to run this against an earlier version of Windows with Windows PowerShell 2.0, it will fail because you need WSMAN 3.0 on the remote system to use CIM sessions.

The older versions of Windows can’t be left out of this, so you need to look at another way of retrieving the data. WMI supplies a Win32_Share class that you can use:

£> Get-WmiObject -Class Win32_Share | Format-Table -AutoSize

Name   Path       Description

----   ----       -----------

ADMIN$ C:\Windows Remote Admin

C$     C:\        Default share

D$     D:\        Default share

IPC$              Remote IPC

Users  C:\Users

The next step is to extend this to a remote machine. Get-WmiObject has had a –ComputerName parameter since PowerShell 1.0. Back in those days, it was our only tool for working remotely!

 £> Get-WmiObject -Class Win32_Share -ComputerName Win12R2 | Format-Table –AutoSize

So, you get to the age-old question, “Which do I use?”

The drawback with CIM sessions is that you need WSMAN 3.0 on the remote machine, which means that PowerShell 3.0 or above must be installed. On older machines with Windows PowerShell 2.0, you need to have WMI allowed through any firewalls you have between you and the remote machine. As the percentage of machines with WSMAN 3.0 increases, you need to be maximizing the use of CIM sessions. The answer is to test the version of WSMAN that you have installed and proceed accordingly:

£> Test-WSMan -Authentication default -ComputerName win12r2

wsmid           : http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd

ProtocolVersion : http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd

ProductVendor   : Microsoft Corporation

ProductVersion  : OS: 6.3.9600 SP: 0.0 Stack: 3.0

The value you need to test is the Stack property on the ProductVersion. This leads you to a function like this:

function get-share {

[CmdletBinding()]

param(

[parameter(Mandatory=$true,

      ValueFromPipeline=$true,

      ValueFromPipelineByPropertyName=$true)]

[string[]]$computername

)

PROCESS{

foreach ($computer in $computername){

 $stack = ((Test-WSMan -Authentication default -ComputerName $computer  -ErrorAction SilentlyContinue ).ProductVersion -split ": ")[-1]

  switch ($stack) {

  "2.0" {

          Get-WmiObject -Class Win32_Share -ComputerName $computer |

          select PSComputername, Name, Path, Description

        }

  "3.0" {

          $sess = New-CimSession -ComputerName Win12R2

          Get-SmbShare -CimSession $sess |

          select PSComputername, Name, Path, Description

          $sess | Remove-CimSession

        }

  default {Write-Warning -Message "Couldn't determine WSMAN Stack version for $computer" }

 } # end switch

} # end foreach

} # End process

}

Take a pipeline input, and test the WSMAN version for a computer that is passed in. This line may look a bit intimidating:

((Test-WSMan -Authentication default -ComputerName $computer  -ErrorAction SilentlyContinue ).ProductVersion -split ": ")[-1]

It’s not that bad when you break it down...

This gets the information:

Test-WSMan -Authentication default -ComputerName $computer  -ErrorAction SilentlyContinue

This simply takes the ProductVersion property:

(Test-WSMan -Authentication default -ComputerName $computer  -ErrorAction SilentlyContinue ).ProductVersion

This splits that property on a “: “:

-split ": "

The result is an array of which you take the last element by using the -1 index. Try building up the command like this at the Windows PowerShell prompt to see how it all works.

A switch statement is used to determine actions based on the WSMAN stack version. Version 2.0 uses WMI and version 3.0 uses CIM. The appropriate cmdlet is run and then a select statement is used to determine the data returned.  In this case, the PSComputerName property is used to identify the machine from which we generate the data.

If the stack version isn’t 2.0 or 3.0, the switch statement drops into the default option, which prints a warning on screen.

What about the situation where you have hundreds or thousands of machines? Do you want to wait for this to run?

If the answer is, “No,” you could try turning this into a workflow, but the switch statement doesn’t work very well in workflows. We recommended against using it in PowerShell in Depth. Your choices are to completely rewrite or to use Windows PowerShell jobs. Get-WmiObject and Get-SmbShare have an –AsJob parameter.

A Windows PowerShell job will create a job object to perform your processing and then immediately continue with the next line in the script. This works for Get-WmiObject, but there is an issue using Get-SmbShare like this because the next line will immediately remove the CIM session the job is trying to use, and the job will fail.

The answer is to use Wait-Job to test the completion of the job, which will delay the whole script and negate the reason for using jobs, or to move the whole process into a script block and use Start-Job.

function get-share {

[CmdletBinding()]

param(

[parameter(Mandatory=$true,

      ValueFromPipeline=$true,

      ValueFromPipelineByPropertyName=$true)]

[string[]]$computername

)

PROCESS{

foreach ($computer in $computername){

 $stack = ((Test-WSMan -Authentication default -ComputerName $computer -ErrorAction SilentlyContinue).ProductVersion -split ": ")[-1]

  switch ($stack) {

  "2.0" {

          Get-WmiObject -Class Win32_Share -ComputerName $computer -AsJob

        }

  "3.0" {

          $sb = {

            param($comp)

            $sess = New-CimSession -ComputerName $comp

            Get-SmbShare -CimSession $sess

            $sess | Remove-CimSession

          }

          Start-Job -ScriptBlock $sb -ArgumentList $computer

        }

  default {Write-Warning -Message "Couldn't determine WSMAN Stack version for $computer" }

 } # end switch

} # end foreach

} # End process

}

You can then use the standard job cmdlets to retrieve the data.

Creating your scripts in this way allows you to mix and match new technologies with older ways of doing things so that your automation will keep working as your server estate changes.

If you want to extend the use of this script you could:

  • Incorporate it into the server documentation tool that you read about in the first article in this series: The Admin’s First Steps: Documenting Servers.
  • Add process to retrieve data from the jobs.
  • Extend process to retrieve the permissions granted on the share.

OL, there is how you use Windows PowerShell to discover the shares on your remote servers. Next time, I’ll have another idea for you to try as you bring more automation into your environment.

If you would like to read more in this series, check out these posts:

Bye for now.

~Richard

Richard Siddaway is based out of the UK, and he spends his time automating anything and everything for Kelway, Ltd. A six-year Windows PowerShell MVP, Richard is a prolific blogger, mainly about Windows PowerShell. He is a frequent speaker at user groups and Windows PowerShell conferences. He has written a number of books: PowerShell in Practice, PowerShell and WMI, PowerShell in Depth (co-author), PowerShell Dive (co-editor), and he is currently finishing Learn Active Directory Management in a Month of Lunches, which features a lot of Windows PowerShell. All of the books are available from Manning Publications.

Contact information: Richard Siddaway's Blog

Thanks, Richard.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Scripting Guy!, Operating system, Services, Windows PowerShell, Richard Siddaway, servers

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hi,

    In the first function, the computername is hardcoded:

    $sess = New-CimSession -ComputerName Win12R2

    ...whereas it should be:

    $sess = New-CimSession -ComputerName $computer