Learn How to Use Description Attributes in PowerShell Functions

Learn How to Use Description Attributes in PowerShell Functions

  • Comments 2
  • Likes

Summary: Microsoft Scripting Guy, Ed Wilson, teaches you how to use description attributes to enable a quick retrieval of Windows PowerShell functions.

Weekend Scripter

Microsoft Scripting Guy, Ed Wilson, is here. Well, the 2011 Scripting Games are nearly completely over. “What?” you may ask. “Is there more to come?” Not specifically. But the impact of the 2011 Scripting Games will continue to be felt for the rest of the year. Common problems that Scripting Games participants had will continue to be addressed in future Hey, Scripting Guy! blogs, in conference presentations, and in feedback to other Windows PowerShell writers at Microsoft.

But today, I am not going to write about the 2011 Scripting Games. Instead I want to talk about something that is simply cool. You will need to decide how you want to use the information. That is right—I have a solution that is searching for a problem to solve. I got the idea while reading Windows PowerShell Cookbook by Lee Holmes.

As I was searching for a problem, I initially thought that I could use my solution to identify functions as coming from specific modules. This was something that if I knew about previously, I had forgotten; and therefore, I thought my solution would be a cool approach. However, before I expend much effort on a task, I generally like to see what capabilities Windows PowerShell exposes.

As it turns out, you can easily find commands that are exported from a module by using the Get-Module cmdlet. I then use the Select-Object cmdlet to expand the exportedCommands property. The reason for this, is that without using the ExpandProperty parameter, it is difficult to see all of the commands that are actually exported. This is illustrated by the following series, where I try a variety of commands to expand the ExportedCommands property.

PS C:\Users\ed.NWTRADERS> Get-Module conv* | Select-Object exportedCommands

 

ExportedCommands                                                                                                                                                                                                                          

----------------                                                                                                                                                                                                                          

{ConvertTo-Feet, ConvertTo-Miles, ConvertTo-MetersPerSecond, ConvertTo-Pounds...}                                                                                                                                                         

 

PS C:\Users\ed.NWTRADERS> Get-Module conv* | Select-Object exportedCommands | fl *

 

ExportedCommands : {ConvertTo-Feet, ConvertTo-Miles, ConvertTo-MetersPerSecond, ConvertTo-Pounds...}

 

PS C:\Users\ed.NWTRADERS> Get-Module conv* | Select-Object exportedCommands | ft *

 

ExportedCommands                                                                                                                                                                                                                          

----------------                                                                                                                                                                                                                          

{ConvertTo-Feet, ConvertTo-Miles, ConvertTo-MetersPerSecond, ConvertTo-Pounds...}                                                                                                                                                         

 

PS C:\Users\ed.NWTRADERS> Get-Module conv* | Select-Object exportedCommands | fw *

 

{ConvertTo-Feet, ConvertTo-Miles, ConvertTo-MetersPerSecond, ConvertTo-Pounds...} 

The following command shows the use of the ExpandProperty parameter. Note that I am using Get-Module to retrieve the conversion module that I wrote in the first six Weekend Scripter blogs. The complete conversion module is available on the Scripting Guys Script Repository.

PS C:\Users\ed.NWTRADERS> Get-Module conv* | select-object -ExpandProperty exportedCommands

 

Name                           Value          

----                           -----           

ConvertTo-Feet                 ConvertTo-Feet        

ConvertTo-Miles                ConvertTo-Miles         

ConvertTo-MetersPerSecond      ConvertTo-MetersPerSecond      

ConvertTo-Pounds               ConvertTo-Pounds       

ConvertTo-Meters               ConvertTo-Meters       

ConvertTo-celsius              ConvertTo-celsius      

ConvertTo-Fahrenheit           ConvertTo-Fahrenheit     

ConvertTo-Liters               ConvertTo-Liters      

ConvertTo-Kilometers           ConvertTo-Kilometers     

Therefore, as it turns out, using the comment attribute does not “buy me anything” for identifying functions as coming from a module. But what about a higher level of abstraction? My Windows PowerShell ISE profile loads a specific module and several functions that are contained in separate files. This is shown in the following image.

Image of command output

Although I can use the Get-Module cmdlet to retrieve functions that are exported from my Windows PowerShell ISE profile module, I will not be able to retrieve the functions that are contained in other files. To rectify this, I can use my “solution in search of a problem” comment attribute technique.

For example, I have no information that tells me that the Get-UsGovWeather function (created in the Get-UsGovWeatherFunction.ps1 script) is loaded via my profile. If I use Get-Item to retrieve information about that function, the only hint might be that it comes from the iseProfileFileModule folder. This is shown in the following image.

Image of command output

Therefore, the solution is to add a description attribute to the Get-UsGovWeather function. This is shown here:

[System.ComponentModel.Description("Loaded via Profile")]

To retrieve this information, I access the attributes property from the ScriptBlock object. One way to do this is shown here:

PS C:\Users\ed.NWTRADERS> (Get-Command Get-UsGovWeather).Scriptblock.attributes

 

Description                                              TypeId        

-----------                                              ------    

Loaded via Profile                                       System.ComponentModel.DescriptionAttribute         

When I add the Description attribute to a function, it needs to be the first line after the function declaration. This is shown here:

Function Get-UsGovWeather

{

 [System.ComponentMOdel.Description("Loaded via Profile")]

 Param([string]$zip,

       [int]$numberDays

      )

You can see that this appears even before the Param statement. One thing that I have not been able to do is to integrate this with comment-based Help. If I place the command before the comment-based Help, as shown here, an error occurs.

function Save-AllISEFiles

{

 [System.ComponentMOdel.Description("Loaded via Profile")]

 <#

 .SYNOPSIS

     Saves all ISE Files except for untitled files. If You have

The error message from the previous command is shown here:

Unexpected attribute 'System.ComponentMOdel.Description'.

At C:\Users\ed.NWTRADERS\documents\WindowsPowerShell\Modules\iseProfileModule\Save-AllIseFiles.ps1:3 char:36

+  [System.ComponentMOdel.Description <<<< ("Loaded via Profile")]

    + CategoryInfo          : ParserError: (:) [], ParseException

    + FullyQualifiedErrorId : UnexpectedAttribute

Depending on where I place the command, the script seems to alternate from ignoring the command or generating an error. It might not be possible to combine this technique with a function that uses comment-based Help.

To retrieve all of the descriptions, you can use code such as the Get-FunctionAttributes script, as shown here:

Get-FunctionAttributes.ps1

foreach( $function in (Get-Command -CommandType function))

{

 if($function.scriptblock.attributes[0].description)

  {

    write-host $function.Name $function.scriptblock.attributes[0].description

  }

}

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • There was discussion on getting function's source not long ago on Twitter (?). Proposed solution that works great both for dot-sourced functions and functions in modules is File property of function's scriptblock:

    Get-Command -CommandType Function | foreach { $_.ScriptBlock.File }

    It failed only with few functions for me, mainly built-in ones.

    Re: adding description and help: not sure what could be the cause, but this syntax worked fine for me (I can see both help and description is visible):

    function Test-Description {

    <#

    .SYNOPSIS

    This is only to test if it works.

    .DESCRIPTION

    I do not have any details to share. :P

    .PARAMETER  Test

    Nothing interesting here.

    .EXAMPLE

    Test-Description

    .INPUTS

    System.Int32

    .OUTPUTS

    System.Int32

    .NOTES

    :D

    .LINK

    blogs.technet.com/.../learn-how-to-use-description-attributes-in-powershell-functions.aspx

    #>

    [System.ComponentModel.Description("Loaded from File")]

    param (

    [int]$Test

    )

    $Test * 2

    }

    My guess is that it has same limitations that [CmdletBinding()] has - it has to be just before param() statement.

  • @Bartek Bielawski I am referencing this article in the Hey Scripitng Guy blog article on October 29, 2011. I have come up with a really cool (and simple) solution. I talk about it in reference to my WMI helper module that I have been building ... but it applies to everything.