Registry is just another drive

Today, one of my colleague at work was asked to set a registry value on many computer using a login script. First try was to export value to a reg file which seems the easiest way to do it. Problem is, that one of the key in the path was dynamic and randomly generated during the installation of the product in cause. This is when Powershell comes in play!

First of all, did you know that registry can be access like a normal drive in Powershell? In fact, each hive is a drive (it sounds like a cheap rap lyrics…).

Try this and you will list the HKEY_Local_Machine hive.

cd HKLM:
dir

You can now drill down to keys using Set-Location or cd.

Back to our initial problem: How to get to a value with an unknown part…
Path of the value was looking like this: HKLM:\Software\SoftNAME\Modules\{12345678-A1B2-12A3-A123-0000A00AAAAA}\ShowSplash=0
We were lucky that in Modules there was only one sub-key in every installation but it has a new name each time we install the product.

With a simple Get-ChildItem we can retrieve the name of that mysterious key name.

$key = Get-ChildItem HKLM:\Software\SoftNAME\Modules

Then, we used the Set-Item cmdlet to define the value of ShowSplash to 0

Set-ItemProperty -path $key -name ShowSplash -value 0

Because this is always cool to get a script in a one-liner, we pushed it a little further, so the final script looks like this:

Set-ItemProperty -path (Get-ChildItem HKLM:\Software\SoftNAME\Modules) -name ShowSplash -value 0

With classic reg and batch files, things are pretty static or inflexible. But with Powershell, it is so easy to manipulate the data and get the real information you are looking for.

Special variables

Because Powershell cmdlets are pretty self-explanatory they are easy to understand or at least, to deduct their functionality. There is some exceptions that you can’t find by yourself, you just need to know they exist and what they are meant for. I’ve wrote a list of the principal you would need to use in your scripts.

$_ : Refers to the current object inside a loop. This script will output the name of each files listed by the Get-ChildItem.

Get-ChildItem | ForEach-Object {Write $_.name}

$true and $false : Those are presets for true and false I bet you didn’t see that one coming, did you?

if ($true){Write "It's true!"}

$env : Refers to the environment variables set in your OS. Basically, those you used to refer like %appdata% in a DOS shell.

$env:computername
$env:appdata

# and <# #>: Single line comment and block of comments

# This is a single line comment
Get-ChildItem # This is an inline comment
<# This part is a block
of comment. You don't need to precede each line by a pound sign.
This is particularly useful to skip a long part of a script
for testing. #>

Tip: When you add a line of comments, leave a space after the pound sign. But when you temporarily comment a line of code, put it right before it. This facilitate the search to rapidly spot those line. Then, with a simple regexp search, you find those temporary commented lines: #[^ ]

# List the content of a directory
#Get-ChildItem D:\ -Recurse

$? : Contains a boolean value representing the success/failure of the last operation

Get-ChildItem C:\Scripts\ -recurse
$? # Would return true
Remove-Item C:\Scrip
$? # Would return false because the folder C:\Scrip does not exist

` : Breaks a command on multiple line. It is also the escape characters for tab (t), return (r), newline (n), single-quote (‘) and double-quote (“) inside a string.

Get-ChildItem C:\Scripts\ -recurse `
-Include *.txt
 
Write-Output "This will print a TAB `t."
Write-Output "While this will show a double-quote `""

This is not a complete list, but I think it covers the most useful ones. If I forgot one you use frequently, let me know and I will add it up.

Bill

Obey the law, respect the conventions

Have you ever noticed that every cmdlets respect a simple naming convention? They all start with a verb, followed by a dash, then a noun. This format is for readability, and it is especially useful for a new comer. In fact, it is fairly easy to understand the purpose of a script even if you never heard about powershell.

Few examples:

  • Get-ChildItem
  • Set-Date
  • Remove-Item

To preserve the readability and consistency, it is a good practice to use this naming scheme when you create you own functions.

First of all, you need to choose a proper verb representing the action that your function will accomplish. To know which are the official verbs, guess what? there is a cmdlet for that: Get-Verb. It will output all verbs that are predefined and suitable to use. You will notice a second property named Group that represent the suggested field of use. This is only for an informational purpose, and not to limit their use because in fact, you can choose any verbs in the list to name your function.

The easy part is the dash! There is not much latitude for that one.

Last but not the least, the noun. This section of the name must be clear enough to evoke the kind of data involved.

Moreover, each noun in a cmdlet is singular, there is no plural. This one is the easiest to forget because frequently a function is design to return a collection of objects, so it is tempting to use the plural form like Get-DisabledUsers but you better name it Get-DisabledUser.

If you plan to share your functions/cmdlets, another trick would be to use a short prefix for the noun to avoid conflict with other modules.
For example, Quest had created a really great module to manage Active Directory. They prefixed their cmdlet nouns with “Q”.
Get-ADcomputer is from Microsoft, where Get-QADcomputer is from Quest. Same for Get-ADuser and Get-QADuser. If Quest had not use this prefix, it would have thrown a failure each time you would start a powershell session stating that there is conflict between modules.

In short, when you create a function, take a second and design it’s name to fit that scheme. At first, you will need to think about it, but it will fast become an automatic minding.

For those of you who are still wondering if it would work if you name your functions otherwise…. …. …. yes, but please DON’T! ;-)

Pimp my script, create custom objects

One of the most obscure concept for me was “How to build an object with custom properties”. In fact, I didn’t even know I needed an “object”, I just wanted to regroup different data into a kind of array. When I re-read my first scripts… ohh ohhhhh, I realized that I’ve done some really weird tricks to achieve my goals. So bizarre that I will not show you ;-)

My techniques were far from perfect, but they were doing the trick. One day, I stumble upon a script (I don’t remember by whom) searching for a complete other concept, but there was a “home made object”. Wooooowwww!

I’ll show you 3 different ways to construct your own.

1 – Create a blank object and use the Add-Member cmdlet

$CustomObject = New-Object -TypeName PSObject
Add-Member -InputObject $CustomObject -MemberType NoteProperty -Name Name -Value "Bill"
Add-Member -InputObject $CustomObject -MemberType NoteProperty -Name Address -Value "123 Powershell Drive"
Add-Member -InputObject $CustomObject -MemberType NoteProperty -Name Phone -Value "888.123.6987"
Add-Member -InputObject $CustomObject -MemberType NoteProperty -Name Email -Value "bill@ITshifts.NET"
Write-Output $CustomObject

For many, this is the official way to create a custom object. It is in fact, the “by the book” manner, but IMO not the easiest.

1.5 – Extended use of #1 with the use of the pipeline

$CustomObject = New-Object -TypeName PSObject
$CustomObject | Add-Member –MemberType NoteProperty -Name Name -Value "Bill" -PassThru |
	Add-Member –MemberType NoteProperty -Name Address -Value "123 Powershell Drive" -PassThru |
	Add-Member –MemberType NoteProperty -Name Phone -Value "888.123.6987" -PassThru |
	Add-Member –MemberType NoteProperty -Name Email -Value "bill@ITshifts.NET"
Write-Output $CustomObject

In this one, I have removed the InputObject switch because this value is acquired by the pipeline. It is possible to pipe from one Add-Member to another because of the PassThru switch. Normally, Add-Member do not output anything but using this switch will “Pass” the new modified object “Thru” the pipeline.

3 – Create a hashtable then create an object based on it

$CustomObject = @{
	Name		=	"Bill"
	Address		=	"123 Powershell Drive"
	Phone		=	"888.123.6987"
	Email		=	"bill@ITshifts.NET"
}
New-Object -TypeName PSObject -Property $CustomObject

This one is my favourite, because of the clarity that it confers. I’ve created a simple hashtable with all the properties I want to get in my new object. Then I’ve called the New-Object cmdlet and specified the hashtable as the input for properties. It is also easy to replace a static value by a small script to gather the desired info and preserve the clarity.

4 – Use Select-Object then define new properties

$CustomObject = "" | Select-Object -Property Name,Address,Phone,Email
$CustomObject.Name	= "Bill"
$CustomObject.Address	= "123 Powershell Drive"
$CustomObject.Phone	= "888.123.6987"
$CustomObject.Email	= "bill@ITshifts.NET"
Write-Output $CustomObject

Some would say that the Select-Object is not meant to create properties but to select specified properties from an input object. Others would tell you that if it works, why not?!
I’ve started with this method, then I switched to the hashtable one after. Anyway, both are comparable in terms of performance but are also clearly faster than the Add-Member way. You can check this blog post from Boe Prox for an extensive test about speed.

Conclusion: Use the method you are more comfortable with and if performance is an issue, stay away from Add-Member.

Remember that you better output an object than an simple formatted string. Text is static, object keeps moving!

Bill

WhatIf it goes wrong?

You certainly have ever needed to test a script in a sandbox but on real data or production server. I do! Often!!!

There is a switch you can use that will try the command and throw you the simulated output. It is explicitly called WhatIf and it is implemented in many cmdlets.

You know me, I won’t let you down with only theories, let’s test this in a script. We will choose a destructive cmdlet for the credibility of the test.

Remove-Item C:\Scripts\test.txt -Whatif

Output:
What if: Performing operation “Remove File” on Target “C:\Scripts\test.txt”.

Disclaimer: No text file had been hurt during this test.

This is easy to use for a single command, but think of a scenario where you have a complex script, many lines and a tons of cmdlets with the WhatIf switch, it would not be useful to remove all of them to trigger the operation in the real world. And it would be much more embarrasing if you would need to write them back. But, there is a solution: you can use a boolean variable along with the switch.

$simulation = $true
Remove-Item C:\Scripts\test.txt -Whatif:$simulation

This one script would produce the exact same result as the previous. But changing the value of $simulation to $false and running it another time would delete the text file.

This switch is pretty useful when you are using wildcards or a CSV from another system and you want to know what will be done before doing it for real.

If you want to know all the cmdlets which support “WhatIf”, you can run this:

Get-Command | Where-Object { $_.parameters.keys -contains "WhatIf"}

For now, I’ll get back to my scripts for the last events of Powershell Scripting games because time is running out!

Bill