Saturday, January 10, 2009

Windows 7 Beta Has Arrived – But Not For Everyone

The Windows 7 and Windows Server 2008 R2 beta versions were released this week. I got the ISOs myself during the week, and finished off today loading R2, Win7 Ultimate and WIn7 Home Premium as VMware virtual machines. But it looks like Microsoft has totally underestimated the demand. In a blog post over on The Windows Blog, Brandon LeBlanc explains that they are delaying the introduction of the public beta. They are adding extra infrastructure to cope with the demand. Another blog post, Windows 7 Beta Downloads Will be Available Soon - Microsoft's Servers Already Can't Handle Demand, Frederic Lardinois gives more detail.

I guess it was inevitable that Win7 would be popular once it was officially released. The beta had already leaked out, and at one point the torrents had thousands of folks downloading. Looking at The Pirate Bay, the Win 7 torrent (“directly from Microsoft”) today has 1700 lechers. It should have been obvious that this was going to be popular. Of course, these problems just make great press stories, and stoke the enthusiasm of the industry. Perhaps Microsoft should have considered deploying Bit-Torrent to enable the download?

Irrespective of Microsoft’s infrastructure woes, Win7 and Server 2008 R2 look like real winners. Win 7 looks really good, although thus far, I’m only running it in a VM. And Server 20087 R2 also looks pretty good! For me the biggest feature of both, thus far, is the inclusion of PowerShell into the OS. As I’m running these betas in VMware, I am not yet getting the benefit of the updates to the Aero UI – my new laptop comes very soon and I’m really looking forward to running Win7 natively.

If you have Windows 7 and/or Windows Server 2008 R2 betas, be sure to check out PowerShell V2. The OS betas incorporate what is more or less PowerShell CTP3 beta (which itself was released just before Christmas). This new version has some pretty exciting new features – I’ve been blogging about these ever since I got the beta. The version of PowerShell that is contained in the OS betas is just slightly earlier than the released PowerShell CTP3. But there should be no real difference in functionality (aside from having a few less bugs in the full CTP3). While remoting and eventing are pretty cool technology, the module functionality, including Auto-Help, are awesome.

So if you don’t have the WIn7, or R2, betas yet – be patient. The infrastructure will soon be in place. I suspect most users will feel the wait is worth while. Hopefully, their reaction will be a little more positive than xkcd’s.

 

Technorati Tags: ,,

 

Share this post :

Friday, January 09, 2009

More on PowerShell Best Practice

I read an very interesting post over on James O'Neill's blog. It was interesting  both because it talked PowerShell, but also because it talked about OCS and the neat PowerShell code James had written to support OCS 2007. 

As part of the Server 2008 Resource Kit, there’s a large script that contains a number of function definitions. If you dot source this script file, you can then use the functions sort of like cmdlets and administer OCS using PowerShell. I demo this script in my OCS Voice Ignite classes and the reaction is good!

When I first saw these cmdlets, my eyes lit up since it meant I could use these functions. However, I quickly noticed that some important best practices had not entirely been adhered to. Nothing major and certainly nothing that would break the functions – but it did not leverage PowerShell’s discoverability model.

In the latest post, James describes the re-write and the lessons he learned. The lessons are great ones that all PowerShell users should employ as they implement PowerShell into their environment. These are:

  • PowerShell nouns are written in the singular. So a cmdlet/function to get all users would be GET-USER not GET-USERS (even though the latter is more likely to be the result of the get).
  • Be consistent with Nouns, Avoid using “usage” in one function and “PhoneUsage” in another one.
  • Avoid creating new verbs. While adding verbs like LIST is tempting, using Get- is more discoverable.
  • Make use of the pipeline  when writing cmdlets. This enable a the user to pipe things into commands, pass an object or a name to fetch the object.
  • Assume users want to use wildcards and allow wherever possible.

These are great lessons we all should  learn. Personally, I have some trouble with the third one when writing scripts for my PowerShell Scripts Blog (http://pshscripts.blogspot.com).

For those of you who will be buying the OCS 2007 R2 Resource Kit book (something I’ll sure be doing!), the function library is a real opportunity to do some repackaging using PowerShell V2 CTP. James’ .PS1 script contains none of the V2 stuff (Cmdlet binding and parameter attributes, auto-help contents, using manifests to do the updating of type data, etc).

Wednesday, January 07, 2009

PowerShellers: The "#Requires" statement

Another interesting PowerShell tidbit. Alexandair has documented a bit more information about the #Requires statement in PowerShell. As he described in PowerShellers: The "#requires" statement, the #Requires statement has been in PowerShell since Version 1 (but was undocumented and not communicated widely!).

What’s neat is what else #Requires can do. The #Requires statement can also mandate a particular shell ID (i.e. this script only runs under a particular shell id) or that a particular snap-in is loaded (e.g. the Quest Active Roles tools).

This is more goodness in terms of Enterprise-readyness. I’d like to see what else could be added to the #Requires statement. Any thoughts?

Technorati Tags: ,,

Tuesday, January 06, 2009

PowerShell Audit Reports – Turning Great Scripts Into a Module

I just read a neat blog post entitled PowerShelling Audit Reports over on the TenBrink Tech blog. This blog post sets out three scripts: GetRecursiveGroupMembership.ps1, Audit-QuickGroup.ps1, and Audit-MultipleGroups.ps1. The second and third make use of the first. They make use of Quests’s AD tools to create CSV file(s) containing details of members in an AD Group.

Scripts to Modules

Dillon (the post’s author) has implemented all three of these as separate PS1 files. Which is so Version 1. :-)!!  As I read through his scripts, I could not escape the felling that a much better approach would be to implement them as a single module with PowerShell V2. So I did! It took a bit more time than I’d hoped, but it helped me to learn a bit more about modules in PowerShell V2.

Creating a Module

I first created a module file (audit.psm1) which contained the three functions Dillon created. These were slightly modified to be functions rather than script files, but the resulting module is essentially identical to the original. I also created a module manifest, which describes the module, and indicates the functions the module should expose to the user once the module is imported. I felt the first function was a helper function so the module only exports two functions not all three.Of course, I could be wrong, but it did make an interesting challenge – just exporting two of the three functions using a manifest.

Here’s the PSM1 Module itself:

  1. # 
  2. Write-Host "Importing Module Audit.psm1" 
  3. function Get-RecursiveGroupMembership { 
  4. <# 
  5. .SYNOPSIS 
  6.     Gets membership of a group.   
  7. .DESCRIPTION 
  8.     Uses recursion to handle nested groups 
  9. .NOTES 
  10.     File Name  : Audit.psm1 
  11.     Author     : Dillon (@tealshark on Twitter) 
  12.     Updated by : Thomas Lee tfl@psp.co.uk 
  13.     Requires   : PowerShell V2 CTP3 
  14.     This is a helper function and not exported by the module. 
  15. .LINK 
  16.     http://www.pshscripts.blogspot.com 
  17.     http://tenbrink.us/index.php/2009/01/03/powershelling-audit-reports/ 
  18. .PARAMETER DistinguishedName 
  19.     This paramater is the DN of the group you want to expand 
  20. .PARAMETER AddOtherTypes 
  21.     This parameter adds other types to the search 
  22. #> 
  23.  
  24. param
  25. [Parameter(Position=0, Mandatory=$TRUE, ValueFromPipeline=$TRUE)]   
  26. [string] $distinguishedname
  27. [Parameter(Position=1, Mandatory=$FALSE, ValueFromPipeline=$FALSE)]     
  28. [bool] $addOtherTypes = $false 
  29.  
  30. # Start of function 
  31.  $members = @() 
  32.  
  33.  $this = (Get-QADGroup $distinguishedname).member | Get-QADObject 
  34.  $this | foreach
  35.       if ($_.type -eq ‘user’) { 
  36.           $members += $_ 
  37.       } 
  38.       elseif ($_.type -eq ‘group’) { 
  39.           Write-Host "Adding sub group $_" 
  40.           $members += Get-RecursiveGroupMembership $_.dn $addOtherTypes 
  41.       } 
  42.       else
  43.           if ($addOtherTypes -eq $true) { 
  44.               $members += $_ 
  45.            } 
  46.            else
  47.               Write-Host "Non user/group member detected. Not added. Use -addOtherTypes flag to add." 
  48.            } 
  49.        } 
  50.     } 
  51.     return $members 
  52.  
  53. function Audit-QuickGroup { 
  54. <# 
  55. .SYNOPSIS 
  56.     This function takes the distinguishedName of a group in any domain and writes 
  57.     the results of that group membership to a csv file of the same name. 
  58. .DESCRIPTION 
  59.     This script uses get-recursivegroupmembership function to get the group membership 
  60. .NOTES 
  61.     File Name  : audit.psm1 
  62.     Author     : Dillon (@tealshark on Twitter) 
  63.     Updated by : Thomas Lee tfl@psp.co.uk 
  64.     Requires   : PowerShell V2 CTP3 
  65.     This function is exported 
  66. .LINK 
  67.     http://www.pshscripts.blogspot.com 
  68.     http://tenbrink.us/index.php/2009/01/03/powershelling-audit-reports/ 
  69. .EXAMPLE 
  70. .PARAMETER Name 
  71.     Disginguished name of a group whose membership the script will ascertain. 
  72. #> 
  73.  
  74. param
  75. [Parameter(Position=0, Mandatory=$TRUE, ValueFromPipeline=$TRUE)]   
  76. [string] $Name 
  77.  
  78. # Start of Function 
  79. $Csvdata = Get-RecursiveGroupMembership $name | select name,type,dn,title,office,description | convertto-csv -NoTypeInformation 
  80. $Filename = $Name + ".csv" 
  81. [String]$Reportdate = "Report Generated: " + [datetime]::Now 
  82. $f = new-item -itemtype file $filename 
  83. add-content $f "Audit Report - Active Directory Group - $name" 
  84. add-content $f $reportdate 
  85. add-content $f $csvdata 
  86.  
  87.  
  88. function Audit-MultipleGroups { 
  89. <# 
  90. .SYNOPSIS 
  91.     Gets membership of multiple groups 
  92. .DESCRIPTION 
  93.     This function uses the filtering abilities of the Quest Get-QADGroup cmdlet to  
  94.     get the membership of multiple groups. These are then written to multiple files. 
  95. .NOTES 
  96.     File Name  : get-autohelp.ps1 
  97.     Author     : Dillon (@tealshark on Twitter),  
  98.     Updated by : Thomas Lee tfl@psp.co.uk 
  99.     Requires   : PowerShell V2 CTP3 
  100.     This function is exported 
  101. .LINK 
  102.     http://www.pshscripts.blogspot.com 
  103. .EXAMPLE 
  104.     Left as an exercise for the reader 
  105. .PARAMETER GroupInput 
  106.     The groups you want to audit. 
  107. #> 
  108.  
  109. param
  110. [Parameter(Position=0, Mandatory=$TRUE, ValueFromPipeline=$TRUE)]   
  111. [string] $GroupInput 
  112.  
  113. # Start of function 
  114. # First get groups 
  115. $GroupList = get-qadgroup $groupinput 
  116.  
  117. # Iterate through groups, creating output 
  118.  foreach ($Group in $GroupList) { 
  119.      Write-Host $group.dn 
  120.     $GroupMembers = Get-RecursiveGroupMembership $group.DN | select name,type,dn,title,office,description | convertto-csv -NoTypeInformation 
  121.     #now create file 
  122.     $filename = $Group.Name + ".csv" 
  123.     [String]$reportdate = "Report Generated: " + [datetime]::Now 
  124.     $file = New-Item -ItemType file $filename -Force 
  125.     Add-Content $file "Audit Report - Active Directory Group Membership" 
  126.     Add-Content $file $reportDate 
  127.     Add-Content $file $groupMembers 
  128.   } 
  129.  
  130. # End of Module 

Creating the Manifest

I used the New-ModuleManifest cmdlet to produce the basic module manifest, the .psd1 file. I then did a bit of editing with PowerSHell plus to achieve this final manifest:

  1. # Module manifest for module 'audit' 
  2. # Generated by: Thomas Lee 
  3.  
  4. @{ 
  5. # These modules will be processed when the module manifest is loaded. 
  6. NestedModules = 'Audit.psm1' 
  7. # This GUID is used to uniquely identify this module. 
  8. GUID = '5eed72f9-5f1d-4819-973c-63f80ccee415' 
  9. # The author of this module. 
  10. Author = 'Thomas Lee (tfl@psp.co.uk), with functions by dillon.' 
  11. # The company or vendor for this module. 
  12. CompanyName = 'PS Partnership' 
  13. # The copyright statement for this module. 
  14. Copyright = '(c) PS Partnership 2009' 
  15. # The version of this module. 
  16. ModuleVersion = '1.0' 
  17. # A description of this module. 
  18. Description = 'This module is a packaging of audit scripts by Dillon into a single module.' 
  19. # The minimum version of PowerShell needed to use this module. 
  20. PowerShellVersion = '2.0' 
  21. # The CLR version required to use this module. 
  22. CLRVersion = '2.0' 
  23. # Functions to export from this manifest. 
  24. ExportedFunctions = ('Audit-QuickGroup', 'Audit-MultipleGroups'
  25. # Aliases to export from this manifest. 
  26. ExportedAliases = '*' 
  27. # Variables to export from this manifest. 
  28. ExportedVariables = '*' 
  29. # Cmdlets to export from this manifest. 
  30. ExportedCmdlets = '*' 
  31. # This is a list of other modules that must be loaded before this module. 
  32. RequiredModules = @() 
  33. # The script files (.ps1) that are loaded before this module. 
  34. ScriptsToProcess = @() 
  35. # The type files (.ps1xml) loaded by this module. 
  36. TypesToProcess = @() 
  37. # The format files (.ps1xml) loaded by this module. 
  38. FormatsToProcess = @() 
  39. # A list of assemblies that must be loaded before this module can work. 
  40. RequiredAssemblies = @() 
  41. # Lists additional items like icons, etc. that the module will use. 
  42. OtherItems = @() 
  43. # Module specific private data can be passed via this member. 
  44. PrivateData = '' 

The Results

It turns out that converting a set of script files, like Dillon created initially, into a module (as above) is easy. The syntax of the module file turns out to be a bit tricky, and the error messages and help text in CTP3 are woefully inadequate.

Here’s what this module looks like at runtime:

PS MyMod:\> # Note the module is not yet loaded so you get no output from Get-module

PS MyMod:\> Get-Module audit
PS MyMod:\> # So import the module, then look at module details

PS MyMod:\> Import-Module audit
Importing Module Audit.psm1
PS MyMod:\> Get-Module audit

Name              : audit
Path              : C:\Users\tfl\Documents\WindowsPowerShell\Modules\audit\audit.psd1
Description       : This module is a packaging of audit scripts by Dillon into a single module.
Guid              : 5eed72f9-5f1d-4819-973c-63f80ccee415
Version           : 1.0
ModuleBase        : C:\Users\tfl\Documents\WindowsPowerShell\Modules\audit
ModuleType        : Manifest
PrivateData       :
AccessMode        : ReadWrite
ExportedAliases   : {}
ExportedCmdlets   : {}
ExportedFunctions : {[Audit-QuickGroup, Audit-QuickGroup], [Audit-MultipleGroups, Audit-MultipleGroups]}
ExportedVariables : {}
NestedModules     : {Audit.psm1}

PS MyMod:\> # Here – use AutoHelp feature to get help on the exported function/
PS MyMod:\> Get-Help Audit-QuickGroup

NAME
    Audit-QuickGroup

SYNOPSIS
    This function takes the distinguishedName of a group in any domain and writes
    the results of that group membership to a csv file of the same name.

SYNTAX
    Audit-QuickGroup [-Name] [<String>] [-Verbose] [-Debug] [-ErrorAction [<ActionPreference>]] [-WarningAction [<ActionPreference>]] [-ErrorVariable [<String>]] [-WarningVariable [<String>
    ]] [-OutVariable [<String>]] [-OutBuffer [<Int32>]] [<CommonParameters>]

DETAILED DESCRIPTION
    This script uses get-recursivegroupmembership function to get the group membership

RELATED LINKS
http://www.pshscripts.blogspot.com
http://tenbrink.us/index.php/2009/01/03/powershelling-audit-reports/

REMARKS
    To see the examples, type: "get-help Audit-QuickGroup -examples".
    For more information, type: "get-help Audit-QuickGroup -detailed".
    For technical information, type: "get-help Audit-QuickGroup -full".

What I learned

This was an interesting exercise. It was pretty easy, but I did stumble a bit with the module (how I wish there has been better documentation on modules with CTP3!). Here are some of my take-aways relating to PowerShell modules in CTP3:

  1. Turning a set of inter-related scripts into a module is both easy, and a good thing!
  2. If you have a module that you want to also have a manifest with, they can both have the same file name but with different extensions (the module itself in a .psm1 file and the manifest in a pds1 file).
  3. To export only a subset of the functions in the .psm1 file, you use the ExportedFunctions feature in the manifest.
  4. To export multiple functions you enclose the set of functions as a string array inside the parenthesis. See this in line 24 above. This format was not all that obvious at first.
  5. You can add statements into the module that are executed when you import the module. See Line 2 in the module – this prints out a short message when you import the module. This has some great potential – thanks to Jeffrey Snover for the tip!
  6. If you import a module (using import-module as above) you can generally remove it using remove-module. This aids in testing!

There’s certainly room for improvement in this module. One thing that could be done would be to check to see if the Quest QAD tools were installed and issue a warning message if not (even better, if the tools are not found the script could go get them and install them for you auto-magically!). There should also be some trap or try/catch statements in the functions to better handle errors. Some auditing of the functions usage could also be implemented.

Modules are pretty cool – I hope this helps you understand them a bit better!

Monday, January 05, 2009

PowerShell-Scripting.com

My French is none too great. If it were, PowerShell-Scripting.com might be of more use. The site looks to be a rich resource for French speaking PowerShell users.

Now if only they’s list my PowerShell Scripts blog (http://pshscripts.blogspot.com)!

Technorati Tags: ,,

Sunday, January 04, 2009

Modules in PowerShell V2

Introduction

As PowerShell has evolved, there are a number of things that have been needed to be added in order to make it truly enterprise ready. One of the key concepts added into Version 2 of PowerShell is that of a module. A module is some chunk of code that you can import into PowerShell and then use. Once imported, any cmdlets, scripts, or providers can be accessed. Installation of a module is now very simple – just use xcopy. 

Modules came originally with CTP2 of PowerShell V2 and are greatly improved in the latest CTP (CTP3) issued late December 2008. Unfortunately, good documentation on how to use modules is scarce: there’s no detailed information in the release notes, no about_module help information, and all the module cmdlets lack auto-help (get-help just dumps raw syntax and offers nothing else in the way of help). Nevertheless, a bit of playing, extensive Googling, and lots of trial and error has yielded a better understanding. For such an important feature, MS has let the side down a bit. But I digress!

Modules vs Snap-ins

In Version 1, we had the Snap-in as the way to add functionality into PowerShell. Teams like Exchange used this to add their own Exchange cmdlets into Exchange 2007. You install a snap-in by copying the necessary files and then updating registry entries to point to those files. Once installed, you could use the Add-PSSnapIn cmdlet to add the functionality of the snap-in into your environment. With a snap-in, the installation routine had to be written (although it could be partly automated) and the user had to run commands to perform the installation. Modules simplify this greatly.

Modules are a replacement for Snap-ins, although V2 will still support V1 type snap-ins. Modules are a much much improved way of creating add-ins and are to be strongly preferred going forward. You can create a module either by using compiled code (as you did in V2),  but also by writing them in PowerShell script. You can decorate modules with meta data, including Parameter descriptions, to enable them to be better integrated into a user’s  PowerShell experience. By using the Auto-Help feature, the module is self documenting.

Creating a Module with Powershell CTP3

At it’s simplest, a module is just a PowerShell script, contained in a file with a .PSM1 extension. For it to be of any use, you must save this script in a folder below your modules folder which is where PowerShell looks for modules. PowerShell has an environment variable, PSMODULEPATH, which defines where modules are to be found. On my system, I can see this as follows:

PS C:\foo> dir env:psmodulepath

Name                           Value
----                           -----
PSMODULEPATH                   C:\Users\tfl\Documents\WindowsPowerShell\Modules; C:\Windows\SysWOW64\WindowsPowerShell\v1.0\Modules

As you can see, there are two folders on my system (one a per user, the other a per system). As you can also see, I’m running on an X64 OS (specifically Windows Server 2008 configured as a workstation!). I’m running PowerShell to create this post from within PowerShell Plus. Thus, you might see a different set of values on your system.

Much like Profiles, the per user module paths does not actually exist by default. You need to create the actual folder as Jeffrey Snover describes at the end of an interesting blog post. 

Sample Module

To illustrate PowerShell modules, here’s a module based on a script I posted to MDSN and to my PowerShell Scripts blog:

  1. function DeviceInterface { 
  2. param ($value
  3. switch ($value) { 
  4. 0    {"Other"
  5. 1    {"Unknown"
  6. 3    {"Serial"
  7. 4    {"PS/2"
  8. 5    {"Infrared"
  9. 6    {"HP-HIL"
  10. 7    {"Bus Mouse"
  11. 8    {"ADP (Apple Desktop Bus)"
  12. 160  {"Bus Mouse DB-9"
  13. 161  {"Bus Mouse Micro-DIN"
  14. 162  {"USB"
  15.  
  16. function Handedness { 
  17. param ($value
  18. switch ($value) { 
  19. 0 {"Unknown"
  20. 1 {"Not Applicable"
  21. 2 {"Right-Handed Operation"
  22. 3 {"Left-Handed Operation"
  23.  
  24. function PointingType { 
  25. param ($value
  26. switch ($value) { 
  27. 1 {"Other"
  28. 2 {"Unknown"
  29. 3 {"Mouse"
  30. 4 {"Track Ball"
  31. 5 {"Track Point"
  32. 6 {"Glide Point"
  33. 7 {"Touch Pad"
  34. 8 {"Touch Screen"
  35. 9 {"Mouse - Optical Sensor"
  36.  
  37. function get-MouseInfo { 
  38.  
  39. # Now do script stuff 
  40. # Get Mouse information 
  41. $mouse = Get-WmiObject -Class Win32_PointingDevice 
  42.  
  43. # Display details 
  44. "Mouse Information on System: {0}" -f $mouse.systemname 
  45. "Description            : {0}" -f $mouse.Description 
  46. "Device ID              : {0}" -f $mouse.DeviceID 
  47. "Device Interface       : {0}" -f (Deviceinterface($mouse.DeviceInterface)) 
  48. "Double Speed Threshold : {0}" -f $mouse.DoubleSpeedThreshold 
  49. "Handedness             : {0}" -f (Handedness($mouse.handedness)) 
  50. "Hardware Type          : {0}" -f $mouse.Hardwaretype 
  51. "INF FIle Name          : {0}" -f $mouse.InfFileName 
  52. "Inf Section            : {0}" -f $mouse.InfSection 
  53. "Manufacturer           : {0}" -f $mouse.Manufacturer 
  54. "Name                   : {0}" -f $mouse.Name 
  55. "Number of buttons      : {0}" -f $mouse.NumberOfButtons 
  56. "PNP Device ID          : {0}" -f $mouse.PNPDeviceID 
  57. "Pointing Type          : {0}" -f (Pointingtype ($mouse.PointingType)) 
  58. "Quad Speed Threshold   : {0}" -f $mouse.QuadSpeedThreshold 
  59. "Resolution             : {0}" -f $mouse.Resolution 
  60. "Sample Rate            : {0}" -f $mouse.SampleRate 
  61. "Synch                  : {0}" -f $mouse.Synch 
  62.  
  63. # Export just the last function. 
  64. Export-ModuleMember Get-Mouseinfo 
  65. # End of Script 

Using Modules

First, I saved this file to MouseInfo.psm1, and then I stored that file under the folder MouseInfo contained in my per-user module folders (in specific, C:\Users\tfl\Documents\WindowsPowerShell\Modules\MouseInfo). Once this folder and the .PSM1 file are in place, you add this module into PowerShell by calling Import-Module, as follows:

PS C:\foo> # Note function does not currently exist!
PS C:\foo> get-mouseinfo
The term 'get-mouseinfo' is not recognized as a cmdlet, function, operable program, or script file. Verify the term and try again.
At line:1 char:14
+ get-mouseinfo <<<<
    + CategoryInfo          : ObjectNotFound: (get-mouseinfo:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

PS C:\foo> # So now import it – note no output by default
PS C:\foo>
Import-Module mouseinfo
PS C:\foo> # now use a function contained in the module
PS C:\foo> get-mouseinfo
Mouse Information on System: COOKHAM8
Description            : PS/2 Compatible Mouse
Device ID              : ACPI\PNP0F13\4&33DCE2F0&0
Device Interface       : PS/2
Double Speed Threshold :
Handedness             :
Hardware Type          : PS/2 Compatible Mouse
INF FIle Name          : msmouse.inf
Inf Section            : PS2_Inst
Manufacturer           : Microsoft
Name                   : PS/2 Compatible Mouse
Number of buttons      : 0
PNP Device ID          : ACPI\PNP0F13\4&33DCE2F0&0
Pointing Type          : Unknown
Quad Speed Threshold   :
Resolution             :
Sample Rate            :
Synch                  :
PS C:\foo> # Find out more with Get-Module cmdlet
PS C:\foo> Get-Module mouseinfo

Name              : mouseinfo
Path              : C:\Windows\SysWOW64\WindowsPowerShell\v1.0\Modules\mouseinfo\mouseinfo.psm1
Description       :
Guid              : 00000000-0000-0000-0000-000000000000
Version           : 0.0
ModuleBase        : C:\Windows\SysWOW64\WindowsPowerShell\v1.0\Modules\mouseinfo
ModuleType        : Script
PrivateData       :
AccessMode        : ReadWrite
ExportedAliases   : {}
ExportedCmdlets   : {}
ExportedFunctions : {[get-MouseInfo, get-MouseInfo]}
ExportedVariables : {}
NestedModules     : {}

As you can see, importing a module is very simple (once it’s in the right folder). And once you have imported your module, you can use the functions from the module that have been exported. In the MouseInfo module above, there are several helper functions to decrypt the values returned from WMI and which I (as the module’s author) do not wish to expose to a user of the module. The only exported function – in other words the only function that you can use from this module – is Get-MouseInfo. I explicitly export this function by using the Export-ModuleMember cmdlet in line 71 of the module.

Module Cmdlets

Modules are a key part of CTP3. To support modules, there are 7 module related cmdlets included with Powershell V2 CTP3, as follows:

  • New-Module – creates a new module from a script block.
  • Import-Module – imports a module from your Modules folder
  • Export-ModuleMember – notes the functions that a module exports and you can use once you import the module
  • Get-Module – gets information about modules
  • Remove-Module – removes a module
  • New-ModuleManifest – Helps create a new module manifest
  • Test-ModuleManifest – tests a module manifest

In addition, you can use the Get-Command cmdlet, specifying the module you want to get information from. For example:

PS MyMod:\> Get-Command -Module mouseinfo

CommandType Name              Definition
----------- ----              ----------
Function    get-MouseInfo     ...

I’ll try to blog more details on the module (and manifest) related cmdlets in due course.

Module Manifests

A module manifest is a specially constructed PowerShell script saved in a .PSD1 file. A module manifest is used to define precisely what is contained in a module. A manifest is an optional component, but the PowerShell team strongly advises use of a manifests to better document and describe a module. For more complex modules, e.g. more than just a single .psm1 file, a manifest is probably required.

A Module Manifest is really just a script that creates a hash table. This hash table contains the keys/values that PowerShell uses in managing your module. Since the manifest is pretty simple, it is very easy create one – just use your favourite script edtiro, and add in the values that you need. To make things even simpler, you can use the New-ModuleManifest cmdlet to create a manifest (and then tweak it using your favourite script editor).

Based on the MouseInfo module above, creating a new manifest using New-ModuleManifest is simple:

PS C:\foo> New-Modulemanifest .\mouseinfo.psd1

cmdlet New-ModuleManifest at command pipeline position 1
Supply values for the following parameters:
NestedModules[0]:Mouseinfo.psm1
Author: Thomas Lee
CompanyName: PS Partnership
Copyright: 2009
Description: My first module
TypesToProcess[0]:
FormatsToProcess[0]:
RequiredAssemblies[0]:
OtherFiles[0]:

After a bit of reformatting for publication and tidiness, here’s what the Mouseinfo module manifest  looks like:

  1. # Module manifest for module 'Mouseinfo' 
  2. # Generated by: Thomas Lee 
  3. # Generated on: 1/3/2009 
  4. @{ 
  5. # These modules will be processed when the module manifest is loaded. 
  6. NestedModules = ‘Mouseinfo.psm1’
  7. # This GUID is used to uniquely identify this module. 
  8. GUID = '94979266-70b4-4243-bef8-6fd87529af69' 
  9. # The author of this module. 
  10. Author = 'Thomas Lee' 
  11. # The company or vendor for this module. 
  12. CompanyName = 'PS Partnership' 
  13. # The copyright statement for this module. 
  14. Copyright = '2009' 
  15. # The version of this module. 
  16. ModuleVersion = '1.0' 
  17. # A description of this module. 
  18. Description = 'Cool Module' 
  19. # The minimum version of PowerShell needed to use this module. 
  20. PowerShellVersion = '2.0' 
  21. # The CLR version required to use this module. 
  22. CLRVersion = '2.0' 
  23. # Functions to export from this manifest. 
  24. ExportedFunctions = 'Get-MouseInfo' 
  25. # Aliases to export from this manifest. 
  26. ExportedAliases = '*' 
  27. # Variables to export from this manifest. 
  28. ExportedVariables = '*' 
  29. # Cmdlets to export from this manifest. 
  30. ExportedCmdlets = '*' 
  31. # This is a list of other modules that must be loaded before this module. 
  32. RequiredModules = @() 
  33. # The script files (.ps1) that are loaded before this module. 
  34. ScriptsToProcess = @() 
  35. # The type files (.ps1xml) loaded by this module. 
  36. TypesToProcess = @() 
  37. # The format files (.ps1xml) loaded by this module. 
  38. FormatsToProcess = @() 
  39. # A list of assemblies that must be loaded before this module can work. 
  40. RequiredAssemblies = @() 
  41. # Lists additional items like icons, etc. that the module will use. 
  42. OtherItems = @() 
  43. # Module specific private data can be passed via this member. 
  44. PrivateData = '' 

If I had included this manifest in my Mouseinfo module folder, after importing the Module, I’d be able to get better help information about the module (e.g. Get-Module would provide the description and the GUID created by New-ModuleManifest, etc). Manifests are an important aspect of PowerShell modules – I’ll cover them more in a future blog post.

References For More Information on Modules

As noted above, details on modules in CTP3 are hard to come by (and many are out of date) . Even the help text is pretty bare! Some blog posts that discuss modules and may be still be useful include:

Summary

With PowerShell V2 CTP3, you create and use modules to add functionality into PowerShell simply and easily. Modules are built using code, script or a combination. A module can be just one .PSM1 file, while more complex modules can be a combination of code, script, and other resources described in a module manifest. You can control what your users see when they import a module by using a module manifest and by using Export-ModuleMember cmdlet. To manage modules, you have a number of module related cmdlets to use. Finally, Module manifests are a key tool to describe modules.

I’ll be posting more about modules in the coming weeks.

Technorati Tags: ,,,

Saturday, January 03, 2009

Parameter Attributes in PowerShell V2 CTP Advanced Functions

In PowerShell V2 CTP3, you can create Advanced Functions which are, in effect, cmdlets written in script. I’ve already blogged about some of the cool features, particularly AutoHelp, which I find utterly cool! One neat aspect is the ability to define the parameters that your script accepts. Thus you can get PowerShell to do at least some of the validation of the parameter for you automagically. With Advanced Functions, you can do two things: you can define the parameter details in your script and have Get-Help provide them as documentation and you can decorate your script with Attributes telling PowerShell how to treat each parameter.

For many PowerShell users, the terms “decorate” and “attribute” may be new. By decorate, I mean just adding in extra text. Attributes are text that tell PowerShell how to do something. In CTP3, you can specify the [Parameter] attribute – decorating your script with these attributes (i.e. adding in the text to your script) enables you to tell PowerShell just how it should handle parameters to your script. I believe this is an important feature – and takes PowerShell further down the road towards being a full .NET language.

Here’s a snipped of code, a param block, showing the use of [Parameter] attributes:

  1. param
  2. [Parameter(Position=0, Mandatory=$false)] 
  3. [string] $Domain = "Cookham" ,     
  4. [Parameter(Position=1, Mandatory=$false)] 
  5. [string] $Computer = "Cookham8"
  6. [Parameter(Position=2, Mandatory=$false
  7. [string] $User     = "tfl"             

This parameter (param) block defines three script parameters ($domain, $computer, $user). The Parameter attributes are also specified using named attributes (eg Mandatory, Position, etc) to ensure PowerShell knows how to handle them. The documentation thus far on these parameter attributes is limited. Well, limited in terms of PowerShell documentation. The MSDN Library has a ton of documentation on how to write PowerShell Cmdlet, but these are written for C# programmers. I’m assuming that, in due course, there will be additional documentation that is more PowerShell focused.

If this script fragment were from a script called Get-Foo (ok bad name but work with me!), then you might call this script as follows:

PS C:\foo> Get-Foo NWTraders MyWorkstation BillyBob

In terms of Parameter attributes, http://msdn.microsoft.com/en-us/library/ms714348(VS.85).aspx contains a list of the name attributes you can use. These are:

  • Mandatory  - a boolean saying whether the parameter is required or not. A mandatory parameter that is not specified generates a run time exception.
  • ParameterSetName – a string naming the parameter set a parameter belongs to. You can use this name in your script to handle different sets of parameters (that result in different behaviour of your script).
  • Position – an integer specifying where parameter’s order. As you can see in the above example, the $User paramater is the third parameter, while $domain is the first and $computer the second.
  • ValueFromPipeline – a boolean indicating if the parameter can come from the pipeline.
  • ValueFromPipelineByPropertyName  - a boolean indicating whether the parameter is filled from a property of the piped-in object that has either the same name or the same alias as this parameter.
  • ValueFromRemainingArguments – a Boolean indicating whether this cmdlet accepts all the remaining arguments passed. This is a good way to pickup any overspecified parameters.
  • HelpMessage – a string containing a short description of this parameter.
  • HelpMessageBaseName – the base name of the help message resource.
  • HelpMessageResourceId – an identifier used when a help message is localised.

Here is a slightly longer script demonstrating some of these:

  1. <#
  2. .SYNOPSIS 
  3.     Shows Parameter attributes 
  4. .DESCRIPTION 
  5.     Script is decorated with Parameter attributes to demostrate the use of them 
  6. .NOTES 
  7.     File Name  : Get-ParameterAttribute1.ps1 
  8.     Author     : Thomas Lee - tfl@psp.co.uk 
  9.     Requires   : PowerShell V2 CTP3 
  10. .LINK 
  11.     To be posted at: 
  12.     http://www.pshscripts.blogspot.com 
  13. .EXAMPLE 
  14.     Simple usage, with partial parameters specified 
  15.     PS C:\foo> .\Get-ParameterAttribute1.ps1 abc 
  16.     ---- 
  17.     Domain    : abc 
  18.     Computer  : Cookham8 
  19.     User      : tfl 
  20.     ---- 
  21. .EXAMPLE 
  22.     Simple usage, with all parameters specified 
  23.     PS C:\foo> .\Get-ParameterAttribute1.ps1 kapoho, kapoho1, BigKahuna 
  24.     ---- 
  25.     Domain    : kapoho 
  26.     Computer  : kapoho1 
  27.     User      : BigKahuna 
  28.     ---- 
  29. .EXAMPLE 
  30.     Showing getting first parameter from the pipeline 
  31.     PS C:\foo> "abc", "def", "GHI" |.\Get-ParameterAttribute1.ps1 
  32.     ---- 
  33.     Domain    : abc 
  34.     Computer  : Cookham8 
  35.     User      : tfl 
  36.     ---- 
  37.     ---- 
  38.     Domain    : def 
  39.     Computer  : Cookham8 
  40.     User      : tfl 
  41.     ---- 
  42.     ---- 
  43.     Domain    : GHI 
  44.     Computer  : Cookham8 
  45.     User      : tfl 
  46.     ---- 
  47. EXAMPLE 
  48.     Shows getting all Value From Remaining Arguments 
  49.     PS C:\foo> .\Get-ParameterAttribute1.ps1 abc def ghi xxx xxx xxx xxx 
  50.     ---- 
  51.     Domain    : abc 
  52.     Compuyter : def 
  53.     User      : ghi xxx xxx xxx xxx 
  54. .PARAMETER Domain 
  55.     A domain name - must be a string 
  56. .PARAMETER Computer 
  57.     A computer Name - must be a string 
  58. .PARAMETER User 
  59.     A user name - must be a string 
  60. #> 
  61. param
  62. [Parameter(Position=0, Mandatory=$true,ValueFromPipeLine=$true)] 
  63. [string] $Domain = "Cookham" ,     
  64. [Parameter(Position=1, Mandatory=$false)] 
  65. [string] $Computer = "Cookham8"
  66. [Parameter(Position=2, Mandatory=$false, ValueFromRemainingArguments=$true)] 
  67. [string] $User     = "tfl"             
  68.  
  69. Process { 
  70. "----" 
  71. "Domain    : {0}" -f $domain 
  72. "Computer  : {0}" -f $computer 
  73. "User      : {0}" -f $user 
  74. "----" 

Friday, January 02, 2009

Posting PowerShell Scripts – one solution

On my PowerShell Scripts blog, I publish daily, or near daily, PowerShell scripts. I am slowly decorating the MSDN on-line library with PowerShell samples. I am also playing with the latest version of PowerShell V2 (aka CTP3) and the new auto-help feature.

Now that CTP3 is out, I’ve decided to change the way I posts scripts – I’ll use the auto-help approach, and include examples in the posted script. You may have noticed based on recently posted scripts. Well – those of you who look at my script blog have noticed. May have noticed. Or not…

In the past, when I’ve created a script, I use Live Writer to create the blog post. In that post, I copy in the script AND the results (each formatted separately). With V2 CTP3’s auto-help, I’ve decided to decorate each script post with documentation that includes expected output. All of this is neatly embedded into the script as posted, with expected output specified in the .EXAMPLE section in the opening comment block.

With the new auto-help format, I can also document the posted scripts better – a comment made on the blog some whiles back.

I’d be interested in thoughts on this idea. Suggestions naturally welcome.

Thursday, January 01, 2009

Free Group Policy Cmdlet from SDM Software

Those nice folks over at the SDM Software Group have posted a new Group Policy Cmdlet. This PoweShell cmdlet lets you check the health of Group Policy processing across computers in your domain. The cmdlethas a number of parameters you can use to check different aspects of GP Health. The cmdlet provides a quick "red or green" status indication of GP processing on an end-user system. It also provides a detailed listing of GPOs that apply to a given computer/user.  A very neat tool! And free!

The cmdlet comes wrapped in an MSI that installs the cmdlet into your environment. After running the MSI, you just need to invoke add-pssnapin to get the snap-in installed into your environment.,