Get-WMIObject verse Get-CimInstance – A journey of discovery

UPDATE: Added function Remove-PSSubShare to my GitHub Repository (https://github.com/PSSubmariner/PSSubFunctions).

Background:

At my company, we are working on a migration of our file server data from our old domain to our new domain. There is much involved with this and you will probably see other articles with more information around this scenario in the future. But this article is talking about dealing with Shares.

The scripted process, when provided a source share and destination location, will connect to the source share and determine the underlying location. It will create the destination (or use an existing location if it is not production) and it will then unshare the source and perform the robocopy of the data from the source to the destination. Then it will share the destination. That is the basic flow of the work to be done.

The rub comes when it is time to unshare the source… If your environment consists exclusively of Windows Server 2012R2 and Windows 8.1 machine or later (hereafter referred to as an “Ideal PS Environment”, then the following will work wonderfully for you:

$session = New-CimSession -ComputerName 'Server1'
Remove-SmbShare -CimSession $session -Name 'ShareName'

Very simple, the “ShareName” share has been removed from “Server1”. Can you do it all in one line without any variables?

Remove-SmbShare -CimSession (New-CimSession -ComputerName 'server1') -Name 'ShareName'

Elegant and easy… A one-liner to remove the share as it was intended. (A one-liner to rule them all?)

The use of this command is all predicated on the Ideal PS Environment…

Raise your hand if you live in that ideal environment already. Ok, Fred, I am happy that you have this ideal environment. For the rest of us, we have to deal with older servers; servers that don’t work with the above command(s). And sometimes, servers that don’t even have PowerShell on them… We are going to go a little deeper to make this work for the less than ideal environments.

Submarine:

When you don’t have a PowerShell command that meets your needs, in many cases, you can fall back on CIM/WMI. I am not going to do a detailed discussion on the differences between CIM and WMI; others have covered those topics in much better detail that I can (The OMI/CIM/WMI/MI/DMTF Dictionary).

We have to face the fact that Get-WMIObject and its related commands are deprecated; we can not keep using them forever. It is time to start using the CIM commands.

NOTE: I am using the manipulation of Shares as an example because that is what I was doing when I figured all of this out. Much of this should adapt over to other situations.

If you search the Interwebs for some PowerShell scripts (you know, instead of using Get-Help like a good PowerShell programmer would do) to accomplish this, you will find a lot of examples that use Get-WMIObject or gwmi command line tool. Here is an example of connecting to a share and then deleting it.

$share = Get-WMIObject -ComputerName 'Server2' -ClassName 'Win32_Share' -Filter "Name = 'ShareName'"

$share now holds a WMI Object of the ShareName share from Server2. To delete the share, we run the following command:

$share.Delete()

This works and ShareName share will be removed from Server2.

Since Get-WMIObject is deprecated (As PSScriptAnalyzer tells you when you run it against your scripts. You are doing that right?), we want to replace it with Get-CimInstance. If we do this:

$share = Get-CimInstance -ComputerName 'Server2' -ClassName 'Win32_Share' -Filter "Name = 'ShareName'"

$share will hold a CIM Object of the ShareName share from Server2 just as before. But, when you try to run the same .Delete() Method, you get a failure:

“Method invocation failed because [Microsoft.Management.Infrastructure.CimInstance] does not contain a method named ‘Delete’.

I found a couple of articles that talked around the issue but it was a comment by Richard Siddaway about just piping it to Remove-CimInstance that gave me the answer. (Win32_Printer delete method discussion)

It might have been obvious to Mr. Siddaway, but it wasn’t obvious to me. With that information, I ran the following command to great success:

$share | Remove-CimInstance

Before I found the article with the Remove-CimInstance comment, I came across an article by Mike F. Robbins (Targeting Down Level Clients with the Get-CimInstance PowerShell Cmdlet).

His article explains further, but basically, if you don’t have at least WMF3 installed or remote management enabled, using Get-CimInstance may not work. He explains how you can have Get-CimInstance use DCOM to access the WMI of older servers like Windows Server 2003 and Windows Server 2008R2 without remote administration and/or newer version of Windows Management Framework (WMF3 or better).

In those situations, you would have to do something like this:

$legacyServer = New-CimSession -ComputerName 'Server3' -SessionOption (New-CimSessionOption -Protocol Dcom)
$share = Get-CimInstance -CimSession $legacyServer -ClassName 'Win32_Share' -Filter "Name = 'ShareName'"

The first line establishes a CIMSession with your legacy server. The second line is almost identical to our previous line. The only difference is the use of the CimSession Parameter in place of the ComputerName parameter. Once you have this connection established, when you pass the object to Remove-CimInstance, it will remove the share.

NOTE: If you don’t do the Session Option of DCOM, you will get a successful connection to the object but when you try to pass it to Remove-CimInstance, it will error out. Telling it to use DCOM will allow Remove-CimInstance to use the DCOM protocol and therefore the ‘.Delete()’ method will work.

With the above information, you have everything you need to interact with file servers of all variety and manage their shares (and printers and other stuff that you might want to delete…). I am going to go a little deeper into the rabbit hole now.

DSRV:

If you do a Get-Member on the output of the Get-WMIObject command, you can easily see that it has a Delete Method:

$share = Get-WMIObject -ComputerName 'Server2' -ClassName 'Win32_Share' -Filter "Name = 'ShareName'"
$share | Get-Member

wmiobject-get-member

Figure 1: Results of Get-Member for Get-WMIObject of a Win32_Share

When you do the same thing for the Get-CimInstance command, you will not see a Delete Method.

$share = Get-CimInstance -ComputerName 'Server2' -ClassName 'Win32_Share' -Filter "Name = 'ShareName'"
$share | Get-Member

ciminstance-get-member

Figure 2: Results of Get-Member for Get-CimInstance of a Win32_Share

I hope you have enjoyed this little journey on a Deep Dive.

Legend:

Server1 – Ideal world server. Running Windows Server 2012R2 or later

Server2 – Downlevel server. Any server version previous to 2012R2 (i.e. 2008R2 or 2003)

Server3 – Downlevel server that doesn’t even have Powershell potentially

Deep Dive: I don’t want to just answer the easy questions. I want to answer the deeper questions that aren’t normally covered in many articles.

Submarine: How deep does a Naval Submarine go? That’s classified but they say they can go deeper than 800 feet.

DSRV: Deep Submergence Rescue Vehicle. According to Wikipedia, they go significantly deeper than the 800 feet that the US Navy will acknowledge.

@PSSubmariner

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s