Несмотря на то, что в интернете вы можете найти очень много различных инструкций по поводу того, как создать свой собственный Powershell-модуль, даже официальная документация не дает полного понимания, как все-таки создать модуль правильно.
Я потратил несколько часов на изучение разных статей и, как обычно, пришел к своему подходу, который мне кажется достаточно удобным, гибким, и которым я хочу поделиться сейчас.
└─ScriptModuleRepositoryTemplate
│ ScriptModuleRepositoryTemplate.psd1
│ ScriptModuleRepositoryTemplate.psm1
│ ScriptModuleRepositoryTemplate.Tests.ps1
At the top of the Pester ScriptModuleRepositoryTemplate.Tests.ps1
file I have this statement to import the module:
using module './ScriptModuleRepositoryTemplate.psm1'
The module is imported and the Pester tests run fine in Pwsh 7, but on Windows PowerShell 5.1 it throws the error:
System.IO.FileNotFoundException: The specified module 'D:\a\PowerShell.ScriptModuleRepositoryTemplate\PowerShell.ScriptModuleRepositoryTemplate\ScriptModuleRepositoryTemplate.psm1' was not loaded because no valid module file was found in any module directory.
If I instead use Import-Module
like this:
Import-Module -Name "$PSScriptRoot/ScriptModuleRepositoryTemplate.psm1"
it works in both PowerShell 5.1 and 7, but of course Import-Module
does not import classes and enums, so I need to use using module
.
Both Pwsh and Windows PowerShell are using Pester v5.5.0, so I don’t think the issue is with Pester, but with Windows PowerShell itself.
I can reproduce the issue on my local machine by running Invoke-Pester .
, as well as in GitHub Actions (see this workflow run in the open source project).
Does anyone have any ideas on how I can get using module './PathToModuleFile.psm1'
to work properly?
#Requires -Version 3.0
<#
.SYNOPSIS
Compiling PowerShell Class Modules into C# DLLs.
.DESCRIPTION
This script compiles PowerShell Class Modules into C# Dynamic Link Libraries (DLLs).
The compiled file can be imported into PowerShell using the “using assembly .\Path.dll” directive,
or utilized with other .NET languages such as C# by adding the DLL to references and importing it using “using static ExampleName;”.
.PARAMETER Path
Specifies the path to the *.ps1 or *.psm1 script containing only a class and related namespaces.
.PARAMETER ScriptDefinition
Provides a string containing the class code.
.PARAMETER OutputAssembly
Specifies the path where the resulting *.cs or *.dll file will be saved.
.PARAMETER DebugMode
Enables the GetPowershell() method, as well as access to both ClassHandlers and other features.
.PARAMETER SkipUpdate
Doesn’t check for updates.
.EXAMPLE
Compile-Dll .\example.psm1 -o .\example.dll
.EXAMPLE
Compile-Dll .\example.psm1 -o .\example.cs
” “
string Script = Encoding.UTF8.GetString(Convert.FromBase64String(“cGFyYW0gKFtzdHJpbmddJE1ldGhvZCwgW29iamVjdFtdXSRBcmd1bWVudHMsIFtoYXNodGFibGVdJFByb3BlcnRpZXMpDQpDbGFzc0hhbmRsZXIgJE1ldGhvZCAkQXJndW1lbnRzICRQcm9wZXJ0aWVzICRmYWxzZQ==”));
Hashtable Properties = new Hashtable();’
“`n Properties.Add($(‘”‘ + $property.Keys + ‘”‘), $($property.Keys));`n”
“`n }`n`n”
string Script = Encoding.UTF8.GetString(Convert.FromBase64String(“cGFyYW0gKFtzdHJpbmddJE1ldGhvZCwgW29iamVjdFtdXSRBcmd1bWVudHMsIFtoYXNodGFibGVdJFByb3BlcnRpZXMpDQpDbGFzc0hhbmRsZXIgJE1ldGhvZCAkQXJndW1lbnRzICRQcm9wZXJ0aWVzICR0cnVl”));
Hashtable Properties = new Hashtable();’
“`n Properties.Add($(‘”‘ + $property.Keys + ‘”‘), $($property.Keys));`n”
“`n }`n”
” //Visible Class`n`n”
“`nSucesss, $((Get-Item $OutputAssembly).Name) has been compiled!”
What is a PowerShell Module?
about Modules – PowerShell
Explains how to install, import, and use PowerShell modules.
Put briefly, a PowerShell module — similar to a Python module — is a way to organize a collection of related code, variables, aliases, etc. in a contained workspace.
There are two types of PowerShell modules:
- Script modules (PowerShell scripts)
- Binary modules (compiled DLLs)
You can intermingle both scripts and compiled DLLs in a single module if the need arises, as there are performance benefits and other enhancements to running compiled code.
Module Loading Paths
If you inspect this PowerShell environment variable, you can see from which folders your PowerShell modules will be loaded.
$env:PSModulePath
PSModulePath environment variable
If you install a module from PowerShell Gallery, you can use the Scope
parameter to determine which folder in $env:PSModulePath
the module will be downloaded.
# Local User
# Install the module to C:\Users\user.name\Documents\WindowsPowerShell\Modules\ on Windows
# Install the module to /home/user.name/.local/share/powershell/Modules on Linux
Install-Module -Name <module-name> -Scope CurrentUser
# Global Module
# Install the module to C:\Program Files\WindowsPowerShell\Modules on Windows
# Install the module to /usr/local/share/powershell/Modules on Linux
Install-Module -Name <module-name>
Different ways to install modules from a network repository
Why mention this?
When to Create a Module
- Create a script for a distinct purpose or project
- Additional scripts continue to be added
- Adding helper scripts
- Or, extending the scope of the project
- The project scope continues to grow, we need a place to keep things organized
A PowerShell module — like a function — should be fine-tuned to a specific purpose. In other words, if you see your module becoming too monolithic and trying to be a one-size-fits-all tool, you might consider breaking it up into several individual modules.
Scaffolding the Module
Please note that this is the way that I create PowerShell modules, and should not be construed as the only way to create PS Modules.
Module Directory Structure
ModuleName
|___ModuleName.psd1
|___ModuleName.psm1
|___Private
| |___ps1
| |___Verb-Noun.ps1
| |___Verb-Noun.ps1
|
|___Public
|___ps1
|___Verb-Noun.ps1
|___Verb-Noun.ps1
ModuleName
— Name the module after the project or its intended purposeModuleName.psd1
— This is the module manifest, we’ll cover this laterModuleName.psm1
— This is your script module, we’ll cover this later as wellPrivate
— This is the directory to store your private functions and variables (eg. helper functions)ps1
— The PowerShell functions are stored here and are not directly referenced by the userVerb-Noun.ps1
— the script file containing the private PowerShell function.
Public
— This is directory to store your public functions and variablesps1
— The PowerShell function code is stored here and these are the cmdlets the user will be directly runningVerb-Noun.ps1
— the script file containing the public PowerShell function
Commands to Create the Module Structure
# Variables
$year = (Get-Date).Year
$moduleName = 'TestModule'
$templateString = 'Test Module'
$version = '1.0'
# Create the "TestModule" top-level directory
New-Item -ItemType Directory -Name $moduleName
# Create subdirectories
# TestModule
# |___ ...
# |___ ...
# |___Private
# | |___ps1
# |___ ...
New-Item -Path "$PWD\$moduleName\Private\ps1" -ItemType Directory -Force
# Create subdirectories
# TestModule
# |___ ...
# |___ ...
# |___ ...
# |___Public
# |___ps1
New-Item -Path "$PWD\$moduleName\Public\ps1" -ItemType Directory -Force
# Create the script module
# TestModule
# |___ ...
# |___ TestModule.psm1
New-Item -Path "$PWD\$moduleName\$moduleName.psm1" -ItemType File
# Create the module manifest
# TestModule
# |___TestModule.psd1
# |___ ...
$moduleManifestParameters = @{
Path = "$PWD\$moduleName\$moduleName.psd1"
Author = $templateString
CompanyName = $templateString
Copyright = "$year $templateString by Benjamin Heater"
ModuleVersion = $version
Description = $templateString
RootModule = "$moduleName.psm1"
}
New-ModuleManifest @moduleManifestParameters
Inspecting the Manifest
ModuleName
|___ModuleName.psd1
|___ ...
|___ ...
The module manifest in relation to the directory structure
Get-Content "$PWD\TestModule\TestModule.psd1"`
Output the contents of the moudle manifest
Example Module Manifest
#
# Module manifest for module 'TestModule'
#
# Generated by: Test Module
#
# Generated on: 8/17/2023
#
@{
# Script module or binary module file associated with this manifest.
RootModule = 'TestModule.psm1'
# Version number of this module.
ModuleVersion = '1.0'
# Supported PSEditions
# CompatiblePSEditions = @()
# ID used to uniquely identify this module
GUID = '147cfba3-3c6d-4260-aeca-f99aa900cc0c'
# Author of this module
Author = 'Test Module'
# Company or vendor of this module
CompanyName = 'Test Module'
# Copyright statement for this module
Copyright = '2023 Test Module by Benjamin Heater'
# Description of the functionality provided by this module
Description = 'Test Module'
# 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. This prerequisite is valid for the PowerShell Desktop edition only.
# DotNetFrameworkVersion = ''
# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
# 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 = @()
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
FunctionsToExport = '*'
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
CmdletsToExport = '*'
# Variables to export from this module
VariablesToExport = '*'
# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
AliasesToExport = '*'
# DSC resources to export from this module
# DscResourcesToExport = @()
# 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. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
PrivateData = @{
PSData = @{
# Tags applied to this module. These help with module discovery in online galleries.
# Tags = @()
# A URL to the license for this module.
# LicenseUri = ''
# A URL to the main website for this project.
# ProjectUri = ''
# A URL to an icon representing this module.
# IconUri = ''
# ReleaseNotes of this module
# ReleaseNotes = ''
} # End of PSData hashtable
} # End of PrivateData hashtable
# HelpInfo URI of this module
# HelpInfoURI = ''
# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
# DefaultCommandPrefix = ''
}
When we ran the New-ModuleManifest
command before, PowerShell added our inputs to a .psd1
file with the above template.
The module manifest is responsible for declaring some important things about our module, including:
- The
RootModule
script (eg.TestModule.psm1
) - The
GUID
to uniquely identify the module FunctionsToExport
for function names that should be executable by the userCmdletsToExport
for compiled cmdlets that should be executable by the userVariablesToExport
for any module-specific variablesAliasesToExport
for any aliases defined in your functions or cmdlets- And, many more configurations
about Module Manifests – PowerShell
Describes the settings and practices for writing module manifest files.
Inspecting the Script Module
ModuleName
|___ ...
|___ModuleName.psm1
|___ ...
The script module in relation to the directory structure
Get-Content "$PWD\TestModule\TestModule.psm1"
Output the contents of the script module
Script Module I’ve Created for All My Modules
$directorySeparator = [System.IO.Path]::DirectorySeparatorChar
$moduleName = $PSScriptRoot.Split($directorySeparator)[-1]
$moduleManifest = $PSScriptRoot + $directorySeparator + $moduleName + '.psd1'
$publicFunctionsPath = $PSScriptRoot + $directorySeparator + 'Public' + $directorySeparator + 'ps1'
$privateFunctionsPath = $PSScriptRoot + $directorySeparator + 'Private' + $directorySeparator + 'ps1'
$currentManifest = Test-ModuleManifest $moduleManifest
$aliases = @()
$publicFunctions = Get-ChildItem -Path $publicFunctionsPath | Where-Object {$_.Extension -eq '.ps1'}
$privateFunctions = Get-ChildItem -Path $privateFunctionsPath | Where-Object {$_.Extension -eq '.ps1'}
$publicFunctions | ForEach-Object { . $_.FullName }
$privateFunctions | ForEach-Object { . $_.FullName }
$publicFunctions | ForEach-Object { # Export all of the public functions from this module
# The command has already been sourced in above. Query any defined aliases.
$alias = Get-Alias -Definition $_.BaseName -ErrorAction SilentlyContinue
if ($alias) {
$aliases += $alias
Export-ModuleMember -Function $_.BaseName -Alias $alias
}
else {
Export-ModuleMember -Function $_.BaseName
}
}
$functionsAdded = $publicFunctions | Where-Object {$_.BaseName -notin $currentManifest.ExportedFunctions.Keys}
$functionsRemoved = $currentManifest.ExportedFunctions.Keys | Where-Object {$_ -notin $publicFunctions.BaseName}
$aliasesAdded = $aliases | Where-Object {$_ -notin $currentManifest.ExportedAliases.Keys}
$aliasesRemoved = $currentManifest.ExportedAliases.Keys | Where-Object {$_ -notin $aliases}
if ($functionsAdded -or $functionsRemoved -or $aliasesAdded -or $aliasesRemoved) {
try {
$updateModuleManifestParams = @{}
$updateModuleManifestParams.Add('Path', $moduleManifest)
$updateModuleManifestParams.Add('ErrorAction', 'Stop')
if ($aliases.Count -gt 0) { $updateModuleManifestParams.Add('AliasesToExport', $aliases) }
if ($publicFunctions.Count -gt 0) { $updateModuleManifestParams.Add('FunctionsToExport', $publicFunctions.BaseName) }
Update-ModuleManifest @updateModuleManifestParams
}
catch {
$_ | Write-Error
}
}
I use the script module as an initialization script for when the module is first imported. Looking at the code from top to bottom, I’ll briefly summarize what the script module is doing.
- Dynamically determine the module name using
$PSScriptRoot
- Assume the
.psd1
file shares the same name as the parent directory
(the.psd1
is invoking this.psm1
script module) - Pull the current module manifest using
Test-ModuleManifest
, in order to compare and update things as necessary - Discover all
.ps1
files in private and public directories and source them in - Loop over each public function and determine if any aliases are defined and export them as module members
- Do some comparisons against the current module manifest to see if any functions or aliases have been added or removed, and if so, update the module manifest
You could add additional logic to the .psm1
script module if there are things you’d like your module to do when it’s first imported.
Adding Code to the Module
ModuleName
|___ ...
|___ ...
|___Private <--- May not be referenced by user
| |___ps1 <--- Script files
| |___Verb-Noun.ps1 <--- Private function 1
| |___Verb-Noun.ps1 <--- Private function 2
| |___etc.
|
|___Public <--- May be referenced by user
|___ps1 <--- Script files
|___Verb-Noun.ps1 <--- Public function 1
|___Verb-Noun.ps1 <--- Public function 2
|___etc.
The PowerShell code in relation to the directory structure
The examples here are going to be a bit contrived. The intent — however — is to keep this blog post as concise as possible while still providing clear examples of functionality.
Adding a Private Function
ModuleName
|___ ...
|___ ...
|___Private
| |___ps1
| |___Confirm-CoffeeTime.ps1
| |___ ...
|
|___ ...
Private function in relation to the directory structure
function Confirm-CoffeeTime {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[ValidateSet('Regular', 'Decaf')]
[String]$CoffeeType
)
begin {
$currentDateTime = Get-Date
$regularCoffeeHours = 4..20
$decafCoffeeHours = 0..3 + 21..23
}
process {
if ($CoffeeType -eq 'Regular') {
if ($currentDateTime.Hour -in $regularCoffeeHours) {
$result = $true
}
else {
$result = $false
}
}
else {
if ($currentDateTime.Hour -in $decafCoffeeHours) {
$result = $true
}
else {
$result = $false
}
}
}
end {
if ($result -eq $true) {
return "Enjoy your cup of $CoffeeType coffee!"
}
else {
throw "You may not drink $CoffeeType right now!"
}
}
}
Note the function name matches the file name ‘Confirm-CoffeeTime’
Adding a Public Function
ModuleName
|___ ...
|___ ...
|___ ...
|
|___Public
|___ps1
|___Get-Coffee.ps1
|___ ...
Public function in relation to the directory structure
function Get-Coffee {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, Position = 0)]
[ValidateSet('Regular', 'Decaf')]
[String]$CoffeeType
)
begin {}
process {
try {
# Call the private function to confirm coffee time
$confirmation = Confirm-CoffeeTime -CoffeeType $CoffeeType
Write-Host $confirmation -ForegroundColor Green
}
catch {
throw $_
}
}
end {}
}
Note the function name matches the file name ‘Get-Coffee’
Importing and Using the Module
What we should expect to happen:
- Import the module
- Run
Get-Command -Module <module_name>
- Observe that we have only one user-executable command
Import-Module .\TestModule
Importing our test module
Get-Command -Module TestModule
See which commands are provided by the module


Perfect! Our public Get-Coffee
function is performing as expected — calling the private Confirm-CoffeeTime
function and returning stdout
or stderr
.
Applying this to Real Projects
As mentioned before, the examples above are contrived, and not great at demonstrating how a PowerShell module could be used for real projects. Some examples of how you might create a PowerShell module:
- API clients
- CTF tools
- Hacking tools
- Forensic tools
- Diagnostic tools
- Etc.
Don’t create more work for yourself!
Creating a duplicate PowerShell module as a learning experience is absolutely fine. However, if there’s a module on GitHub or the PS Gallery that meets your needs, use that and consider contributing to the source code if it’s open source.
If you see an existing module, but aren’t satisfied with it for any reason, that would absolutely be a perfect opportunity to create your own solution.
Some Modules I’ve Written
GitHub – 0xBEN/PSProxmox: PowerShell module for interfacing with Proxmox API
PowerShell module for interfacing with Proxmox API – GitHub – 0xBEN/PSProxmox: PowerShell module for interfacing with Proxmox API
Good reference for my usage of public/private functions
GitHub – 0xBEN/PSToolbox: PowerShell module containing a set of generally useful tools.
PowerShell module containing a set of generally useful tools. – GitHub – 0xBEN/PSToolbox: PowerShell module containing a set of generally useful tools.
No usage of private functions, just bundling tools
Wrapping Up
I want to emphasize that this post is a demonstration of the way that I create PowerShell modules. It is not the only way to create PowerShell modules.
That said, I do hope that I gave you some ideas on how to get started with creating your own modules, and how you can keep your related code organized.
Установите необходимые утилиты
Кроме самого Powershell, Вам понадобится утилита командной строки nuget
. Она будет нужна, чтобы паковать модуль в nuget-пакет и распространять модуль между другими компьютерами\пользователями.
Упаковка модуля
Попробуем запустить local-build.ps1 и посмотреть, что получится.
Отлично! Nuget-пакет готов и лежит в папке My.OwnModule.Powershell\_bin\My.OwnModule.Powershell.0.0.3.nupkg. Попробуем его установить и воспользоваться.
Создайте необходимые файлы
Также нужно создать файлы, которые собственно и превращают набор Powershell-сриптов в модуль.
D:.
│ LICENSE
│ PSScriptAnalyzerSettings.psd1
│ README.md
│
└───My.OwnModule.Powershell
│ local-build.ps1
│ local-install.ps1
│ My.OwnModule.Powershell.nuspec
│ My.OwnModule.Powershell.psd1
│ My.OwnModule.Powershell.psm1
│
├───images
│ icon.png
│
├───private
│ _stub.ps1
│
├───public
│ └───common
│ Read-MyOwnJsonFile.ps1
│
└───_bin
Нас интересуют файлы, которые лежат в корневой папке модуля, в private и public. Остальные – это служебные файлы git-репозитория. Ниже привожу содержимое каждого файла с комментариями.
Придумайте префикс для названия командлетов\функций
Если вы разрабатываете несколько разных модулей (например, для разных компаний или команд), иногда в модулях может быть одна и та же функция. И если вы начнете сами использовать эти модули одновременно, при их импорте начнутся конфликты имен. Поэтому имеет смысл использовать для каждого модуля свой уникальный префикс.
В нашем примере я буду использовать MyOwn
. Например стандартное имя New-Item превратится в New-MyOwnItem.
GitHub репозиторий
Вы можете найти все эти файлы в моем репозитории: https://github.com/vicioussn/My.OwnModule.Powershell.
Установка Powershell-модуля из nuget-пакета.
Прекрасно, модуль готов к работе!
Там Verbose говорит, что версия 0.0.5 уже установлена и так далее. Это потому что я попытался понизить версию, вместо того, чтобы повысить. Не обращайте внимание.
Создайте дерево каталогов
Кроме корневой папки, которая будет называться так же, как и сам модуль, понадобится начальный набор каталогов.
D:.
└───My.OwnModule.Powershell
├───images
├───private
├───public
│ └───common
└───_bin
- images. Тут будет лежать иконка для пакета, которая в теории будет видна в каком-нибудь репозитории типа Artifactory.
- private. Тут будут лежать скрипты, которые должны быть доступны только для служебных нужд. Например, чтобы прочитать файл с диска с обработкой ошибок.
- public. Тут будут лежать скрипты, в которых будут доступны непосредственно пользователям модуля.
- public -> common. Эта папка нужна исключительно для структурирования и группировки файлов. Все внутри Public будет читаться рекурсивно.
- _bin. Тут будут лежать пакеты с модулем, когда утилита nuget скомпилирует пакет.
Заполняем служебные файлы для Powershell-модуля
My.OwnModule.Powershell.nuspec
Этот файл нужен, чтобы nuget
генерировал пакет.
<?xml version="1.0" encoding="utf-8"?>
<package >
<metadata>
<id>My.OwnModule.Powershell</id>
<version>0.0.2</version>
<authors>Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. </authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<icon>images\icon.png</icon>
<description>
There is no any description available at this time. Sorry for that.
</description>
<releaseNotes>Initial release.</releaseNotes>
<tags>PSEdition_Core PSEdition_Desktop Linux Mac PSModule PSIncludes_Cmdlet</tags>
</metadata>
</package>
My.OwnModule.Powershell.psd1
Этот файл – манифест, описание модуля с точки зрения самого Powershell. Тут хранятся различные сведения об авторе, о функциях, которые модуль будет предосталять и т.п.
Обратите внимание на строку 11 и параметр FunctionsToExport. Это функции, которые будут доступны пользователям вашего модуля. Поэтому, при добавлении новых функций в модуль, необходимо помнить и добавлять их сюда.
@{
RootModule = "My.OwnModule.Powershell.psm1"
ModuleVersion = "0.0.2"
CompatiblePSEditions = "Desktop", "Core"
GUID = "8f37ba7b-be77-4b6e-8ceb-d30c0c328674"
Author = "Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. "
PowerShellVersion = "7.0"
ScriptsToProcess = @();
FunctionsToExport = @(
"Read-MyOwnJsonFile"
);
CmdletsToExport = "*"
VariablesToExport = "*"
AliasesToExport = "*"
}
My.OwnModule.Powershell.psm1
Этот файл – скрипт, который выполняется, когда вы запускаете (или сама система запускает) Import-Module. Это самый главный файл в модуле, вообще говоря, это и есть сам модуль.
#Requires -Version 7.0
$public = @( Get-ChildItem -Path $PSScriptRoot\public\*.ps1 -Recurse; );
$private = @( Get-ChildItem -Path $PSScriptRoot\private\*.ps1 -Recurse; );
# Dot source the files
foreach ($import in @($public + $private))
{
try
{
Write-Verbose "Importing '$($import.FullName)'.";
. $import.FullName;
}
catch
{
Write-Error -Message "Failed to import function $($import.fullname): $_";
}
}
Export-ModuleMember -Function $public.Basename;
_stub.ps1
Я положил этот файл-заглушку, чтобы скрипт из psm1-файла смог нормально прочитать папку без возможных проблем. А вообще он полностью пустой.
Read-MyOwnJsonFile.ps1
Это файл командлета, который мы будем предоставлять нашим модулем. Обратите внимание, как он называется – Read-MyOwnJsonFile, всмысле что используется префикс для имен командлетов модуля.
local-build.ps1
Этот файл не является непосредственной частью Powershell-модуля. Это скрипт, который я использую для упаковки модуля в nuget-пакет, чтобы потом его можно было положить куда-нибудь в архив. Также этот скрипт в каждый запуск изменяет версию модуля на +1.
# Config
$moduleName = "My.OwnModule.Powershell";
# Clear old packages
# Get-ChildItem .\$moduleName\_bin | Remove-Item -Force -Verbose;
#region Get current package version and increment Patch
$version = Select-String -Path .\$moduleName\$moduleName.nuspec -Pattern "<version>(\d)\.(\d)\.(\d{1,})</version>";
[int]$majorVersion = $version | Select-Object @{name="version"; expression={$_.Matches.Groups[1].Value}} | select-object -ExpandProperty version;
[int]$minorVersion = $version | Select-Object @{name="version"; expression={$_.Matches.Groups[2].Value}} | select-object -ExpandProperty version;
[int]$patchVersion = $version | Select-Object @{name="version"; expression={$_.Matches.Groups[3].Value}} | select-object -ExpandProperty version;
$patchVersion++;
$nuspecVersionString = "<version>$majorVersion.$minorVersion.$patchVersion</version>";
$psd1VersionString = "ModuleVersion = `"$majorVersion.$minorVersion.$patchVersion`"";
#endregion /Get current package version and increment Patch
#region Update package version
$a = Get-Content -Path ".\$moduleName\$moduleName.nuspec" | %{$_ -replace "<version>(\d)\.(\d)\.(\d{1,})<\/version>", $nuspecVersionString };
$a | Out-File ".\$moduleName\$moduleName.nuspec" -Verbose;
$a = Get-Content -Path ".\$moduleName\$moduleName.psd1" | %{$_ -replace "ModuleVersion = `"\d\.\d.\d{1,}`"", $psd1VersionString };
$a | Out-File ".\$moduleName\$moduleName.psd1" -Verbose;
#endregion /Update package version
nuget pack ".\$moduleName\$moduleName.nuspec" -OutputDirectory .\$moduleName\_bin -Properties NoWarn=NU5111,NU5110,NU5100
$package = Get-ChildItem .\$moduleName\_bin\*.nupkg | Sort-Object -Property CreationTime | Select-Object -Last 1;
$package
local-install.ps1
Соответственно, этот кастомный скрипт – для установки модуля.
$moduleName = "My.OwnModule.Powershell";
$repoName = "My.OwnModule.Powershell.Repository";
$repoPath = "D:\Private\My.OwnModule.Powershell\My.OwnModule.Powershell\_bin\";
Register-PSRepository -Name $repoName -SourceLocation $repoPath -InstallationPolicy Trusted -ScriptSourceLocation $repoPath;
Install-Module -Name $moduleName -Repository $repoName -Verbose;
Get-Module $moduleName -ListAvailable;
Придумайте название
Это важно :-). Потом с этим названием вы будете долго жить, поэтому придумайте то, что вам потом будет приятно видеть каждый день =).
В моем случае я решил придерживаться следующего подхода: <код_заказчика\название заказчика>.<код_проекта>.Powershell
.
Для целей этого гайда давайте делать модуль My.OwnModule.Powershell
.