Categories

10391

Testing module existence

I had a comment left on an old post stating that Get-ADuser errored stating it wasn’t a cmdlet.  This is because the module wasn’t loaded or on PowerShell 3 and above available to be auto-imported.  That got me thinking about testing for a modules existence.

function test-module {
[CmdletBinding()]
param (

[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$name,

[Parameter(ParameterSetName='Installed')]
[switch]$installed,

[Parameter(ParameterSetName='Loaded')]
[switch]$loaded

)

switch ($psCmdlet.ParameterSetName) {
'Installed' {
                Get-Module -Name "*$name*" -ListAvailable
                break
             }
'Loaded'    {
                Get-Module -Name "*$name*"

             }
default     {
                Throw "Error!!! Should not be here"
             }
}

}

Define a parameter for the module name and 2 switch parameters – loaded tests if the module is loaded into PowerShell and installed tests if the module can be found on the module path.

I’ve used parameter sets to make the switches mutually exclusive. 

A switch statement based on the parameter set name calls the Get-Module in an appropriate manner. Notice that the module name you supply is wrapped in wildcards so you don’t have to type the full module name.

You can use the function like this:

test-module -name cim –installed

or

test-module -name cim -loaded

You can even do this:

if (-not (test-module -name cim -loaded)){throw "module not found"}

CDXML: Module Manifest

Last time we created a module using CDXML to wrap the Win32_Bios WMI class. This gave us a cmdlet – Get-Bios.  As the intention is to create a number of modules that expose the WMI classes related to hardware we need a module manifest file (.psd1) to load them so that we can take advantage of module auto-loading in PowerShell 3 & 4

Remember – one WMI class per CDXML file and each CDXML file is treated as a module

I find the easiest way to create new manifest is run New-ModuleManifest and give it the full path to the psd1 file you want to create

 

New-ModuleManifest -Path C:\scripts\Modules\Hardware\Hardware.psd1 –PassThru

 

You can then open the file in ISE and edit to give this:

#
# Module manifest for module 'Hardware'
#
# Generated by: richard
#
# Generated on: 30/11/2013
#

@{

# Script module or binary module file associated with this manifest.
# RootModule = ''

# Version number of this module.
ModuleVersion = '1.0'

# ID used to uniquely identify this module
GUID = '55512ad7-c2aa-4678-818f-8f19b4f110dd'

# Author of this module
Author = 'Richard'

# Company or vendor of this module
CompanyName = 'Macdui'

# Copyright statement for this module
Copyright = '(c) 2013 Richard. All rights reserved.'

# Description of the functionality provided by this module
# Description = ''

# Minimum version of the Windows PowerShell engine required by this module
# PowerShellVersion = ''

# Name of the Windows PowerShell host required by this module
# PowerShellHostName = ''

# Minimum version of the Windows PowerShell host required by this module
# PowerShellHostVersion = ''

# Minimum version of Microsoft .NET Framework required by this module
# DotNetFrameworkVersion = ''

# Minimum version of the common language runtime (CLR) required by this module
# CLRVersion = ''

# Processor architecture (None, X86, Amd64) required by this module
# ProcessorArchitecture = ''

# Modules that must be imported into the global environment prior to importing this module
# RequiredModules = @()

# Assemblies that must be loaded prior to importing this module
# RequiredAssemblies = @()

# Script files (.ps1) that are run in the caller's environment prior to importing this module.
# ScriptsToProcess = @()

# Type files (.ps1xml) to be loaded when importing this module
# TypesToProcess = @()

# Format files (.ps1xml) to be loaded when importing this module
# FormatsToProcess = @()

# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
NestedModules = @('Win32_BIOS.cdxml')

# Functions to export from this module
FunctionsToExport = @('Get-Bios')

# Cmdlets to export from this module
CmdletsToExport = '*'

# Variables to export from this module
VariablesToExport = '*'

# Aliases to export from this module
AliasesToExport = '*'

# List of all modules packaged with this module
# ModuleList = @()

# List of all files packaged with this module
# FileList = @()

# Private data to pass to the module specified in RootModule/ModuleToProcess
# PrivateData = ''

# HelpInfo URI of this module
# HelpInfoURI = ''

# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
# DefaultCommandPrefix = ''

}

 

You can cut out the items you don’t need but I prefer to leave them as reminders of the commands.

Once the file is modified – save it back as Hardware.psd1

Start a new PowerShell console and your new module is available for use immediately.

This also means that you can add new CDXML files and test them independently of the module. Once you’re happy with the new functionality you add the appropriate lines to the module manifest.

A count down timer

Last month I started playing with WPF to show how a multi-coloured clock could  be displayed on screen. This was picked up and an easier version was created by Doug Finke using the ShowUI module from codeplex  http://showui.codeplex.com/.

Other links are here

http://msmvps.com/blogs/richardsiddaway/archive/2011/07/08/an-easier-clock.aspx

 

As well as wanting to know the time I often want to set a count down timer.  I did a version of this ages ago using PowerGadgets but  if you don’t have that then we can do something with show UI

Import-Module ShowUI                        
            
function timeleft {            
 $ts = $close - (Get-Date)            
 $time = $ts.ToString() -split "\."            
 $time[0].Substring(3,5)            
}            
            
            
$close = (Get-Date).AddMinutes(35)            
            
$windowAttributes = @{            
    WindowStartupLocation = "CenterScreen"            
    SizeToContent = "WidthAndHeight"            
    WindowStyle = "None"            
    Background = "Transparent "                        
            
    On_MouseRightButtonDown = { Close-Control}            
    On_MouseLeftButtonDown  = { $_.Handled = $true;$window.DragMove() }            
    On_Loaded = {            
        Register-PowerShellCommand -ScriptBlock {            
            $window.Content.Content = timeleft            
        } -Run -In "0:0:0.5"            
    }            
}                        
            
$labelAttributes = @{            
    Content = timeleft            
    FontFamily = "Impact, Arial"            
    FontWeight = 800            
    FontSize = 90            
}                        
            
New-Window @windowAttributes -AllowsTransparency -Show  {            
    Label @labelAttributes -Name Clock -Foreground (            
        LinearGradientBrush $(            
            GradientStop -Color Red    -Offset 1            
            GradientStop -Color Orange -Offset 0.85            
            GradientStop -Color Yellow -Offset 0.7            
            GradientStop -Color Green  -Offset 0.55            
            GradientStop -Color Blue   -Offset 0.4            
            GradientStop -Color Indigo -Offset 0.2            
            GradientStop -Color Violet -Offset 0            
        )            
    )            
}


All I changed was to point the Content to the timeleft function instead of get-date. The function takes the closing time – in this case 35 minute from starting and subtracts the current datetime (from get-date). The resultant timespan is converted to a string and split at the millisecond point. The minutes and seconds are extracted as  a substring to display



By the way – anyone noticed the deliberate(?) mistake with the colours

Scripting Guy discusses PAM modules

My codeplex project publishing PowerShell Admin Modules (PAM) is discussed in this post

http://blogs.technet.com/b/heyscriptingguy/archive/2011/06/29/don-t-write-wmi-scripts-use-a-powershell-module.aspx

In particular the Get-OSInfo function from the PAMSysInfo module is heavily featured

Snapins to modules

In PowerShell v1 we had snapins to extend and add functionality. In PowerShell v2 modules were added. Modules give us more flexibility and control.

I’ve had to re-install a couple of snapins recently as part of the preparation for the presentation to the user group. I’d not put them on to my laptop when I rebuilt it as Windows 7.

The PowerShell provider for OneNote comes as an MSI that installs the snapin and registers it with Powershell – can’t do much with that – easier to leave as is.

The OpenXML snapin is available as source code or the binaries can be down loaded which consist of a dll and a help file.

To get the snapin loaded either use the supplied batch file to register the dll or create a module manifest file

#
# Module manifest for module 'OpenXml'
#
# Generated by: Richard
#
# Generated on: 18/06/2011
#

@{

# Script module or binary module file associated with this manifest
ModuleToProcess = 'OpenXml.PowerTools.dll'

# Version number of this module.
ModuleVersion = '1.0'

# ID used to uniquely identify this module
GUID = '0275db55-3bf5-4eec-aecd-1a5cd1764c32'

# Author of this module
Author = 'Richard'

# Company or vendor of this module
CompanyName = 'Macdui'

# Copyright statement for this module
Copyright = 'Macdui'

# Description of the functionality provided by this module
Description = 'Loads OpenXML snapin'

# Minimum version of the Windows PowerShell engine required by this module
PowerShellVersion = ''

# Name of the Windows PowerShell host required by this module
PowerShellHostName = ''

# Minimum version of the Windows PowerShell host required by this module
PowerShellHostVersion = ''

# Minimum version of the .NET Framework required by this module
DotNetFrameworkVersion = ''

# Minimum version of the common language runtime (CLR) required by this module
CLRVersion = ''

# Processor architecture (None, X86, Amd64, IA64) required by this module
ProcessorArchitecture = ''

# Modules that must be imported into the global environment prior to importing this module
RequiredModules = @()

# Assemblies that must be loaded prior to importing this module
RequiredAssemblies = @()

# Script files (.ps1) that are run in the caller's environment prior to importing this module
ScriptsToProcess = @()

# Type files (.ps1xml) to be loaded when importing this module
TypesToProcess = @()

# Format files (.ps1xml) to be loaded when importing this module
FormatsToProcess = @()

# Modules to import as nested modules of the module specified in ModuleToProcess
NestedModules = @()

# Functions to export from this module
FunctionsToExport = '*'

# Cmdlets to export from this module
CmdletsToExport = '*'

# Variables to export from this module
VariablesToExport = '*'

# Aliases to export from this module
AliasesToExport = '*'

# List of all modules packaged with this module
ModuleList = @()

# List of all files packaged with this module
FileList = @()

# Private data to pass to the module specified in ModuleToProcess
PrivateData = ''

}

 

Use new-ModuleManifest to create the file and save it with as a .psd1 file. 

Its now available as a module and can be used as such.

Advanced Function template

In this post I showed my Advanced Function template  http://msmvps.com/blogs/richardsiddaway/archive/2011/05/13/powershell-module-construction.aspx

I’ve since modified it to add the parameter validation methods. I can never remember them all so decided putting them in the template was the easiest way forward.

 

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039

function aaaa-yyyyyy{ 
[CmdletBinding(SupportsShouldProcess=$true, 
    ConfirmImpact="Medium Low High None", 
    DefaultParameterSetName="XXXXX")] 
param ( 
[parameter(Position=0,
   Mandatory=$true,
   ParameterSetName="YYYYYYYYYY",
   ValueFromPipeline=$true, 
   ValueFromPipelineByPropertyName=$true,
   ValueFromRemainingArguments=$true,
   HelpMessage="Put your message here",
   Alias("CN", "ComputerName")  )] 
   [AllowNull()]
   [AllowEmptyString()]
   [AllowEmptyCollection()]
   [ValidateCount(1,10)]
   [ValidateLength(1,10)]
   [ValidatePattern("[A-Z]{2,8}[0-9][0-9]")]
   [ValidateRange(0,10)]
   [ValidateScript({$_ -ge (get-date)})]
   [ValidateSet("Low", "Average", "High")]
   [ValidateNotNull()]
   [ValidateNotNullOrEmpty()]
   [string]$computer="." 
) 
BEGIN{}#begin
PROCESS{

if ($psCmdlet.ShouldProcess("## object ##", "## message ##")) {
    ## action goes here
}

}#process
END{}
#end

<#
.SYNOPSIS
.DESCRIPTION
.PARAMETER <Parameter-Name>
.EXAMPLE
.INPUTS
.OUTPUTS
.NOTES
.LINK
#>

}

 

One or two may not be right on a string parameter but they are there as a reminder not code that will run

New PAM version

I’ve added the 0.6 release of the PowerShell Admin Modules to codeplex - http://psam.codeplex.com/

This release adds two modules

PAMADSNAPSHOT – functions for work with AD snapshots in Windows 2008 & 2008 R2

PAMLOAD – loads all of the PAM modules in one command

Import-Module PAMLOAD –Force

 

Further releases are planned during the year

PowerShell Module Construction

I’ve created a fair number of PowerShell modules over the last year or so. I’ve experimented with various configurations:

  • multiple functions in a single .psm1 file
  • single function per file all loaded from the .psm1 file
  • modules with and without manifests
  • comment based help in various configurations
  • external help files

I have standardised on the following:

  • single function per file
  • comment based help at the end of the function. Its present but its not intrusive

This is the template I use

function aaaa-yyyyyy{
[CmdletBinding()]
param (
[parameter(ValueFromPipeline=$true,
   ValueFromPipelineByPropertyName=$true)]
[string]$computer="."
)
BEGIN{}#begin
PROCESS{}#process
END{}#end

<#
.SYNOPSIS


.DESCRIPTION


.PARAMETER  <Parameter-Name>


.EXAMPLE


.INPUTS


.OUTPUTS


.NOTES


.LINK

#>

}

 

I’ve put in the CmdletBinding() so I get an advanced function. I’ve added the parameter statement for pipeline input.

The function body is split into BEGIN/PROCESS/END blocks to make me think about the way the function will work

The keywords for the comment based help are present and the comment block has spaces before and after to ensure other comments don’t conflict.

I keep this as a template to load into ISE.

One thing I might add is the other parameter attributes and the validation functions to save looking them up.

Feel free to amend this suit your needs.

PowerShell UK User Group–May meeting slides and recording

Thanks again to Jonathan Medd for an excellent session on PowerShell modules. As promised the slides and demo scripts are available on Jonathan’s blog

http://www.jonathanmedd.net/2011/05/slides-from-uk-powershell-user-group-session-on-modules.html

 

The session recording will be available for the next 365 days from:

Richard Siddaway has invited you to view a Microsoft Office Live Meeting recording.
View Recording
Recording Details
Subject: PowerShell Modules
Recording URL: https://www.livemeeting.com/cc/usergroups/view
Recording ID: 8TWQGF
Attendee Key: 6NB,TJm(m

This Office Live Meeting invitation is a personal invitation meant only for you. It should not be forwarded. If you received this email by mistake or require Live Meeting Assistance, please refer to the Live Meeting Assistance Center at http://r.office.microsoft.com/r/rlidLiveMeeting?p1=12&p2=en_US&p3=LMInfo&p4=support

Creating a module

I’m writing a new PowerShell book and have got to the fun bit where I’m creating lots of scripts. I wanted to supply these as a series of modules. So each individual listing is a function. But I don’t want to manually maintain the module file as I’m working through each chapter. Usually I create a module with one .psm1 file that has all of the functions in it.

 

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015

## get folder name
$path = Get-Location
$folder = Split-Path -Path $path.path -Leaf

$mfile =  "$folder.psm1"

## remove old module file
if (Test-Path -Path $mfile){Remove-Item -Path $mfile}
 
## get files
Get-ChildItem -Filter *.ps1 | 
Select Name | Sort Name |
foreach {
 ". ./$($_.Name)" | Out-File -FilePath $mfile -Append
}

 

It is also possible to have a .psm1 file that runs a set of other PowerShell scripts. So that’s what I’ve done. The script shows how I can create a module file by getting the names of the individual PowerShell files adding the “. ./” in front to dot source them.

One quick module file with minimum effort.