ScriptRunner Blog
Building Your First PowerShell Module
Table of Contents
PowerShell has a variety of powerful cmdlets built-in to the core language. But native functions only take you so far, and in most projects you will need more functionality. The PowerShell Gallery offers a wide range of downloadable third-party modules, but there’s also the option to create custom PowerShell modules. This article will guide you through the process of creating your first PowerShell module.
What is a PowerShell Module?
- psd1 File – PowerShell definition file
- psm1 File – PowerShell module loading file
- Functions
There are two types of modules, script modules, and binary modules. In this example, we are building a script module. Consisting of traditional PowerShell functions, a script function is easy to build. A binary module is a .NET framework compiled assembly. Written in .NET, cmdlets are not as approachable as script modules. First, we need to define the purpose of the PowerShell module. Not all aspects of a module need mapping before starting. Creating a clear plan makes defining necessary functions much easier.
Defining the Module’s Purpose
Structure of a Module
Public
- func_New-TeamsMessage.ps1
- func_Invoke-PowerAutomateFlow.ps1
- func_Get-SharePointMember.ps1
Private – Empty
- UtilityModule.psd1
- UtilityModule.psm1
Creating the Module
$Params = @{ "Path" = 'D:\WorkingFolder\Articles\UtilityModule.psd1' "Author" = 'Fake Author' "CompanyName" = 'Fake Company' "RootModule" = 'UtilityModule.psm1' "CompatiblePSEditions" = @('Desktop','Core') "FunctionsToExport" = @('Get-SharePointMember','Invoke-PowerAutomateFlow','New-TeamsMessage') "CmdletsToExport" = @() "VariablesToExport" = '' "AliasesToExport" = @() "Description" = 'Utility Module' } New-ModuleManifest @Params
We export functions using Export-ModuleMember, but performance best practices dictate the use of empty arrays in the module definition. For unknown reasons, an empty array does not export correctly under VariablesToExport, but an empty string does output an empty array.
Running the New-ModuleManifest command creates a UtilityModule.psd1 file. Many comment blocks pertain to extra configurations. Below, we have removed all extra comments to show the configured parameters.
UtilityModule.psd1
@{ RootModule = 'UtilityModule.psm1' ModuleVersion = '0.0.1' CompatiblePSEditions = 'Desktop', 'Core' GUID = 'dc18a919-f4bf-4da2-8c76-24b68fa33ef0' Author = 'Fake Author' CompanyName = 'Fake Company' Copyright = '(c) Fake Author. All rights reserved.' Description = 'UtilityModule' FunctionsToExport = 'Get-SharePointMember','Invoke-PowerAutomateFlow','New-TeamsMessage' CmdletsToExport = @() VariablesToExport = @() AliasesToExport = @() PrivateData = @{ PSData = @{} } }
If all module functions are dot-sourced, then why do we need to list the functions to export? There are two primary reasons for explicitly listing exported functions. Private functions should not be made public, and exported functions will auto-complete on the command line even if the module is not yet loaded.
Next we need to create the module loading file. Again, there are several ways to approach this, but the below method has been reliable. The first section will only retrieve func_ prefixed files and dot-source them. The second will export all functions in the Public folder.
UtilityModule.psm1
Get-ChildItem (Split-Path $script:MyInvocation.MyCommand.Path) -Filter 'func_*.ps1' -Recurse | ForEach-Object { . $_.FullName } Get-ChildItem "$(Split-Path $script:MyInvocation.MyCommand.Path)\Public\*" -Filter 'func_*.ps1' -Recurse | ForEach-Object { Export-ModuleMember -Function ($_.BaseName -Split "_")[1] }
Creating our Functions
New-TeamsMessage
This function will take in a message string and team ID string and create the REST call to create a Teams message. This wraps the necessary JSON formatting into a simplified API call.
Function New-TeamsMessage { [CmdletBinding()] Param( [Parameter(Position = 0, Mandatory = $true)][String]$Message, [Parameter(Position = 1, Mandatory = $true)][String]$Title, [Parameter(Position = 2, Mandatory = $true)][String]$URI ) Process { $Params = @{ "URI" = $URI "Method" = 'POST' "Body" = [PSCustomObject][Ordered]@{ "@type" = 'MessageCard' "@context" = 'http://schema.org/extension' "summary" = $Title "title" = $Title "text" = ($Message | Out-String) } "ContentType" = 'application/json' } Invoke-RestMethod @Params | Out-Null } }
Invoke-PowerAutomateFlow
Next, we are defining a Power Automate Flow function. There is a simple way to do this by using the HTTP trigger, “When a HTTP request is received”. This uses the given URI displayed upon saving to call the trigger.
Function Invoke-PowerAutomateFlow { [CmdletBinding()] Param( [Parameter(Position = 0, Mandatory = $true)][String]$URI ) Process { $Params = @{ "URI" = $URI "ContentType" = 'application/json' "Method" = 'GET' } Invoke-WebRequest @Params } }
Get-SharePointMember
Get-SharePointMember supports our final workflow need of retrieving a SharePoint member list. This function gets all SharePoint site members for a given site and displays the results. Passing in a group will filter the results.
Function Get-SharePointMember { [CmdletBinding()] Param( [Parameter(Position = 0, Mandatory = $true)][String]$URI, [Parameter(Position = 1)][String]$Group ) Process { $Params = @{ "URI" = $URI } If ($Group) { Get-SPOUser @Params | Where-Object -Contains $Group } Else { Get-SPOUser @Params } } }
Importing and Testing the Module
Import the module using the Import-Module command and verify there are no errors. Next, we will use Get-Command to see all exported members.
Import-Module -Name 'UtilityModule' Get-Command -Module 'UtilityModule'
To test, run the various commands and make sure that they are functioning as you would expect. In the script below we are running the functions and utilizing their output.
# Get SharePoint Members $Members = Get-SharePointMember # Invoke Invoke-PowerAutomateFlow -URI 'https://...' New-TeamsMessage -Message $Groups -Title 'Group Members' -URI 'https://...'
Conclusion
Related posts
14 min read
How to Use Winget and PowerShell for Efficient App Deployment
Dec 19, 2024 by Jeffery Hicks
Boost IT efficiency with Winget and PowerShell! Learn how to automate app installations, updates, and management...
17 min read
How to Leverage .NET in PowerShell for event-driven scripting
Dec 17, 2024 by Sonny Jamwal
Extend PowerShell with .NET for powerful event automation. Learn how to monitor and handle system events like a pro!...
13 min read
Master PowerShell WMI: Automate system event monitoring effortlessly
Dec 17, 2024 by Sonny Jamwal
Optimize event monitoring with PowerShell WMI! Learn how to automate system tasks and streamline event management...
About the author:
Adam is a 20+ year veteran of IT and experienced online business professional. He’s an entrepreneur, IT influencer, Microsoft MVP, blogger, trainer, author and content marketing writer for multiple technology companies.
Latest posts:
- How to Use Winget and PowerShell for Efficient App Deployment
- How to Leverage .NET in PowerShell for event-driven scripting
- Master PowerShell WMI: Automate system event monitoring effortlessly
- Festive PowerShell: Holiday Scripting with ScriptRunner
- How to Leverage IT Automation for Maximum Efficiency