Use PowerShell
The Shell Is Calling
The Shell Is Calling
Jan 6th
PowerShell Version 3 introduces the concept of Default Parameter Values.
This allows you to specify a value for one or more parameters for one or more commands.
To implement this, you need to populate a new hashtable – $PSDefaultParameterValues
Let’s look at some examples –
First off, we can specify the value for a parameter for a specific command. The pattern used for the hashtable entries is “NameOfCommand:NameOfParameter” = “ValueOfParameter”.
(I’m stealing some of these examples from the samples provided in CTP1.)
$PSDefaultParameterValues = @{"Get-Process:Name"="powershell"}
Get-Process
Get-Process e*
With that set, every time we run Get-Process and do not specify the Name parameter, the PowerShell runtime will stick “powershell” in for that value. A specified parameter will always override a default value.
Let’s go the other way and, instead of specifying one command, set a parameter value on all commands (that have that parameter.
$cred = Get-Credential mydomain\mydomainadminaccount
$PSDefaultParameterValues = @{"*:Credential" = $cred}
This would provide your $cred to any Credential parameter.
We can also use wildcards to match multiple commands.
$PSDefaultParameterValues = @{"*-AD*:Server" = 'MyDomainController'}
This would provide a default value to the Server parameter for any command whose noun started with AD (like everything in the ActiveDirectory module.
Since this is a hashtable, it can hold many different mappings –
$PSDefaultParameterValues = @{
"*:Credential" = $cred
"Get-Process:Name"="powershell"
"*-AD*:Server" = 'MyDomainController'
}
To turn off DefaultParameterValues (without removing everything you have set up), you can set a key “Disabled” equal to $true.
$PSDefaultParameterValues = @{"Disabled" = $true}
This concludes our brief look at DefaultParameterValues for today.
Go grab Version 3 CTP2 and give it a whirl…
Jan 6th
A question came up about the behavior of Import-Module (in the context of this interesting discussion of whether module authors should provide aliases), especially with Version 3 and the auto-loading of modules (what’s that?).
Does the auto-loading of modules in PowerShell Version 3 use Import-Module behind the scenes? If it does, can we use default parameter sets to control what types of things are imported?
So, it turns out that Import-Module does get called behind the scenes and a default parameter set (new feature) can be used to customize the import of new modules.
$PSDefaultParameterValues = @{
"Import-Module:Alias"=@()
"Import-Module:Function"='*'
"Import-Module:Cmdlet"='*'
"Import-Module:Variable"='*'
}
Get-Module
Get-BitsTransfer
Get-Module
This example (when used in PowerShell V3) will (from a base PowerShell session) show you what modules are loaded, attempt to run the Get-BitsTransfer command, and then show you what modules are now loaded (should include the BitsTransfer module now). Since we’ve provided an empty array to the Alias parameter for Import-Module, any module that is imported will import any aliases.
The problem comes in when you specify that only one of the four parameters (Alias, Function, Cmdlet, and Variable).
You might expect that when you specify one parameter (example Alias), that the other portions of the import (Function, Cmdlet, and Variable) would behave just like if you did not specify any of them.
Example -
Open a new PowerShell session (V2 or V3) -
Import-Module BitsTransfer Get-Command -Module BitsTransfer Get-Module
This will import BitsTransfer module and all attendant commands and functions. Note the ExportedCommands in the Get-Module output.
Open a new PowerShell session and try -
Import-Module BitsTransfer -Alias @() Get-Command -Module BitsTransfer Get-Module
This should import the BitsTransfer without any aliases (yes, I know BitsTransfer doesn’t have any aliases, but I wanted a module that most any Windows 7 or Server 2008 R2 machine would have – feel free to test with whatever module you desire).
This did import the BitsTransfer, but no commands or variables were imported. It only attempted to import aliases, and since we specified that aliases were to be an empty array, nothing was imported.
This is counter-intuitive and is not the experience we should have. Specifying one parameter should not change the default value of other parameters (from all to none in this case).
First of all, open PowerShell and try it! If you have one of the CTPs of Version 3, try it there. Also try it in V2 using Import-Module directly.
Then go to Microsoft Connect and vote up https://connect.microsoft.com/PowerShell/feedback/details/716857/module-partially-loads-with-import-module.
Jan 6th
PowerShell V3 includes a new “feature” – Auto-loading of modules.
Module discovery has been updated to make the exported commands for modules that are not loaded visible in a PowerShell session.
In Version 2, if you run Get-Module on a module that is not loaded but in your PSModulePath, you’ll get some metadata about the module, but nothing about the ExportedFunctions, ExportedCmdlets, ExportedAliases, or ExportedVariables.
Version 3 changes this, allowing Get-Module to retrieve information on all the exported features of the module.
This enables tab completion to complete the names of commands from modules that are not loaded. It also provides information to the PowerShell runtime on where to get the command information from, if the module is not loaded.
So, in Version 3, if you run a command from a module that is not loaded, the runtime will search your PSModulePath for the first command that it can find that matches that name and load the module that command is in for you.
I really don’t care for this feature. I like to be in control of what module I import. The order in which modules are automatically imported depends on your PSModulePath. You don’t get to pick what the order is… User specific PSModulePath entries are first, then machine specific PSModulePath entries. This can cause a host of unexpected problems when there are command name collisions.
For example, the new Hyper-V cmdlets for Windows 8 cannot manage down-level Hyper-V machines, so you might need James O’Neil’s Hyper-V module from Codeplex. Both modules have Get-VM. Since James’ module will be in my user PSModulePath (most likely), unless I specifically load the Windows 8 Hyper-V module, running Get-VM will always load James’ module.
Sep 14th
(NOTE: I started this post a few weeks ago)
This morning I woke up with my kitchen floor covered in water. As I was cleaning it up, I couldn’t help drawing some parallels between fixing a leaky pipe and a "leaky" (non-performing) script.
My repair process this morning took the following steps:
Since most of you won’t really care about the plumbing in my kitchen, let’s run through how dealing with this problem applies to troubleshooting a misbehaving script (or function).
The first step I needed to take was to contain the leak.. in PowerShell, that means I need to pull that script or function from general use, as well as terminating any running instances.
This can be tougher than you think, especially if you are using remote jobs or are performing a sequential series of actions that cannot be rolled back (especially if you don’t have good logging in place!).
After I’ve got the leak contained, I had to find where the leak was coming from. For my misbehaving script, I either need to find what has failed. Normally, the errors that I’m getting will help point me towards the source of the problem.
However, if I’m not getting errors, I need to step through the script (or function) and can use Set-PSBreakpoint to create an entry point in my script where I want to start walking through.
If my script (or function) is an advanced function, I could use -Debug and have it break on any Write-Debug statements that I’ve used throughout my script. This is a GREAT reason to have a periodic Write-Debug statement before any significant blocks of logic in your script.
Once I’ve found the source of the leak, I can begin to stop the leak. In my kitchen leak, I had to turn off the water.
In the case of my script, I have to find any scheduled tasks using it and stop them or advise other people who might be using the script to stop using it until I can publish a new script.
Once I had my leak stopped, I started to check for residual damage that I might have to control. Water had leaked onto where the power cord for my water heater plugged into an extension cord, This caused the GFI outlet to trip.
Collateral damage from a misbehaving script or function can be disastrous. Very often, scripts are run under administrative credentials and can have unexpected side effects. When locating the source of the leak, I’ll check to see what other systems or areas that the script is touching and examine them for potentially improper changes or updates. Finally, I need to document any “damage” or errors that the script created.
Once I found that the GFI was tripped, I unplugged everything on that chain. I let everything dry out and replaced the extension cord.
For a misbehaving script, this is going to vary based on what the script was actually doing, but this can be one of the most important steps. I have to record all the actions I take to mitigate the “damage” found in the previous step. Even if I decide not to do anything about the damage I found in the previous step, that is still an action and should be documented.
Patching the leak is pretty straightforward. Going to the source of the leak, I removed the faulty tap and patched the pipe.
In my script, that means going to the source of the faulty logic or source of the errors and either fixing the logic and/or adding error handling to deal with the source of the errors. I’ll also add additional Write-Verbose or Write-Debug statements to make tracing problems and verifying functionality easier. For scripts that will
After I patched the leak, I turned the water back on and watched it for a short while to make sure that there was no seepage from the patch. I tested down-level plumbing to make sure that the pressure was appropriate as well.
After I’ve added my error handling and/or fixed the logic, I’ll run the script again, perhaps with –Verbose or –Debug. If I’m really motivated, I’ll make sure there is the CmdletBinding attribute and add –WhatIf support.
After I was able to confirm that the leak was fixed, I stayed home and periodically checked the line to watch for any seepage or residual problems. Over the next few days, I continued to monitor the site of the patch for any issues, though not as frequently.
After I’ve fixed my script, I’ll continue to watch was it gets used, perhaps logging all the output, until I’ve regained confidence in the project.