{"id":18776,"date":"2020-12-08T09:20:18","date_gmt":"2020-12-08T17:20:18","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/powershell\/?p=18776"},"modified":"2021-11-06T11:40:53","modified_gmt":"2021-11-06T19:40:53","slug":"announcing-powershell-crescendo-preview-1","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/powershell\/announcing-powershell-crescendo-preview-1\/","title":{"rendered":"Announcing PowerShell Crescendo Preview.1"},"content":{"rendered":"<p>As a shell, PowerShell is intended to work with native commands in addition to cmdlets. However,\nnative commands have their own unique syntax, sometimes containing many subcommands and\nparameters\/switches, and are often like its own language. Wouldn&#8217;t it be great to have an option to\nleverage your PowerShell skills to use new commands or be even more proficient with native\ncommands you are already using? As a PowerShell user, it would be great if those native commands\nsupported PowerShell features like:<\/p>\n<ul>\n<li>Clear naming convention &#8211; <code>Verb-Noun<\/code><\/li>\n<li>Consistent parameter naming for similar uses<\/li>\n<li>Output consisting of objects<\/li>\n<li>Common method of getting command help<\/li>\n<li>Easy to work with objects in a pipeline<\/li>\n<li>Easy to share using Modules<\/li>\n<\/ul>\n<p>We are pleased to announce the first preview of <strong>PowerShell Crescendo<\/strong>, a framework to rapidly\ndevelop PowerShell cmdlets for native commands, regardless of platform.<\/p>\n<p>Many of today&#8217;s modern native commands are complex, they themselves are <em>mini-shells<\/em> with their own\n<em>mini-language<\/em> containing sub levels, or sub contexts. If you&#8217;ve worked with <code>kubectl<\/code>, <code>docker<\/code> or\n<code>netsh.exe<\/code>, you have experienced the complexity of executing and automating these commands.<\/p>\n<p>To make native commands have a more PowerShell-like feel and offer a similar experience, you could\nre-write the tool yourself, or if the tool uses <code>REST<\/code>, directly call the web APIs using\n<a href=\"https:\/\/github.com\/Azure\/autorest\">AutoRest<\/a>. These options work very well, but require more\ndevelopment experience and can be harder to maintain over time.<\/p>\n<p>Crescendo provides the tools to easily wrap a native command to gain the benefits of PowerShell\ncmdlets. Wrapping native commands into Crescendo cmdlets can provide parameter handling like\nprompting for mandatory parameters and tab-completion for parameter values. Crescendo cmdlets\ncan take the text output from the native application and parse it into objects. The output objects\nallow you to take advantage of all the post processing tools such as <code>Sort-Object<\/code>, <code>Where-Object<\/code>,\netc.<\/p>\n<h2>What&#8217;s supported<\/h2>\n<p>Microsoft.PowerShell.Crescendo 0.4.1 Preview.1 is currently available for download from the\n<a href=\"https:\/\/www.powershellgallery.com\/packages\/Microsoft.PowerShell.Crescendo\/0.4.1\">PowerShell Gallery<\/a><\/p>\n<p>Supported PowerShell versions for authoring a module with Crescendo:<\/p>\n<ul>\n<li>PowerShell 7.0+<\/li>\n<\/ul>\n<p>Supported PowerShell versions that can execute a Crescendo module:<\/p>\n<ul>\n<li>Windows PowerShell 5.1+<\/li>\n<li>PowerShell 7.0+<\/li>\n<\/ul>\n<h2>What&#8217;s next<\/h2>\n<p>Next\/Future plans for preview.2 will be based on feedback and include a few items under investigation:<\/p>\n<ul>\n<li>Create cmdlet aliases when generating a module<\/li>\n<li>Investigate parsing man pages and documentation to improve automatic JSON generation<\/li>\n<\/ul>\n<h2>Installing Crescendo<\/h2>\n<p>To create a Crescendo cmdlet, you will need the module\n<strong>Microsoft.PowerShell.Crescendo<\/strong> from the PowerShell Gallery.<\/p>\n<pre><code class=\"powershell\">Install-Module Microsoft.PowerShell.Crescendo\r\n<\/code><\/pre>\n<h2>Documentation<\/h2>\n<p>We&#8217;ve included the <code>about_Crescendo<\/code> and we&#8217;ll be adding more documentation in the future.\nMake sure to check out the Samples folder in the module.<\/p>\n<pre><code class=\"powershell\">Get-Help about_Crescendo\r\n<\/code><\/pre>\n<p>To author a Crescendo module, you create a JSON configuration file describing how you want the\nnative command projected as a cmdlet. You need the <strong>Microsoft.PowerShell.Crescendo<\/strong> module to\nbuild the finished module containing the Crescendo proxy cmdlets. The\n<strong>Microsoft.PowerShell.Crescendo<\/strong> module runs on PowerShell 7+.<\/p>\n<p>To consume a module built with Crescendo, you don&#8217;t need to have <strong>Microsoft.PowerShell.Crescendo<\/strong>\ninstalled. Simply download or install the generated module and you&#8217;re good to go. Because a\nCrescendo-built module is just like any other module, these are supported on Windows PowerShell 5.1\nand above. Your work can help others in the community automate native commands. Fortunately,\nbuilding and sharing Crescendo modules is easy when you publish to the PowerShell Gallery.<\/p>\n<p>For more information about the concepts and design approach, see:<\/p>\n<ul>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/powershell\/native-commands-in-powershell-a-new-approach\/\">Native Commands in PowerShell Part 1<\/a><\/li>\n<li><a href=\"https:\/\/devblogs.microsoft.com\/powershell\/native-commands-in-powershell-a-new-approach-part-2\/\">Native Commands in PowerShell Part 2<\/a><\/li>\n<\/ul>\n<h2>Examples<\/h2>\n<h3>Creating a new cmdlet<\/h3>\n<p>This example builds on a common Linux packaging utility <code>apt<\/code> for displaying, installing and\nremoving software. The properties <strong>Verb<\/strong> and <strong>Noun<\/strong> define the name of the new cmdlet\n<code>Get-InstalledPackage<\/code>.<\/p>\n<p>The Crescendo module includes a schema definition file for assistance creating and editing JSON\nconfiguration files. The schema file <code>Microsoft.PowerShell.Crescendo.Schema.json<\/code> is included with\nthe module. In the below example, the schema file is copied to a temporary working directory with\nthe JSON file under development. The location of the schema file is defined in the JSON\nconfiguration and can be located anywhere.<\/p>\n<p>Common properties defined by the schema:<\/p>\n<ul>\n<li><code>Verb<\/code>: The name of the cmdlet verb (Check <code>Get-Verb<\/code> for valid verb names)<\/li>\n<li><code>Noun<\/code>: The name of the cmdlet noun<\/li>\n<li><code>OriginalName<\/code>: The original native command name and location<\/li>\n<li><code>OriginalCommandElements<\/code>: Some native commands have additional mandatory switches to execute\nproperly for a given scenario<\/li>\n<li><code>OutputHandlers<\/code>: The output handler captures the string output from the native command and\ntransforms it into structured data (objects). As with other PowerShell cmdlets, this object output\nmay be manipulated further down the pipeline.<\/li>\n<\/ul>\n<p>Note: The example code contains comments (<code>\/\/ &lt;--<\/code>) in JSON. They are for\ndescriptive purposes only and should be removed.<\/p>\n<pre><code class=\"json\">{\r\n    \"$schema\": \".\/Microsoft.PowerShell.Crescendo.Schema.json\", \/\/ &lt;-- Schema definition file\r\n    \"Verb\": \"Get\", \/\/ &lt;-- Cmdlet Verb\r\n    \"Noun\":\"InstalledPackage\", \/\/ &lt;-- Cmdlet Noun\r\n    \"OriginalName\": \"apt\", \/\/ &lt;-- Native command\r\n    \"OriginalCommandElements\": [\"-q\",\"list\",\"--installed\"],\r\n    \"OutputHandlers\": [ \/\/ &lt;-- Add string output to an object\r\n        {\r\n            \"ParameterSetName\":\"Default\",\r\n            \"Handler\": \"$args[0]| select-object -skip 1 | %{$n,$v,$p,$s = \"$_\" -split ' '; [pscustomobject]@{ Name = $n -replace '\/now'; Version = $v; Architecture = $p; State = $s.Trim('[]') -split ',' } }\"\r\n        }\r\n    ]\r\n}\r\n<\/code><\/pre>\n<p>The following example shows how to use the <code>Get-InstalledPackage<\/code> we defined in the JSON file.<\/p>\n<pre><code class=\"powershell\">PS&gt; Get-InstalledPackage | Where-Object { $_.name -match \"libc\"}\r\n\r\nName        Version            Architecture State\r\n----        -------            ------------ -----\r\nlibc-bin    2.31-0ubuntu9.1    amd64        {installed, local}\r\nlibc6       2.31-0ubuntu9.1    amd64        {installed, local}\r\nlibcap-ng0  0.7.9-2.1build1    amd64        {installed, local}\r\nlibcom-err2 1.45.5-2ubuntu1    amd64        {installed, local}\r\nlibcrypt1   1:4.4.10-10ubuntu4 amd64        {installed, local}\r\n\r\nPS&gt; Get-InstalledPackage | Group-Object -Property Architecture\r\n\r\nCount Name   Group\r\n----- ----   -----\r\n   10 all    {@{Name=adduser; Version=3.118ubuntu2; Architecture=all; State=System.String[}, @{Name=debconf; V\u2026\r\n   82 amd64  {@{Name=apt; Version=2.0.2ubuntu0.1; Architecture=amd64; State=System.String[]}, @{Name=base-files\u2026\r\n<\/code><\/pre>\n<h3>Example with parameters<\/h3>\n<p>This example wraps the Windows command <code>ipconfig.exe<\/code> and illustrates how to define parameters. In\naddition to the properties in the above example, the schema supports a <code>Parameters<\/code> section to wrap\nand project descriptively named parameters to the new cmdlet.<\/p>\n<ul>\n<li><code>Name<\/code>: Name of parameter that appears in the new cmdlet<\/li>\n<li><code>OriginalName<\/code>: Name of original parameter that appears in the native command<\/li>\n<\/ul>\n<pre><code class=\"json\">{\r\n    \"$schema\" : \".\/Microsoft.PowerShell.Crescendo.Schema.json\",\r\n    \"Verb\": \"Get\",\r\n    \"Noun\": \"IpConfig\",\r\n    \"OriginalName\":\"c:\/windows\/system32\/ipconfig.exe\",\r\n    \"Description\": \"This will display the current IP configuration information on Windows\",\r\n\r\n    \"Parameters\": [ \/\/ &lt;-- Parameter definition\r\n        {\r\n            \"Name\":\"All\", \/\/ &lt;-- Name of parameter that appears in cmdlet\r\n            \"OriginalName\": \"\/all\", \/\/ &lt;-- Name of original parameter that appears in native cmd\r\n            \"ParameterType\": \"switch\",\r\n            \"Description\": \"This switch provides all ip configuration details\" \/\/ &lt;-- Help parameter\r\n        },\r\n        {\r\n            \"Name\":\"AllCompartments\",\r\n            \"OriginalName\": \"\/allcompartments\",\r\n            \"ParameterType\": \"switch\",\r\n            \"Description\": \"This switch provides compartment configuration details\"\r\n        }\r\n    ],\r\n\r\n    \"OutputHandlers\": [\r\n        {\r\n        \"ParameterSetName\": \"Default\",\r\n        \"Handler\":\"param ( $lines )\r\n        $post = $false;\r\n        foreach($line in $lines | ?{$_.trim()}) {\r\n            $LineToCheck = $line | select-string '^[a-z]';\r\n            if ( $LineToCheck ) {\r\n                if ( $post ) { [pscustomobject]$ht |add-member -pass -typename $oName }\r\n                $oName = ($LineToCheck -match 'Configuration') { 'IpConfiguration' } else {'EthernetAdapter'}\r\n                $ht = @{};\r\n                $post = $true\r\n            }\r\n            else {\r\n                if ( $line -match '^   [a-z]' ) {\r\n                    $prop,$value = $line -split ' :',2;\r\n                    $pName = $prop -replace '[ .-]';\r\n                    $ht[$pName] = $value.Trim()\r\n                }\r\n                else {\r\n                    $ht[$pName] = .{$ht[$pName];$line.trim()}\r\n                }\r\n            }\r\n        }\r\n        [pscustomobject]$ht | add-member -pass -typename $oName\"\r\n        }\r\n    ]\r\n}\r\n<\/code><\/pre>\n<h3>Export the proxy module from JSON configuration<\/h3>\n<p>Exporting a Crescendo JSON configuration file results in a PowerShell module that can be shared\nlocally, on GitHub, and on the PowerShell Gallery. Crescendo includes the\n<code>Export-CrescendoModule<\/code> cmdlet that generates the proxy module from the JSON configuration file.<\/p>\n<ul>\n<li>The <strong>ConfigurationFile<\/strong> parameter should include the name and path to the JSON configuration file.<\/li>\n<li>The <strong>ModuleName<\/strong> parameter contains the name of the exported module.<\/li>\n<\/ul>\n<pre><code class=\"powershell\">Export-CrescendoModule -ConfigurationFile .get-ipconfig.crescendo.json -ModuleName Ipconfig.psm1\r\n<\/code><\/pre>\n<h3>Using your Crescendo module<\/h3>\n<p>Once you have created the module with Crescendo, you can install and import it like any other\nmodule. Keep in mind you will need the original native command available wherever you install the\nCrescendo module.<\/p>\n<pre><code class=\"powershell\">PS&gt; Import-Module .ipconfig.psm1\r\nPS&gt; Get-IpConfig -All | Select-Object -Property ipv4address, DefaultGateway, subnetmask\r\n\r\nipv4address  DefaultGateway                              subnetmask\r\n-----------  --------------                              ----------\r\n\r\n172.24.112.1                                             255.255.240.0\r\n192.168.94.2 {fe80::145e:1779:5cfa:c2a5%8, 192.168.94.1} 255.255.255.0\r\n<\/code><\/pre>\n<h2>How you can help<\/h2>\n<p>Our goal is to make it easier to convert your native commands to PowerShell cmdlets and receive the\nbenefits that PowerShell provides. We value your ideas and feedback and hope you will give Crescendo\na try, then stop by our GitHub repository and let us know of any issues or features you would like\nadded.<\/p>\n<p>For more information about <strong>PowerShell Crescendo<\/strong> issues and features, see:\n<a href=\"https:\/\/github.com\/PowerShell\/Crescendo\">Crescendo on GitHub<\/a><\/p>\n<p>Jason Helmick\nProgram Manager, PowerShell<\/p>\n","protected":false},"excerpt":{"rendered":"<p><Summary of the post><\/p>\n","protected":false},"author":7527,"featured_media":13641,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[3173],"class_list":["post-18776","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-powershell","tag-powershell-crescendo"],"acf":[],"blog_post_summary":"<p><Summary of the post><\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts\/18776","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/users\/7527"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/comments?post=18776"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/posts\/18776\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/media\/13641"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/media?parent=18776"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/categories?post=18776"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/powershell\/wp-json\/wp\/v2\/tags?post=18776"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}