When customers won’t conform to your standards…

When working with customer-facing systems, they do not always conform to your standards. A perfect example of this is folder and file names. When users create them, they like to do things like the following:

I like to put spaces in my folder names
I just do it because I think it does not matter to anyone else

With PowerShell, it does not matter as much as it did with string based scripting tools, but I still like a little conformity. My preference it to use CamelCase in my folder naming. While the users customers like their folder names, if I am working with the stuff on the back end, I will typically put it into CamelCase. A perfect example of this is naming groups used for assigning permissions to the folder in question. When my helpdesk person was looking at reconciling permissions on our network drives, the biggest problem was the inconsistency of the naming of the groups to the folder names. My report was saying the folder did not have the correct permissions because I couldn’t find the group that conformed to the folder name. Instead of having my helpdesk person go through and fix all the folder or group names to be the same, I rewrote my report and used this function to help me reconcile the messing customer folder names to my elegant group names. The amount of work that had to be done by my helpdesk person for this project was reduced by over 90%. Let us get to the heart of the matter.

function ConvertTo-CamelCase
{
[CmdletBinding()]
[OutputType([string])]
Param
(
   # String to convert to CamelCase
   [Parameter(Mandatory = $true,
		   Position = 0)]
   [string[]]$TextString
)
Begin {}
Process
{
Write-Verbose "CTCC:StartingValue: $TextString"
foreach ($String in $TextString)
{
Write-Verbose "CTCC:Value of String to process: $String"
If ($String.Contains(" "))
{
	Write-Verbose "CTCC:String has space(s): '$String'"
	$TextInfo = (Get-Culture).TextInfo
	$String = $TextInfo.ToTitleCase($String.ToLower()).Replace(" ", "")
	Write-Verbose "CTCC:OneStepCamelCase String: '$String'"
	Write-Output $String
}
else
{
	Write-Output $String
} #end If String has space
} #end ForEach TextString
} #end Process
End {}
}

You can find the full code on my GitHub account: Function-ConvertTo-CamelCase

The first thing I do is check to see if the provided string has a space in it. If not, I just return the original value untouched. If it does have a space, I then establish a TextInfo object to allow me to modify the provided string. I got this off of someone else’s blog article but that information has been lost through many edits and my changing coding standards (you know, learning my way through it). Since I know the starting string has space(s), I first convert it all to lowercase. Then I use the ToTitleCase method of the TextInfo object. And, finally, I use the Replace method to remove the spaces from the string. I then return the CamelCase’d string as the output of the function.

This is one of my earlier functions and I am sure there are things that can be improved. Additional functionality could be implemented to handle more situations. But, this meets my current needs so I haven’t put any more time into improving my function. I hope you enjoy.

@PSSubmariner

Advertisements

Remove-PSSubShare

I started a GitHub repository to hold my functions.
https:/github.com/PSSubmariner/PSSubFunctions

This function takes a UNC pathed share name as an input. It then will parse the path and determine the server and the name of the share. It will then connect to the server hosting the share with a CIMInstance and removes the share by one of the methods discussed in my previous article. I have tested it against Windows Server 2012R2, Windows Server 2008R3 and Windows Server 2003. On 2008R2, I tested against servers with just PowerShell 2 and no remoting as well as 2008R2 with PowerShell 5 (WMF5). The 2003 server didn’t have PowerShell installed at all.

I have a number of TODO: tags in my script of shortfalls that I can see and want to address over time. But it works in its current state. I have been wanting to blog for a long time but I never felt I was ready or enough of an authority to contribute. I decided recently that I didn’t need to have a perfect function to get started. PowerShell is all about getting in there at the command line and starting to string commands together and when you find something that works, you put it into a script or a function so that you can reuse it. I have been writing basic functions for a long time and started doing Advanced Functions when I started reading the Toolmaking book. I had made a few “advanced” functions prior to this but I didn’t truly understand what I was doing. I was adapting someone else’s Advanced Function to my needs. I am still learning and will continue to do so as any good PowerShell scripter/developer should.

Learn PowerShell Toolmaking in a Month of Lunches

Check out my function and let me know your thoughts.

@PSSubmariner

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

It is time to stop lurking in the PowerShell community

I have been working with Automation in a Windows environment for a long time. The first tool I worked with was just the DOS batch file. I played with my AutoExec.Bat and did all kinds of amazing and fun things with it. My first language of choice was Perl. I liked the functionality it provided and I could find a plethora of sample scripts that I could adapt to my needs. It was also extremely fast with text/string processing. I dabbled a little with KixStart because my company login script had been written in that language. When I decided to overhaul the login script, I considered Perl but the overhead of installing the Perl engine on all of my corporate desktops was more than I was willing to take on. Looking at the other options available, I opted for learning VBScript as my next language of choice. I had some co-workers who could follow the VBScripts, so I was not alone in my ability to maintain it; an advantage over Perl at the time. I duplicated the functionality of the KixStart script in VBScript and then started extending the functionality with some logs that got integrated into our Intranet on a page used by our support personnel. We had a new co-worker that had some development experience and brought that skill set and expanded the functionality of the login script significantly. He also broke down the VBScript into Subroutines and Functions and building something akin to modules in PowerShell.

But enough of the background on my journey to PowerShell. My interest was peaked by the Monad Manifesto (http://www.jsnover.com/Docs/MonadManifesto.pdf) by Jeffery Snover. I dabbled with it a little in Exchange 2007 but I was not maintaining Exchange as part of my day-to-day job anymore. PowerShell was not yet ready to take over the functionality that VBScript provided me and I was not ready to refactor all of my plethora of scripts that I had accumulated. I continued to follow the development of the language and grew more excited at the prospects. When Microsoft released Windows 7 and Windows Server 2008 R2 with version 2.0 integrated into the OS, PowerShell because a real language that I could start using on a day to day basis. The new cmdlets that were introduced brought a level of functionality that I could really start to use in my day-to-day job. I used books by Don Jones and Jeffery Hicks since I was familiar with them from their VBScript books, as well as Richard Siddaway to start to learn more about the language. They are all quick to point out that the most useful cmdlet in PowerShell is Get-Help. The help that is included in PowerShell by default is amazing in its depth and functionality. When writing my own functions and scripts, I try to emulate that depth and functionality as much as I can. I truly like the examples that are provided. Often when I have an idea to do something in PowerShell, with a little perusal of Get-Help and the Examples section, I find an example that is close to what I want if not an exact match.

Besides the books by those mentioned before and others, I have also learned a lot from the videos by Don Jones, Jason Helmick and Jeffery Snover (The Creator) and others. I would highly recommend the free Microsoft Virtual Academy videos with Jason and Jeffery as they are both very informative and entertaining.

As PowerShell continues to mature and more functionality has been introduced, I have started using it more in my daily activities. I have written many functions and even put together a few modules for my own internal company use. I will be cleaning up and publishing some of these in the Gallery in the not too distant future. That and a GitHub repository (forthcoming) for collaborative development. Coming from the operations side of the house, I have been adopting the developer mentality of using a code repository and organizing my code (much of it modeled around what my co-worker first did with my VBScript-based login script).

I have been following many of the PowerShell personality, on Twitter, YouTube, conferences, PowerShell Saturday, and GitHub. I have been learning from them and absorbing their PowerShell essence. It is time for me to give back and start sharing some of what I have learned along this wonderful journey. I have started writing down ideas for blog posts and I have a few in the works that I will be publishing soon. 

Welcome to my journey. I hope I can give at least a fraction back of what I have learned from this wonderful PowerShell community.

@PSSubmariner