Tag Archives: Crescendo

PowerShell User Group Austria – Crescendo

This morning — well, this morning my time — I was a part of a speaking event with the PowerShell User Group Austria, where I discussed the Microsoft Crescendo PowerShell module (https://github.com/PowerShell/Crescendo and https://www.powershellgallery.com/packages/Microsoft.PowerShell.Crescendo). It was clear to me that a couple of related posts of mine got the attention of Roman, and put me in a place where I could share what I knew about the project. It was fun!

The project just entered preview-2 on May 6, 2021 — a mere five days ago. While I didn’t focus on the additions from that preview, I essentially walked through my Part I and Part II, which were written with preview-1 in mind. I did, however, mention the additions that preview-2 brought us.

As I stated I would during that user group meeting, I’ve offered up my demo file. It includes several helpful links, as well as everything I used to create those things I did. The paths won’t line up with your system, so keep that in mind if you work through the examples.

PowerShell_User_Group_Austria_DemoFile (2610 downloads ) PowerShell_User_Group_Austria_JSONFile (2981 downloads )

 

Simple Simple Microsoft Crescendo Example Part II

The Microsoft.PowerShell.Crescendo module is mostly brand new. It’s still early on in its development. It’s currently at version 0.4.1. This isn’t my first discussion on this topic, so please read Part I on this topic. That’ll give you an idea of my first experience working with the module. In that post, we created a PowerShell module that contained a single command that wrapped the mstsc.exe Remote Desktop Connection executable. Although there are several, I only wrote it with a single parameter: a stand-in for /v. This parameter expects the name of the computer or IP address, such as mstsc /v Server01.

In that example I copied, pasted, and edited an existing *.Crescendo.json file. Today, we’ll wrap the same command (even though I alluded to using something else), however, we’ll do it using other cmdlets from the module. These will include New-CrescendoCommand, New-ParameterInfo, New-UsageInfo, and New-ExampleInfo. I had no idea what these were for as the help is limited, but I think I figured it out for now. If you know more, then of course you’re welcome to comment and participate.

In the first example, we invoked New-CrescendoCommand. The available parameters at the time of this writing are -Verb and -Noun. Knowing those parameters, it’s straightforward what this was about. This is the naming for our new PowerShell command. As you can see, I supplied Connect and RemoteComputer, respectively. These are the same values that we supplied in Part I of this series. As you can likely tell, the command creates an object (a [TypeName] Command object to be exact). We send that object to ConvertTo-Json, as we’ll need everything in JSON before creating our mstsc.Crescendo.json file. As you can also tell, the object — both the PowerShell object and JSON object — has several other properties. As best as I could determine, there was no way to include these when invoking the New-CrescendoCommand. Do notice that the OriginalName property is blank and that there wasn’t a way to include that when invoking the command. We’ll be back to discuss this later.

[PS7.1.0] $Command = New-CrescendoCommand -Verb Connect -Noun RemoteComputer
[PS7.1.0] $Command

Verb : Connect
Noun : RemoteComputer
OriginalName :
OriginalCommandElements :
Aliases :
DefaultParameterSetName :
SupportsShouldProcess : False
SupportsTransactions : False
NoInvocation : False
Description :
Usage :
Parameters : {}
Examples : {}
OriginalText :
HelpLinks :
OutputHandlers :

[PS7.1.0] $Command = $Command | ConvertTo-Json
[PS7.1.0] $Command
{
  "Verb": "Connect",
  "Noun": "RemoteComputer",
  "OriginalName": null,
  "OriginalCommandElements": null,
  "Aliases": null,
  "DefaultParameterSetName": null,
  "SupportsShouldProcess": false,
  "SupportsTransactions": false,
  "NoInvocation": false,
  "Description": null,
  "Usage": null,
  "Parameters": [],
  "Examples": [],
  "OriginalText": null,
  "HelpLinks": null,
  "OutputHandlers": null
}

In this code example, we essentially do the same thing. We create a PowerShell object and then convert it to JSON. This is the addition of a new parameter.

[PS7.1.0] $Parameter = New-ParameterInfo -Name ComputerName -OriginalName /v
[PS7.1.0] $Parameter

ParameterType                   : object
Position                        : 2147483647
Name                            : ComputerName
OriginalName                    : /v
OriginalText                    :
Description                     :
DefaultValue                    :
DefaultMissingValue             :
AdditionalParameterAttributes   :
Mandatory                       : False
ParameterSetName                :
Aliases                         :
OriginalPosition                : 0
ValueFromPipeline               : False
ValueFromPipelineByPropertyName : False
ValueFromRemainingArguments     : False
NoGap                           : False

[PS7.1.0] $Parameter = $Parameter | ConvertTo-Json
[PS7.1.0] $Parameter
{
  "ParameterType": "object",
  "Position": 2147483647,
  "Name": "ComputerName",
  "OriginalName": "/v",
  "OriginalText": null,
  "Description": null,
  "DefaultValue": null,
  "DefaultMissingValue": null,
  "AdditionalParameterAttributes": null,
  "Mandatory": false,
  "ParameterSetName": null,
  "Aliases": null,
  "OriginalPosition": 0,
  "ValueFromPipeline": false,
  "ValueFromPipelineByPropertyName": false,
  "ValueFromRemainingArguments": false,
  "NoGap": false
}

And in this code example, we essentially do the same thing, too. This PowerShell object converted to JSON has to do with Usage. I’m still working that one over in my mind, but we’ll take a closer look.

[PS7.1.0] $Usage = New-UsageInfo -usage 'Runs Remote Desktop Connection'
[PS7.1.0] $Usage

Synopsis                       SupportsFlags HasOptions
--------                       ------------- ----------
Runs Remote Desktop Connection False         False

[PS7.1.0] $Usage = $Usage | ConvertTo-Json
[PS7.1.0] $Usage
{
  "Synopsis": "Runs Remote Desktop Connection",
  "SupportsFlags": false,
  "HasOptions": false,
  "OriginalText": null
}

Finally, we create a comment-based help example and ensure it’s in the JSON format.

[PS7.1.0] $Example = New-ExampleInfo -command Connect-RemoteComputer -originalCommand 'C:\Windows\System32\mstsc.exe' -description 'Wraps Remote Desktop Connection' 
[PS7.1.0] $Example

Command                OriginalCommand               Description
-------                ---------------               -----------
Connect-RemoteComputer C:\Windows\System32\mstsc.exe Wraps Remote Desktop Connection

[PS7.1.0] $Example = $Example | ConvertTo-Json
[PS7.1.0] $Example
{
  "Command": "Connect-RemoteComputer",
  "OriginalCommand": "C:\\Windows\\System32\\mstsc.exe",
  "Description": "Wraps Remote Desktop Connection"
}

Next, we have to manually put these pieces of JSON together into our single, mstsc.Crescendo.json file. Let’s do that next. We’ll begin with the Parameter JSON we created and add to it the Command JSON. In the Command JSON section, we had an entry that said "Parameters": [], In between the square brackets, we have to add the Parameter JSON. This has been done below.

{
    "Verb": "Connect",
    "Noun": "RemoteComputer",
    "OriginalName": null,
    "OriginalCommandElements": null,
    "Aliases": null,
    "DefaultParameterSetName": null,
    "SupportsShouldProcess": false,
    "SupportsTransactions": false,
    "NoInvocation": false,
    "Description": null,
    "Usage": null,
    "Parameters": [
        {
            "ParameterType": "object",
            "Position": 2147483647,
            "Name": "ComputerName",
            "OriginalName": "/v",
            "OriginalText": null,
            "Description": null,
            "DefaultValue": null,
            "DefaultMissingValue": null,
            "AdditionalParameterAttributes": null,
            "Mandatory": false,
            "ParameterSetName": null,
            "Aliases": null,
            "OriginalPosition": 0,
            "ValueFromPipeline": false,
            "ValueFromPipelineByPropertyName": false,
            "ValueFromRemainingArguments": false,
            "NoGap": false
        }
    ],
    "Examples": [],
    "OriginalText": null,
    "HelpLinks": null,
    "OutputHandlers": null
}

Next, we’ll add our Usage JSON to its place in our mstsc.Crescendo.json file

{
    "Verb": "Connect",
    "Noun": "RemoteComputer",
    "OriginalName": null,
    "OriginalCommandElements": null,
    "Aliases": null,
    "DefaultParameterSetName": null,
    "SupportsShouldProcess": false,
    "SupportsTransactions": false,
    "NoInvocation": false,
    "Description": null,
    "Usage": {
        "Synopsis": "Runs Remote Desktop Connection",
        "SupportsFlags": false,
        "HasOptions": false,
        "OriginalText": null
    },
    "Parameters": [
        {
            "ParameterType": "object",
            "Position": 2147483647,
            "Name": "ComputerName",
            "OriginalName": "/v",
            "OriginalText": null,
            "Description": null,
            "DefaultValue": null,
            "DefaultMissingValue": null,
            "AdditionalParameterAttributes": null,
            "Mandatory": false,
            "ParameterSetName": null,
            "Aliases": null,
            "OriginalPosition": 0,
            "ValueFromPipeline": false,
            "ValueFromPipelineByPropertyName": false,
            "ValueFromRemainingArguments": false,
            "NoGap": false
        }
    ],
    "Examples": [],
    "OriginalText": null,
    "HelpLinks": null,
    "OutputHandlers": null
}

And finally, we’ll add our Example JSON.

{
    "Verb": "Connect",
    "Noun": "RemoteComputer",
    "OriginalName": null,
    "OriginalCommandElements": null,
    "Aliases": null,
    "DefaultParameterSetName": null,
    "SupportsShouldProcess": false,
    "SupportsTransactions": false,
    "NoInvocation": false,
    "Description": null,
    "Usage": {
        "Synopsis": "Runs Remote Desktop Connection",
        "SupportsFlags": false,
        "HasOptions": false,
        "OriginalText": null
    },
    "Parameters": [
        {
            "ParameterType": "object",
            "Position": 2147483647,
            "Name": "ComputerName",
            "OriginalName": "/v",
            "OriginalText": null,
            "Description": null,
            "DefaultValue": null,
            "DefaultMissingValue": null,
            "AdditionalParameterAttributes": null,
            "Mandatory": false,
            "ParameterSetName": null,
            "Aliases": null,
            "OriginalPosition": 0,
            "ValueFromPipeline": false,
            "ValueFromPipelineByPropertyName": false,
            "ValueFromRemainingArguments": false,
            "NoGap": false
        }
    ],
    "Examples": [
        {
            "Command": "Connect-RemoteComputer",
            "OriginalCommand": "C:\\Windows\\System32\\mstsc.exe",
            "Description": "Wraps Remote Desktop Connection"
        }
    ],
    "OriginalText": null,
    "HelpLinks": null,
    "OutputHandlers": null
}

I don’t think it’s there yet, but one day there will likely be a command that does all this for us. Maybe there already is and somehow I’ve overlooked it. With this JSON created and saved as mstsc.Crescendo.json, we can attempt to use it with Export-CrescendoModule as we did in Part I.

[PS7.1.0] Export-CrescendoModule -ConfigurationFile 'C:\Users\tommymaynard\Documents\PowerShell\Modules\Microsoft.PowerShell.Crescendo\0.4.1\Samples\mstsc.Crescendo.json' -ModuleName 'RemoteComputer.psm1'

If you try this, you’ll notice it fails.

This is because the New-CrescendoCommand didn’t include an OriginalName parameter and therefore our JSON didn’t include a much-needed value. Without it, the command doesn’t point to an existing command on the computer, which becomes the reason behind the above error message. Even though it throws an error, it still creates the file (although it’s missing much of the good stuff). You’ll have to remove the file or you’ll get another error about the file already existing.

Our JSON ends up with this: “OriginalName”: null, instead of this: “OriginalName”:”/Windows/System32/mstsc.exe”,. As you can see below, I’ve added this in, so the code here is complete.

{
    "Verb": "Connect",
    "Noun": "RemoteComputer",
    "OriginalName": "/Windows/System32/mstsc.exe",
    "OriginalCommandElements": null,
    "Aliases": null,
    "DefaultParameterSetName": null,
    "SupportsShouldProcess": false,
    "SupportsTransactions": false,
    "NoInvocation": false,
    "Description": null,
    "Usage": {
        "Synopsis": "Runs Remote Desktop Connection",
        "SupportsFlags": false,
        "HasOptions": false,
        "OriginalText": null
    },
    "Parameters": [
        {
            "ParameterType": "object",
            "Position": 2147483647,
            "Name": "ComputerName",
            "OriginalName": "/v",
            "OriginalText": null,
            "Description": null,
            "DefaultValue": null,
            "DefaultMissingValue": null,
            "AdditionalParameterAttributes": null,
            "Mandatory": false,
            "ParameterSetName": null,
            "Aliases": null,
            "OriginalPosition": 0,
            "ValueFromPipeline": false,
            "ValueFromPipelineByPropertyName": false,
            "ValueFromRemainingArguments": false,
            "NoGap": false
        }
    ],
    "Examples": [
        {
            "Command": "Connect-RemoteComputer",
            "OriginalCommand": "C:\\Windows\\System32\\mstsc.exe",
            "Description": "Wraps Remote Desktop Connection"
        }
    ],
    "OriginalText": null,
    "HelpLinks": null,
    "OutputHandlers": null
}

Have fun and keep watching for newer versions. I suspect I will.

Simple Simple Microsoft Crescendo Example

Edit: There’s a Part II now!

There’s a newer module that been discussed a few times in the written form, as well as in at least one podcast I listened to recently. Jason Helmick, an MVP turned Microsoft employee, has been notifying the PowerShell community about the Microsoft Crescendo PowerShell module. And he should be. It’s a unique idea for wrapping native system commands as PowerShell commands. I went looking for the easiest possible example and instead of finding that, I ended up here, writing about my first experience with the module.

Before I show you what I did, let me link a few posts about Crescendo. There was this one from Jason himself and a couple from Jim Truher: Part 1 and Part 2. Jim did the development of this module. At the time of this writing they aren’t taking PRs, but here’s the project on Github, too. And then somehow, I ended up watching this on YouTube with Jason and Jim.

The first, first thing I did was install the module from the PowerShell Gallery using the below command. I did that some time ago actually, but you know. I did, however, ensure that there wasn’t a newer version before beginning using Find-Module .

[PS7.1.0] C:\> Install-Module -Name Microsoft.PowerShell.Crescendo

The second, first thing I did was go to “C:\Users\tommymaynard\Documents\PowerShell\Modules\Microsoft.PowerShell.Crescendo\0.4.1\Samples” and copy and paste one of the JSON file examples. I renamed it to mstsc.Crescendo.json. I don’t believe this is the traditional way this is done, but… it was me experimenting with the module. The mstsc.exe executable is used for RDC or Remote Desktop Connection. If you’re like me, you probably call it RDP. I replaced everything in the file with what’s included below. I don’t recall which of the examples I copied from, but I removed one of the parameters, as that one had two and I was only interested in including one for now. Based on the structure of the below JSON you can get an idea of what’s happening.

{
    "$schema" : "./Microsoft.PowerShell.Crescendo.Schema.json",
    "Verb": "Connect",
    "Noun": "RemoteComputer",
    "OriginalName":"/Windows/System32/mstsc.exe",
    "Parameters": [
        {
            "Name": "ComputerName",
            "OriginalName": "/v",
            "ParameterType": "string"
        }
    ]
}

The schema file is used to ensure what’s entered into this JSON file, my mstsc.Crescendo.json file, is correct. The verb is, well the verb I wish to use. Make sure you use an approved verb. It checks against the Schema.json file for approved verb compliance. There’s a noun that’s needed, as well as the path to the native file we’re wrapping. After that is the single parameter I made available for use with this command. There’s plenty of mstsc switches, but I only ever use /v. Perhaps it’s an odd choice for testing, I don’t know, but it was the first to come to me for something simple, simple to try.

In the above JSON, and still in regards to the single parameter I included, I’ve used ComputerName for the parameter name which will stand in for /v. Additionally, I’ve indicated that the parameter type accepts a string. Therefore, if there’s a parameter value included with the parameter it should expect it’s a string value.

Once that portion was complete, I saved and closed my file and ran the Export-CrescendoModule command to create my module file — a .psm1 file. I didn’t see a way to avoid this, but this command will create the module file inside your current directory. Keep that in mind. I didn’t test with Out-File, but perhaps that would be an option for directing the output.

[PS7.1.0] C:\> Export-CrescendoModule -ConfigurationFile 'C:\Users\tommymaynard\Documents\PowerShell\Modules\Microsoft.PowerShell.Crescendo\0.4.1\Samples\mstsc.Crescendo.json' -ModuleName 'RemoteComputer.psm1'

Once the module file has been created, it’s time to import it and use it. Here’s my first go using my new module after my copy, paste, edit, and export method. Notice what happens when I don’t include the ComputerName parameter and value. It simply opens RDP with the last computer and user name. Helpful, but not exactly what I was after.

Here’s my second go at using the module’s Connect-RemoteComputer command. In this example, I included the ComputerName parameter and a value. As it’s not a computer that I’ve ever connected to, it’s prompting me to ensure I trust it. If you use this command with computers that you’ve already trusted, it’ll begin the RDP connection immediately. Perfect — just as I had expected.

A couple of things. This wasn’t a typical example. I think the idea behind the Crescendo module is to make command-line tools — like, strictly command-line tools — act more like PowerShell. I’ve been running mstsc from my command line for so long that it was one of the first command that came to mind. Also, I think this is going to be a Part I of at least one more post. I’d like to try another command — look at this list! Additionally, based on the other command names in the Crescendo module, there appears to be a better way to start a new project that doesn’t include copying and pasting a sample file. I’m going to do a little more experimentation and get back to this if I can. Working with the other cmdlets in the module hasn’t been as straightforward as I had hoped, but I’ll know more as the weekend progresses.

Edit: There’s a Part II now!