How to Write a PowerShell Script Module

 

A script module is essentially any valid PowerShell script saved in a .psm1 extension. This extension allows the PowerShell engine to use a number of rules and cmdlets on your file. Most of these capabilities are there to help you install your code on other systems, as well as manage scoping. You can also use a module manifest file, which can describe even more complex installations and solutions.

You create a script module by saving a valid PowerShell script to a .psm1 file, and then saving that file in a directory located where PowerShell can find it. In that folder you can also place any resources you need to run your script, as well as a manifest file that describes to PowerShell how your module works.

To create a basic PowerShell Module

  1. Take an existing PowerShell script, and save the script with a .psm1 extension.

    Saving a script with the .psm1 extension means that you can use the module cmdlets, such as Import-Module, on it. These cmdlets exist primarily so that you can easily import and export your code onto other user’s systems. (The alternate solution would be to load up your code on other systems and then dot-source it into active memory, which isn’t a particularly scalable solution.) For more information see the Module Cmdlets and Variables section in Windows PowerShell Modules Note that, by default, all functions in your script will be accessible to users who import your .psm1 file, but properties will not.

    An example PowerShell script, entitled Get-Calender, is available at the end of this topic.

    function Show-Calendar {
    param(
        [DateTime] $start = [DateTime]::Today,
        [DateTime] $end = $start,
        $firstDayOfWeek,
        [int[]] $highlightDay,
        [string[]] $highlightDate = [DateTime]::Today.ToString()
        ) 
    {
        #actual code for the function goes here see the end of the topic for the complete code sample
    }
    
  2. If you wish to control user access to certain functions or properties, call Export-ModuleMember at the end of your script.

    The example code at the bottom of the page has only one function, which by default would be exposed. However, it is recommended that you explicitly call out which functions you wish to expose, as described in the following code:

    function Show-Calendar {
          }
    export-modulemember -function Show-Calendar 
    

    You can also restrict what is imported using a module manifest. For more information, see Importing a PowerShell Module and How to Write a PowerShell Module Manifest.

  3. If you have modules that your own module needs to have loaded, you can use them with a call to Import-Module, at the top of your own module.

    Import-Module is a cmdlet that imports a targeted module onto a system. As such, it is also used at a later point in the procedure to install your own module as well. The sample code at the bottom of this page does not use any import modules. If it did, however, they would be listed at the top of the file, as described in the following code:

    Import-Module GenericModule
    
  4. If you want to describe your module to the PowerShell Help system, you can do so either with the standard help comments inside the file, or with an additional Help file.

    The code sample at the bottom of this topic includes the help information in the comments. If you so choose, you could also write expanded XML files that contain additional help content. For more information, see Writing Help for Windows PowerShell Modules.

  5. If you have additional modules, XML files, or other content you want to package up with your module, you can do so with a module manifest.

    A module manifest is a file that contains the names of other modules, directory layouts, versioning numbers, author data, and other pieces of information. PowerShell uses the module manifest file to organize and deploy your solution. However, due to the relatively simple nature of this example, a manifest file was not needed. For more information, see Module Manifests.

  6. To install and run your module, save the module to one of the appropriate PowerShell paths, and make a call to Import-Module.

    The paths where you can install your module are located in the $env:PSModulePath global variable. For example, a common path to save a module on a system would be %SystemRoot%\users\<user>\Documents\WindowsPowerShell\Modules\<moduleName>. Be sure to create a folder for your module to exist in, even if it is only a single .psm1 file. If you did not save your module to one of these paths, you would have to pass in the location of your module in the call to Import-Module. (Otherwise, PowerShell would not be able to find it.) Starting with PowerShell 3.0, if you have placed your module on one of the PowerShell module paths, you do not need to explicitly import it: simply having a user call your function will automatically load it. For more information on the module path, see Importing a PowerShell Module and PSModulePath Environment Variable.

  7. To remove a module from active service, make a call to Remove-Module.

    Note that Remove-Module removes your module from active memory – it does not actually delete it from where you saved the module files.

The following example is a simple script module that contains a single function named Show-Calendar. This function displays a visual representation of a calendar. In addition, the sample contains the PowerShell Help strings for the synopsis, description, parameter values, and example. Note that the last line of code indicates that the Show-Calendar function will be exported as a module member when the module is imported.

<# 
 .Synopsis
  Displays a visual representation of a calendar.

 .Description
  Displays a visual representation of a calendar. This function supports multiple months
  and lets you highlight specific date ranges or days.

 .Parameter Start
  The first month to display.

 .Parameter End
  The last month to display.

 .Parameter FirstDayOfWeek
  The day of the month on which the week begins.

 .Parameter HighlightDay
  Specific days (numbered) to highlight. Used for date ranges like (25..31).
  Date ranges are specified by the Windows PowerShell range syntax. These dates are
  enclosed in square brackets.

 .Parameter HighlightDate
  Specific days (named) to highlight. These dates are surrounded by asterisks.


 .Example
   # Show a default display of this month.
   Show-Calendar

 .Example
   # Display a date range.
   Show-Calendar -Start "March, 2010" -End "May, 2010"

 .Example
   # Highlight a range of days.
   Show-Calendar -HighlightDay (1..10 + 22) -HighlightDate "December 25, 2008"
#>
function Show-Calendar {
param(
    [DateTime] $start = [DateTime]::Today,
    [DateTime] $end = $start,
    $firstDayOfWeek,
    [int[]] $highlightDay,
    [string[]] $highlightDate = [DateTime]::Today.ToString()
    )

## Determine the first day of the start and end months.
$start = New-Object DateTime $start.Year,$start.Month,1
$end = New-Object DateTime $end.Year,$end.Month,1

## Convert the highlighted dates into real dates.
[DateTime[]] $highlightDate = [DateTime[]] $highlightDate

## Retrieve the DateTimeFormat information so that the
## calendar can be manipulated.
$dateTimeFormat  = (Get-Culture).DateTimeFormat
if($firstDayOfWeek)
{
    $dateTimeFormat.FirstDayOfWeek = $firstDayOfWeek
}

$currentDay = $start

## Process the requested months.
while($start -le $end)
{
    ## Return to an earlier point in the function if the first day of the month
    ## is in the middle of the week.
    while($currentDay.DayOfWeek -ne $dateTimeFormat.FirstDayOfWeek)
    {
        $currentDay = $currentDay.AddDays(-1)
    }

    ## Prepare to store information about this date range.
    $currentWeek = New-Object PsObject
    $dayNames = @()
    $weeks = @()

    ## Continue processing dates until the function reaches the end of the month. 
    ## The function continues until the week is completed with 
    ## days from the next month.
    while(($currentDay -lt $start.AddMonths(1)) -or
        ($currentDay.DayOfWeek -ne $dateTimeFormat.FirstDayOfWeek))
    {
        ## Determine the day names to use to label the columns.
        $dayName = "{0:ddd}" -f $currentDay
        if($dayNames -notcontains $dayName)
        {
            $dayNames += $dayName
        }

        ## Pad the day number for display, highlighting if necessary.
        $displayDay = " {0,2} " -f $currentDay.Day

        ## Determine whether to highlight a specific date.
        if($highlightDate)
        {
            $compareDate = New-Object DateTime $currentDay.Year,
                $currentDay.Month,$currentDay.Day
            if($highlightDate -contains $compareDate)
            {
                $displayDay = "*" + ("{0,2}" -f $currentDay.Day) + "*"
            }
        }

        ## Otherwise, highlight as part of a date range.
        if($highlightDay -and ($highlightDay[0] -eq $currentDay.Day))
        {
            $displayDay = "[" + ("{0,2}" -f $currentDay.Day) + "]"
            $null,$highlightDay = $highlightDay
        }

        ## Add the day of the week and the day of the month as note properties.
        $currentWeek | Add-Member NoteProperty $dayName $displayDay

        ## Move to the next day of the month.
        $currentDay = $currentDay.AddDays(1)

        ## If the function reaches the next week, store the current week
        ## in the week list and continue.
        if($currentDay.DayOfWeek -eq $dateTimeFormat.FirstDayOfWeek)
        {
            $weeks += $currentWeek
            $currentWeek = New-Object PsObject
        }
    }

    ## Format the weeks as a table.
    $calendar = $weeks | Format-Table $dayNames -auto | Out-String

    ## Add a centered header.
    $width = ($calendar.Split("'n") | Measure-Object -Max Length).Maximum
    $header = "{0:MMMM yyyy}" -f $start
    $padding = " " * (($width - $header.Length) / 2)
    $displayCalendar = " 'n" + $padding + $header + "'n " + $calendar
    $displayCalendar.TrimEnd()

    ## Move to the next month.
    $start = $start.AddMonths(1)

}
}
export-modulemember -function Show-Calendar
Show: