# PowerShell 7.x - قسمت هشتم - ماژول‌ها

توسط ماژول‌ها میتوانیم یک مجموعه از دستورات را گروه‌بندی کنیم و تحت عنوان یک پکیج ارائه دهیم که برای دیگران نیز قابل استفاده باشند. برای ایجاد یک ماژول کافی است اسکریپت‌های خود را درون یک فایل با پسون

- Published: 2023-02-04
- Language: fa
- Tags: DNTips
- Canonical: https://sirwan.info/blog/fa/dntips-3447

---

> این نوشته نخستین بار در [دات‌نت تیپس](https://www.dntips.ir/post/3447) منتشر شده است.

<div class="postBody">توسط ماژول‌ها میتوانیم یک مجموعه از دستورات را گروه‌بندی کنیم و تحت عنوان یک پکیج ارائه دهیم که برای دیگران نیز قابل استفاده باشند. برای ایجاد یک ماژول کافی است اسکریپت‌های خود را درون یک فایل با پسوند psm1 قرار دهیم؛ به این فایل اصطلاحاً root module گفته میشود. در واقع میتوان گفت ماژول‌ها یک روش مناسب برای به اشتراک‌گذاری اسکریپت‌ها میباشند. تا اینجا با کمک پروفایل‌ها توانستیم امکان استفاده مجدد از توابع و اسکریپت‌ها را داشته باشیم؛ ماژول‌ها نیز یک روش دیگر برای بارگذاری اسکریپت‌ها درون شل هستند. زمانیکه شل را باز میکنیم PowerShell به صورت خودکار یکسری مسیر را برای بارگذاری ماژول‌ها اسکن میکند. توسط متغیر env:PSModulePath$ میتوانیم لیست این مسیرها را ببینیم:   <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">PS /&gt; $env:PSModulePath -Split ":"&#10;&#10;/Users/sirwanafifi/.local/share/powershell/Modules&#10;/usr/local/share/powershell/Modules&#10;/usr/local/microsoft/powershell/7/Modules</pre>
 </div>
همانطور که عنوان شد برای ایجاد یک ماژول کافی است اسکریپت‌های خود را داخل یک فایل با پسوند psm1 ذخیره کنیم. به عنوان مثال میتوانیم تابع Get-PingReply را درون یک فایل با نام PingModule.psm1 ذخیره و سپس توسط دستور Import-Module ماژول را ایمپورت کنیم:   <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">PS /&gt; Import-Module ./PingModule.psm1</pre>
 </div>
سپس توسط دستور Get-Module PingModule میتوانیم جزئیات ماژول ایمپورت شده را مشاهده نمائیم:  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">PS /&gt; Get-Module PingModule&#10;&#10;ModuleType Version    PreRelease Name                                ExportedCommands&#10;---------- -------    ---------- ----                                ----------------&#10;Script     0.0                   PingModule                          Get-PingReply</pre>
 </div>
به صورت پیش‌فرض تمام توابع درون اسکریپت export خواهند شد. اگر ExportedCommands خالی باشد به این معنا است که ماژول به درستی ایمپورت نشده‌است. به عنوان مثال اگر سعی کنید فایل قبل را با پسوند ps1 به عنوان ماژول ایمپورت کنید. خطایی هنگام ایمپورت کردن مشاهده نخواهید کرد و قسمت ExportedCommands خالی خواهد بود. در این‌حالت نیز امکان استفاده از تابع درون اسکریپت را خواهیم داشت؛ اما هیچ تضمینی نیست که به صورت یک ماژول به درستی عمل کند. بنابراین بهتر است ماژول‌هایی که ایجاد میکنیم حتماً پسوند psm1 داشته باشند.<br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">PS /&gt; Import-Module ./PingModule.ps1&#10;PS /&gt; Get-Module PingModule&#10;&#10;ModuleType Version    PreRelease Name                                ExportedCommands&#10;---------- -------    ---------- ----                                ----------------&#10;Script     0.0                   PingModule</pre>
 </div>
ممکن است بخواهیم یکسری توابع را به صورت private تعریف کنیم و فقط تعداد محدودی از توابع به صورت public باشند. در این حالت میتوانیم درون فایل psm1 با کمک دستور Export-ModuleMember اینکار را انجام دهیم:  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">Function Get-PingReply {&#10;    // as before&#10;}&#10;&#10;Function Get-PrivateFunction {&#10;    Write-Debug 'This is a private function'&#10;}&#10;&#10;Export-ModuleMember -Function @(&#10;    'Get-PingReply'&#10;)</pre>
 </div> <div> <b>اضافه کردن متادیتا به ماژول‌ها</b> </div> <div>برای هر ماژول میتوانیم با کمک module manifest یکسری متادیتا به ماژول اضافه کنیم. این متادیتا درون یک فایل با پسوند psd1 که محتویات آن در واقع یک Hashtable است ذخیره میشود. برای ایجاد فایل manifest میتوانیم از دستور New-ModuleManifest استفاده کنیم. به عنوان مثال برای ایجاد فایل manifest برای ماژول PingModule.psm1 اینگونه عمل خواهیم کرد:   </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">$moduleSettings = @{&#10;    Path                 = './PingModule.psd1'&#10;    Description          = 'A module to ping a remote system'&#10;    RootModule           = 'PingModule.psm1'&#10;    ModuleVersion        = '1.0.0'&#10;    FunctionsToExport    = @(&#10;        'Get-PingReply'&#10;    )&#10;    PowerShellVersion    = '5.1'&#10;    CompatiblePSEditions = @(&#10;        'Core'&#10;        'Desktop'&#10;    )&#10;}&#10;New-ModuleManifest @moduleSettings</pre>
 </div>
تنها پراپرتی موردنیاز برای ایجاد module manifest پراپرتی Path میباشد. این پراپرتی به فایلی که متادیتا قرار است درون آن ایجاد شود اشاره دارد. همانطور که مشاهده میکنید یکسری پراپرتی دیگر نیز اضافه کرده‌ایم و توسط splatted hash table (با کمک @) به دستور New-ModuleManifest ارسال کرده‌ایم. به این معنا که کلیدها و مقادیر درون hash table به جای اینکه یکجا به عنوان یک آبجکت به دستور موردنظر ارسال شوند، به صورت جدا پاس داده شده‌اند. اما اگر از $ استفاده میکردیم:  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">$moduleSettings = @{&#10;    Author = 'John Doe'&#10;    Description = 'This is a sample module'&#10;}&#10;&#10;New-ModuleManifest $moduleSettings</pre>
 </div>
با خطای زیر مواجه میشدیم:  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">New-ModuleManifest : A parameter cannot be found that matches parameter name 'Author'.</pre>
 </div>
در نهایت فایل manifest در مسیر تعیین شده ایجاد خواهد شد:  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">#&#10;# Module manifest for module 'PingModule'&#10;#&#10;# Generated by: sirwanafifi&#10;#&#10;# Generated on: 01/01/2023&#10;#&#10;&#10;@{&#10;&#10;    # Script module or binary module file associated with this manifest.&#10;    RootModule           = './PingModule.psm1'&#10;&#10;    # Version number of this module.&#10;    ModuleVersion        = '1.0.0'&#10;&#10;    # Supported PSEditions&#10;    CompatiblePSEditions = 'Core', 'Desktop'&#10;&#10;    # ID used to uniquely identify this module&#10;    GUID                 = '3f8561fc-c004-4c8e-b2fc-4a4191504131'&#10;&#10;    # Author of this module&#10;    Author               = 'sirwanafifi'&#10;&#10;    # Company or vendor of this module&#10;    CompanyName          = 'Unknown'&#10;&#10;    # Copyright statement for this module&#10;    Copyright            = '(c) sirwanafifi. All rights reserved.'&#10;&#10;    # Description of the functionality provided by this module&#10;    Description          = 'A module to ping a remote system'&#10;&#10;    # Minimum version of the PowerShell engine required by this module&#10;    PowerShellVersion    = '5.1'&#10;&#10;    # Name of the PowerShell host required by this module&#10;    # PowerShellHostName = ''&#10;&#10;    # Minimum version of the PowerShell host required by this module&#10;    # PowerShellHostVersion = ''&#10;&#10;    # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.&#10;    # DotNetFrameworkVersion = ''&#10;&#10;    # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.&#10;    # ClrVersion = ''&#10;&#10;    # Processor architecture (None, X86, Amd64) required by this module&#10;    # ProcessorArchitecture = ''&#10;&#10;    # Modules that must be imported into the global environment prior to importing this module&#10;    # RequiredModules = @()&#10;&#10;    # Assemblies that must be loaded prior to importing this module&#10;    # RequiredAssemblies = @()&#10;&#10;    # Script files (.ps1) that are run in the caller's environment prior to importing this module.&#10;    # ScriptsToProcess = @()&#10;&#10;    # Type files (.ps1xml) to be loaded when importing this module&#10;    # TypesToProcess = @()&#10;&#10;    # Format files (.ps1xml) to be loaded when importing this module&#10;    # FormatsToProcess = @()&#10;&#10;    # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess&#10;    # NestedModules = @()&#10;&#10;    # 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.&#10;    FunctionsToExport    = 'Get-PingReply'&#10;&#10;    # 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.&#10;    CmdletsToExport      = '*'&#10;&#10;    # Variables to export from this module&#10;    VariablesToExport    = '*'&#10;&#10;    # 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.&#10;    AliasesToExport      = '*'&#10;&#10;    # DSC resources to export from this module&#10;    # DscResourcesToExport = @()&#10;&#10;    # List of all modules packaged with this module&#10;    # ModuleList = @()&#10;&#10;    # List of all files packaged with this module&#10;    # FileList = @()&#10;&#10;    # 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.&#10;    PrivateData          = @{&#10;&#10;        PSData = @{&#10;&#10;            # Tags applied to this module. These help with module discovery in online galleries.&#10;            # Tags = @()&#10;&#10;            # A URL to the license for this module.&#10;            # LicenseUri = ''&#10;&#10;            # A URL to the main website for this project.&#10;            # ProjectUri = ''&#10;&#10;            # A URL to an icon representing this module.&#10;            # IconUri = ''&#10;&#10;            # ReleaseNotes of this module&#10;            # ReleaseNotes = ''&#10;&#10;            # Prerelease string of this module&#10;            # Prerelease = ''&#10;&#10;            # Flag to indicate whether the module requires explicit user acceptance for install/update/save&#10;            # RequireLicenseAcceptance = $false&#10;&#10;            # External dependent modules of this module&#10;            # ExternalModuleDependencies = @()&#10;&#10;        } # End of PSData hashtable&#10;&#10;    } # End of PrivateData hashtable&#10;&#10;    # HelpInfo URI of this module&#10;    # HelpInfoURI = ''&#10;&#10;    # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.&#10;    # DefaultCommandPrefix = ''&#10;&#10;}</pre>
 </div>
هر ماژول باید یک آی‌دی منحصر به فرد داشته باشد که به صورت Guid توسط یک پراپرتی با همین نام تعیین میشود. برای هر پراپرتی درون این فایل توضیحی به صورت کامنت نوشته شده است؛ اما برای دیدن جزئیات کامل میتوانید به اینجا مراجعه نمائید. در اینجا RootModule به PingModule.psm1 تنظیم شده است. تنظیم این پراپرتی نحوه نمایش module type را در خروجی Get-Module مشخص میکند. این مقدار اگر به فایل ماژول (psm1) اشاره کند، نوع ماژول script در نظر گرفته میشود، اگر به یک DLL اشاره کند به binary و در نهایت اگر به یک فایلی با پسوند دیگری اشاره کند manifest در نظر گرفته میشود. همچنین درون فایل فوق یکسری پراپرتی مانند CmdletsToExport, VariablesToExport, AliasesToExport به صورت خودکار تنظیم شده‌اند. در نهایت برای تست صحت فایل میتوانیم از دستور Test-ModuleManifest استفاده کنیم:  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">PS /&gt; Test-ModuleManifest ./PingModule.psd1   &#10;&#10;ModuleType Version    PreRelease Name                                ExportedCommands&#10;---------- -------    ---------- ----                                ----------------&#10;Script     1.0.0                 PingModule                          Get-PingReply</pre>
 </div> <div> <b>پابلیش ماژول</b> </div> <div>در نهایت توسط Publish-Module میتوانیم ماژول موردنظرمان را درون یک مخزن PowerShell پابلیش کنیم. رایج‌ترین مخزن برای انتشار پکیج‌های PowerShell یک فید نیوگت (مانند PowerShell Gallery) است. همچنین میتوانیم یک مخزن لوکال نیز برای پابلیش پکیج‌ها نیز ایجاد کنیم و خروجی نهایی را به صورت یک فایل با پسوند nupkg با دیگران به اشتراک بگذاریم. برای اینکار ابتدا نیاز است یک مخزن لوکال را به PowerShell معرفی کنیم:  </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">PS /&gt; Register-PSRepository -Name 'PSLocal' `                       &#10;&gt;&gt;     -SourceLocation "$(Resolve-Path $RepoPath)" `&#10;&gt;&gt;     -PublishLocation "$(Resolve-Path $RepoPath)" `&#10;&gt;&gt;     -InstallationPolicy 'Trusted'</pre>
 </div>
در کد فوق میتوانید مقدار متغیر RepoPath را به محل مدنظر روی سیستم‌تان تنظیم کنید. ساختار ماژولی که میخواهیم پابلیش کنیم نیز اینچنین خواهد بود:  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">ProjectRoot &#10;| -- PingModule&#10;     | -- PingModule.psd1 &#10;     | -- PingModule.psm1</pre>
 </div>
در ادامه برای پابلیش ماژول فوق درون ProjectRoot دستور Publish-Module را اینگونه اجرا خواهیم کرد:  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">PS /&gt; Publish-Module -Path ./PingModule/ -Repository PSLocal</pre>
 </div>
خروجی دستور فوق یک فایل با پسوند nupkg در مسیر مخزنی است که معرفی کردیم. همچنین با کمک دستور Find-Module میتوانیم ماژول موردنظر را لیست کنیم:  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">PS /&gt; Find-Module -Name PingModule -Repository PSLocal&#10;&#10;Version              Name                                Repository           Description&#10;-------              ----                                ----------           -----------&#10;0.0.1                PingModule                          PSLocal              Get-PingReply is a.</pre>
 </div>
برای نصب ماژول روی یک سیستم دیگر نیز ابتدا باید مطمئن شوید که سیستم مقصد، مخزن لوکال را تعریف کرده باشد و در نهایت توسط دستور Install-Module میتوانیم ماژول موردنظر را نصب کنیم:  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">PS /&gt; Install-Module -Name PingModule -Repository PSLocal -Scope CurrentUser</pre>
 </div>
همچنین امکان پابلیش کردن ورژن‌های متفاوت را نیز خواهیم داشت:  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">ProjectRoot &#10;| -- PingModule&#10;     | -- 1.0.0&#10;          | -- PingModule.psd1 &#10;          | -- PingModule.psm1&#10;     | -- 1.0.1&#10;          | -- PingModule.psd1 &#10;          | -- PingModule.psm1</pre>
 </div>
برای پابلیش کردن هر کدام از ماژول‌های فوق باید به ازای هر کدام یکبار دستور Publish-Module را اجرا کنیم:  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">PS /&gt; Publish-Module -Path ./PingModule/1.0.0 -Repository PSLocal&#10;PS /&gt; Publish-Module -Path ./PingModule/1.0.1 -Repository PSLocal</pre>
 </div>
اکنون حین نصب ماژول میتوانیم ورژن را نیز تعیین کنیم؛ در غیراینصورت آخرین ورژن یعنی 1.0.1 نصب خواهد شد:  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">PS /&gt; Install-Module -Name PingModule -Repository PSLocal -Scope CurrentUser&#10;PS /&gt; Get-InstalledModule -Name PingModule                                                                            &#10;Version              Name                                Repository           Description&#10;-------              ----                                ----------           -----------&#10;1.0.1                PingModule                          PSLocal              Get-PingReply is a.</pre>
 </div> <div> <b>ساختار مناسب برای ایجاد ماژول‌ها</b> </div> <div>برای توسعه ماژول‌های PowerShell توصیه میشود که از ساختار Multi-file layout استفاده شود به این معنا که بخش‌های مختلف پروژه به قسمت‌های کوچک‌تر و قابل‌نگهداری تقسیم شوند:  </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">ProjectRoot &#10;| -- PingModule&#10;     | -- 1.0.0&#10;          | -- Public/&#10;          | -- Private/&#10;          | -- PingModule.psd1&#10;          | -- PingModule.psm1</pre>
 </div>
در اینحالت باید اسکریپت‌ها و فایل‌های موردنیاز را توسط dot sourcing درون فایل ماژول بارگذاری کنیم:  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">$ScriptList = Get-ChildItem -Path $PSScriptRoot/Public/*.ps1 -Filter *.ps1&#10;&#10;foreach ($Script in $ScriptList) {&#10;    . $Script.FullName&#10;}&#10;&#10;$ScriptList = Get-ChildItem -Path $PSScriptRoot/Private/*.ps1 -Filter *.ps1&#10;&#10;foreach ($Script in $ScriptList) {&#10;    . $Script.FullName&#10;}</pre>
 </div> <div> <b>یک مثال عملی</b> </div> <div>در ادامه میخواهیم یک ماژول تهیه کنیم که قابلیت امضاء روی PDF را با کمک کتابخانه <a href="https://github.com/VahidN/iTextSharp.LGPLv2.Core">iTextSharp.LGPLv2.Core</a> انجام دهیم و به شکل زیر برای کاربر قابل استفاده باشد:  </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">PS /&gt; Set-PDFSingature -PdfToSign "./sample_invoice.pdf" -SignatureImage "./sample_signature.jpg"</pre>
 </div>
بنابراین ساختار پروژه را اینگونه ایجاد خواهیم کرد:  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">ProjectRoot &#10;| -- SignPdf&#10;     | -- 1.0.0&#10;          | -- Public/&#10;               | -- dependencies/&#10;                    | -- BouncyCastle.Crypto.dll&#10;                    | -- System.Drawing.Common.dll&#10;                    | -- Microsoft.Win32.SystemEvents.dll&#10;                    | -- iTextSharp.LGPLv2.Core.dll&#10;               | -- Set-PDFSingature.ps1&#10;          | -- SignPdf.psd1&#10;          | -- SignPdf.psm1</pre>
 </div>
برای استفاده از پکیج ذکر شده نیاز خواهد بود که Dllهای موردنیاز را نیز به عنوان وابستگی به پروژه اضافه کنیم (محتویات پوشه dependencies) سپس درون فایل ماژول (SignPdf.psm1) همانطور که عنوان شد میبایست اسکریپت‌ها درون پوشه Public را بارگذاری کنیم و همچنین درون تابعی که قرار است Export شود را نیز تعیین کرده‌ایم (Set-PDFSingature)  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">$ScriptList = Get-ChildItem -Path $PSScriptRoot/Public/*.ps1 -Filter *.ps1&#10;&#10;foreach ($Script in $ScriptList) {&#10;    . $Script.FullName&#10;}&#10;&#10;Export-ModuleMember -Function Set-PDFSingature</pre>
 </div>
در ادامه درون فایل Set-PDFSignature پیاده‌سازی را انجام خواهیم داد:  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">using namespace iTextSharp.text&#10;using namespace iTextSharp.text.pdf&#10;using namespace System.IO&#10;&#10;Function Set-PDFSingature {&#10;    [CmdletBinding()]&#10;    Param(&#10;        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]&#10;        [ValidateScript({&#10;                if (Test-Path ([Path]::Join($(Get-Location), $_))) {&#10;                    return $true&#10;                }&#10;                else {&#10;                    throw "Signature image not found"&#10;                }&#10;                if ($_.EndsWith('.pdf')) {&#10;                    return $true&#10;                }&#10;                else {&#10;                    throw "File extension must be .pdf"&#10;                }&#10;            })]&#10;        [string]$PdfToSign,&#10;        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]&#10;        [ValidateScript({&#10;                if (Test-Path ([Path]::Join($(Get-Location), $_))) {&#10;                    return $true&#10;                }&#10;                else {&#10;                    throw "Signature image not found"&#10;                }&#10;                if ($_.EndsWith('.png') -or $_.EndsWith('.jpg')) {&#10;                    return $true&#10;                }&#10;                else {&#10;                    throw "File extension must be .png or .jpg"&#10;                }&#10;            })]&#10;        [string]$SignatureImage,&#10;        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]&#10;        [int]$XPos = 130,&#10;        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]&#10;        [int]$YPos = 50&#10;    )&#10;    Try {&#10;        Add-Type -Path "$PSScriptRoot/dependencies/*.dll"&#10;        $pdf = [PdfReader]::new("$(Get-Location)/$PdfToSign")&#10;        $fs = [FileStream]::new("$(Get-Location)/$PdfToSign-signed.pdf", &#10;            [FileMode]::Create)&#10;        $stamper = [PdfStamper]::new($pdf, $fs)&#10;        $stamper.AcroFields.AddSubstitutionFont([BaseFont]::CreateFont())&#10;        $content = $stamper.GetOverContent(1)&#10;        $width = $pdf.GetPageSize(1).Width&#10;        $image = [Image]::GetInstance("$(Get-Location)/$SignatureImage")&#10;        $image.SetAbsolutePosition($width - $XPos, $YPos)&#10;        $image.ScaleAbsolute(100, 30)&#10;        $content.AddImage($image)&#10;        $stamper.Close()&#10;        $pdf.Close()&#10;        $fs.Dispose()&#10;    }&#10;    Catch {&#10;        Write-Host "Error: $($_.Exception.Message)"&#10;    }&#10;}</pre>
 </div>
روال قرار دادن یک امضاء بر روی یک فایل PDF قبلاً در سایت توضیح داده شده است. کدهای فوق در واقع معادل PowerShell همان کدهای موجود در سایت هستند و نکته خاصی ندارند. در نهایت میتوانیم ماژول تهیه شده را روی مخزن موردنظر پابلیش کنیم:  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">PS /&gt; Publish-Module -Path ./SignPdf/1.0.0 -Repository PSLocal</pre>
 </div>
برای نصب ماژول میتوانیم از دستور Install-Module استفاده کنیم:  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">PS /&gt; Install-Module -Name SignPdf -Repository PSLocal -Scope CurrentUser</pre>
 </div>
در نهایت برای استفاده از ماژول ایجاد شده میتوانیم اینگونه عمل کنیم:  <br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">PS /&gt; Set-PDFSingature -PdfToSign "./sample_invoice.pdf" -SignatureImage "./sample_signature.jpg"</pre>
 </div>
خروجی نیز فایل امضاء شده خواهد بود:  <br/> </div> <div> <p style="margin-left: auto; margin-right: auto;"> <img src="/img/dntips/182449957dfcd92f1a77.jpg" style="display: block; margin-left: auto; margin-right: auto; cursor: default; height: 890px;"/> </p> <p style="margin-left: auto; margin-right: auto;">کدهای ماژول را میتوانید از <a href="https://github.com/SirwanAfifi/SignPdf-PS">اینجا</a> دانلود کنید.  <br/> </p> </div> </div> </div> </div> </div></div>
