-
Notifications
You must be signed in to change notification settings - Fork 8.1k
How to create cmdlet w/dotnet CLI #5117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
daxian-dbw
merged 18 commits into
PowerShell:master
from
rkeithhill:rkeithhill/cmdlet-with-dotnet-cli
Oct 19, 2017
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
7574f21
How to create cmdlet w/dotnet CLI
rkeithhill 2485b8d
More editing and address feedback
rkeithhill 45341bf
More fixes inlcuding esc S.M.A
rkeithhill f9b01a2
More editing
rkeithhill 9944c36
fix borked mymodule.dd
rkeithhill b6642ac
Add dotnet --version check
rkeithhill 1d746b1
Add links to netstandard docs/channel
rkeithhill 3a9de59
Clean up Note: usage
rkeithhill e2350a3
Add section header for missing netstandard.dll fix
rkeithhill f332de2
Add doc region to .spelling, fix spelling errors
rkeithhill 4ca34ca
Add wrapup section and more editing
rkeithhill 7a7ca2b
Fix spelling error
rkeithhill f51878a
Missed a few changes
rkeithhill 4214904
Add to markdown.tests.ps1
rkeithhill dc6c460
Fix some markdown lint issues
rkeithhill e07d03a
Fix grammar error
rkeithhill 34fdebf
Switch olist to One style
rkeithhill d38ac0c
Argh, wish VSCode would tell me I have unsaved changes when committing
rkeithhill File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,227 @@ | ||
| # Creating a cross-platform binary module with the .NET Core command-line interface tools | ||
|
|
||
| This example uses the [.NET Core command-line interface tools][dotnet-cli] (aka | ||
| `dotnet` CLI) to demonstrate how to create a binary module that is portable across operating | ||
| systems supported by **PowerShell Core** as well as **Windows PowerShell** version 3 and higher. | ||
|
|
||
| Because the binary module's assembly will be created as a .NET Standard 2.0 class library, | ||
| the same assembly can be imported into both PowerShell Core and Windows PowerShell. | ||
| This means you do not have to build and distribute separate assemblies that target these two | ||
| different implementations of PowerShell. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| * PowerShell Core and/or Windows PowerShell | ||
|
|
||
| For this example, you can use any operating system that is supported by PowerShell Core. | ||
| To see if your operating system is supported and to get instructions on how to install | ||
| PowerShell Core on your operating system, see the [Get PowerShell][pscore-os] topic in | ||
| the PowerShell repo's [README.md][readme] file. | ||
|
|
||
| Note: On Windows 10 Anniversary Update or higher, you can use the [Windows Subsystem for | ||
| Linux][wsl] (WSL) console to build the module. In order to import and use the module, you'll need | ||
| to install PowerShell Core for the distribution and version of Linux you're running. | ||
| You can get that version info by running the command `lsb_release -a` from the WSL console. | ||
|
|
||
| * .NET Core 2.0 SDK | ||
|
|
||
| Download and install the [.NET Core 2.0 SDK][net-core-sdk] for your operating system. | ||
| It is recommended that you use a package manager to install the SDK on Linux. | ||
| See these [instructions][linux-install] on how to install the SDK on Linux. | ||
| Be sure to pick your distribution of Linux e.g. RHEL, Debian, etc to get the | ||
| appropriate instructions for your platform. | ||
|
|
||
| ## Create the .NET Standard 2.0 Binary Module | ||
|
|
||
| 1. Verify you are running the 2.0.0 version of the `dotnet` CLI. | ||
|
|
||
| ```powershell | ||
| dotnet --version | ||
| ``` | ||
|
|
||
| This should output `2.0.0` or higher. If it returns a major version of 1, make sure you have | ||
| installed the .NET Core 2.0 SDK and have restarted your shell to get the newer version of | ||
| the SDK tools. | ||
|
|
||
| 1. Use the `dotnet` CLI to create a starter `classlib` project based on .NET Standard 2.0 | ||
| (the default for classlib projects). | ||
|
|
||
| ```powershell | ||
| dotnet new classlib --name MyModule | ||
| ``` | ||
|
|
||
| 1. Add a `global.json` file that specifies that the project requires the `2.0.0` version of | ||
| the .NET Core SDK. This is necessary to prevent issues if you have more than one | ||
| version of the .NET Core SDK installed. | ||
|
|
||
| ```powershell | ||
| cd MyModule | ||
| dotnet new globaljson --sdk-version 2.0.0 | ||
| ``` | ||
|
|
||
| 1. Add the [PowerShell Standard Library][ps-stdlib] package to the project file. | ||
| This package provides the `System.Management.Automation` assembly. | ||
|
|
||
| Note: As newer versions of this library are released, update the version number | ||
| in this command to match the latest version. | ||
|
|
||
| ```powershell | ||
| dotnet add package PowerShellStandard.Library --version 3.0.0-preview-01 | ||
| ``` | ||
|
|
||
| 1. Add source code for a simple PowerShell command to the `Class1.cs` file by opening | ||
| that file in an editor and replacing the existing code with the following code. | ||
|
|
||
| ```csharp | ||
| using System; | ||
| using System.Management.Automation; | ||
|
|
||
| namespace MyModule | ||
| { | ||
| [Cmdlet(VerbsCommunications.Write, "TimestampedMessage")] | ||
| public class WriteTimestampedMessageCommand : PSCmdlet | ||
| { | ||
| [Parameter(Position=1)] | ||
| public string Message { get; set; } = string.Empty; | ||
|
|
||
| protected override void EndProcessing() | ||
| { | ||
| string timestamp = DateTime.Now.ToString("u"); | ||
| this.WriteObject($"[{timestamp}] - {this.Message}"); | ||
| base.EndProcessing(); | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| 1. Build the project. | ||
|
|
||
| ```powershell | ||
| dotnet build | ||
| ``` | ||
|
|
||
| 1. Import the binary module and invoke the new command. | ||
|
|
||
| Note: The previous steps could have been performed in a different shell such as | ||
| Bash if you're on Linux. For this step, make sure you are running PowerShell Core. | ||
|
|
||
| ```powershell | ||
| cd 'bin/Debug/netstandard2.0' | ||
| Import-Module ./MyModule.dll | ||
| Write-TimestampedMessage "Test message." | ||
| ``` | ||
|
|
||
| ## Using a .NET Standard 2.0 based binary module in Windows PowerShell | ||
|
|
||
| You may have heard that a .NET assembly compiled as a .NET Standard 2.0 class library | ||
| will load into both .NET Core 2.0 applications such as PowerShell Core and | ||
| .NET Framework 4.6.1 (or higher) applications such as Windows PowerShell. | ||
| This allows you to build a single, cross-platform binary module. | ||
|
|
||
| Unfortunately, this works best when the .NET Framework application, in this case | ||
| Windows PowerShell, has either been compiled against a .NET Standard 2.0 library or with | ||
| support declared for .NET Standard libraries. In which case, the build system can provide the | ||
| appropriate binding redirects and facade and shim assemblies so that the .NET Standard 2.0 | ||
| library can find the .NET Framework types it needs within the context of the running | ||
| application. | ||
|
|
||
| Fortunately, this has been fixed in .NET Framework 4.7.1 and in the Windows 10 Fall | ||
| Creators Update. This version of the .NET Framework allows existing applications to | ||
| "just work" without the need to modify and/or re-compile them. On these systems, a | ||
| .NET Standard 2.0 based binary module will work in Windows PowerShell. | ||
|
|
||
| However, for Windows systems that have not been updated to .NET Framework 4.7.1 such a | ||
| binary module will not run correctly in Windows PowerShell. | ||
|
|
||
| Let's see what happens when you attempt use this module in **Windows PowerShell** on | ||
| Windows 10 CU (1703 or lower) without .NET Framework 4.7.1 installed. | ||
|
|
||
| 1. Copy `MyModule.dll` to a folder on a Windows machine. | ||
|
|
||
| 1. Import the module. | ||
|
|
||
| ```powershell | ||
| Import-Module .\MyModule.dll | ||
| ``` | ||
|
|
||
| Note: The module should import without errors. | ||
|
|
||
| 1. Execute the `Write-TimestampedMessage` command. | ||
|
|
||
| ```powershell | ||
| Write-TimestampedMessage "Test message." | ||
| ``` | ||
|
|
||
| This will result in the following error: | ||
|
|
||
| ```text | ||
| Write-TimestampedMessage : Could not load file or assembly 'netstandard, Version=2.0.0.0, Culture=neutral, | ||
| PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The system cannot find the file specified. | ||
| At line:1 char:1 | ||
| + Write-TimestampedMessage "Test message." | ||
| + ~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| + CategoryInfo : NotSpecified: (:) [], FileNotFoundException | ||
| + FullyQualifiedErrorId : System.IO.FileNotFoundException | ||
| ``` | ||
|
|
||
| If the command worked, congratulations! Your system was probably updated to | ||
| .NET Framework 4.7.1. Otherwise, this error indicates that the `MyModule.dll` assembly | ||
| can't find the `netstandard.dll` "implementation" assembly for the version of the | ||
| .NET Framework that Windows PowerShell is using. | ||
|
|
||
| ### The fix for missing netstandard.dll | ||
|
|
||
| If you install (or already have) the .NET Core 2.0 SDK for Windows, you can | ||
| find the `netstandard.dll` implementation assembly for .NET 4.6.1 in the following directory: | ||
| `C:\Program Files\dotnet\sdk\2.0.0\Microsoft\Microsoft.NET.Build.Extensions\net461\lib`. | ||
|
|
||
| If you copy `netstandard.dll` from this directory to the directory containing | ||
| `MyModule.dll`, the `Write-TimestampedMessage` command will work. Let's try that. | ||
|
|
||
| 1. Install the [.NET Core SDK 2.0 for Windows][net-core-sdk], if it isn't already installed. | ||
|
|
||
| 1. Start a new Windows PowerShell console. Remember that once a binary assembly is | ||
| loaded into PowerShell it can't be unloaded. Restarting PowerShell is necessary to | ||
| get it to reload `MyModule.dll`. | ||
|
|
||
| 1. Copy the `netstandard.dll` implementation assembly for .NET 4.6.1 to the module's directory. | ||
| ```powershell | ||
| cd 'path-to-where-you-copied-module.dll' | ||
| Copy-Item 'C:\Program Files\dotnet\sdk\2.0.0\Microsoft\Microsoft.NET.Build.Extensions\net461\lib\netstandard.dll' . | ||
| ``` | ||
|
|
||
| 1. Import the module and execute the command: | ||
| ```powershell | ||
| Import-Module .\MyModule.dll | ||
| Write-TimestampedMessage "Test message." | ||
| ``` | ||
| Now the command should succeed. | ||
|
|
||
| Note: If it fails, restart Windows PowerShell to make sure | ||
| you don't have a previously loaded version of the assembly in the session and repeat | ||
| step 4. | ||
|
|
||
| If you use additional libraries there may be more work involved. This approach has | ||
| been successfully tested using types from `System.Xml` and `System.Web`. | ||
|
|
||
| ## Wrap-up | ||
|
|
||
| In a few steps, we have built a PowerShell binary module using a .NET Standard 2.0 | ||
| class library that will run in PowerShell Core on multiple operating systems. | ||
| It will also run in Windows PowerShell on Windows systems that have been updated to | ||
| .NET Framework 4.7.1 as well as the Windows 10 Fall Creators Update which comes with that | ||
| version pre-installed. Furthermore, this binary module can be built on Linux | ||
| and macOS as well as Windows using the .NET Core 2.0 SDK command-line tools. | ||
|
|
||
| For more information on .NET Standard, check out the [documentation][net-std-docs] | ||
| and the [.NET Standard YouTube channel][net-std-chan]. | ||
|
|
||
| [dotnet-cli]: https://docs.microsoft.com/en-us/dotnet/core/tools/?tabs=netcore2x | ||
| [net-core-sdk]: https://www.microsoft.com/net/download/core | ||
| [net-std-docs]: https://docs.microsoft.com/en-us/dotnet/standard/net-standard | ||
| [net-std-chan]: https://www.youtube.com/playlist?list=PLRAdsfhKI4OWx321A_pr-7HhRNk7wOLLY | ||
| [pscore-os]: https://github.com/powershell/powershell#get-powershell | ||
| [readme]: ../../README.md | ||
| [linux-install]: https://www.microsoft.com/net/core#linuxubuntu | ||
| [ps-stdlib]: https://www.nuget.org/packages/PowerShellStandard.Library/ | ||
| [wsl]: https://msdn.microsoft.com/commandline/wsl/about | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,10 +8,10 @@ Describe 'Common Tests - Validate Markdown Files' -Tag 'CI' { | |
| BeforeAll { | ||
| # Skip if not windows, We don't need these tests to run on linux (the tests run fine in travis-ci) | ||
| $skip = !$IsWindows | ||
| if ( !$skip ) | ||
| if ( !$skip ) | ||
| { | ||
| $NpmInstalled = "not installed" | ||
| if (Get-Command -Name 'npm' -ErrorAction SilentlyContinue) | ||
| if (Get-Command -Name 'npm' -ErrorAction SilentlyContinue) | ||
| { | ||
| $NpmInstalled = "Installed" | ||
| Write-Verbose -Message "NPM is checking Gulp is installed. This may take a few moments." -Verbose | ||
|
|
@@ -27,10 +27,10 @@ Describe 'Common Tests - Validate Markdown Files' -Tag 'CI' { | |
| -Wait ` | ||
| -WorkingDirectory $PSScriptRoot ` | ||
| -NoNewWindow | ||
| } | ||
| } | ||
| elseif( -not $env:AppVeyor) | ||
| { | ||
| <# | ||
| <# | ||
| On Windows, but not an AppVeyor and pre-requisites are missing | ||
| For now we will skip, and write a warning. Work to resolve this is tracked in: | ||
| https://github.com/PowerShell/PowerShell/issues/3429 | ||
|
|
@@ -42,9 +42,9 @@ Describe 'Common Tests - Validate Markdown Files' -Tag 'CI' { | |
| } | ||
|
|
||
| AfterAll { | ||
| if ( !$skip ) | ||
| if ( !$skip ) | ||
| { | ||
| <# | ||
| <# | ||
| NPM install all the tools needed to run this test in the test folder. | ||
| We will now clean these up. | ||
| We're using this tool to delete the node_modules folder because it gets too long | ||
|
|
@@ -74,6 +74,7 @@ Describe 'Common Tests - Validate Markdown Files' -Tag 'CI' { | |
| $docsToTest = @( | ||
| './*.md' | ||
| './docs/*.md' | ||
| './docs/cmdlet-example/command-line-simple-example.md' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we can change this to |
||
| './docs/installation/*.md' | ||
| './docs/maintainers/README.md' | ||
| './demos/SSHRemoting/*.md' | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make sure you have added this file to the markdown syntax tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done