Research the tool’s syntax and output

Start with command-line help and documentation

Many command-line tools include an option to display help content. Most modern command-line tools provide multiple levels of help for the various use-case scenarios the tool provides. For example, running vssadmin.exe /? displays the top-level help, which contains a list of subcommands.

PS> vssadmin /?
vssadmin 1.1 - Volume Shadow Copy Service administrative command-line tool
(C) Copyright 2001-2013 Microsoft Corp.

---- Commands Supported ----

Delete Shadows        - Delete volume shadow copies
List Providers        - List registered volume shadow copy providers
List Shadows          - List existing volume shadow copies
List ShadowStorage    - List volume shadow copy storage associations
List Volumes          - List volumes eligible for shadow copies
List Writers          - List subscribed volume shadow copy writers
Resize ShadowStorage  - Resize a volume shadow copy storage association

Each of the subcommands might have their own subcommands and parameters. For example, the List Shadows subcommand has three possible parameters.

PS> vssadmin List Shadows /?
vssadmin 1.1 - Volume Shadow Copy Service administrative command-line tool
(C) Copyright 2001-2013 Microsoft Corp.

List Shadows [/For=ForVolumeSpec] [/Shadow=ShadowId|/Set=ShadowSetId]
    - Displays existing shadow copies on the system.  Without any options,
    all shadow copies on the system are displayed ordered by shadow copy set.
    Combinations of options can be used to refine the list operation.
    - The Shadow Copy ID can be obtained by using the List Shadows command.
    When entering a Shadow ID, it must be in
    the following format:
       {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
    where the X's are hexadecimal characters.

    Example Usage:  vssadmin List Shadows
                             /Shadow={c5946237-af12-3f23-af80-51aadb3b20d5}

Use the command-line help to discover the possible use cases. You can redirect the output of each help command to a file that you can use for later reference as you create your Crescendo cmdlets.

Capture example output for parsing

Once you have decided which of the tool’s commands to amplify, collect sample output from those commands. Redirect the output to a file for each command. Use this example data to help you design the output handlers (parsers) for your Crescendo cmdlets.

Here is an example of the output from the vssadmin List Shadows command. Notice that the output is well-formatted and contains markers that separate the data fields. Those markers can be used to parse the information as you construct your objects for output.

PS> vssadmin List Shadows
vssadmin 1.1 - Volume Shadow Copy Service administrative command-line tool
(C) Copyright 2001-2013 Microsoft Corp.

Contents of shadow copy set ID: {43dd2d4b-3a58-487a-842d-564143d0501f}
   Contained 1 shadow copies at creation time: 9/10/2021 5:49:26 PM
      Shadow Copy ID: {eef18c41-c6bc-40bb-9863-5491249d2458}
         Original Volume: (C:)\\?\Volume{67a44989-8413-4a7c-a616-79385dae8605}\
         Shadow Copy Volume: \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy3
         Originating Machine: ampersand.redmond.corp.microsoft.com
         Service Machine: ampersand.redmond.corp.microsoft.com
         Provider: 'Microsoft Software Shadow Copy provider 1.0'
         Type: ClientAccessibleWriters
         Attributes: Persistent, Client-accessible, No auto release, Differential, Auto recovered

Contents of shadow copy set ID: {3872a791-51b6-4d10-813f-64b4beb9f935}
   Contained 1 shadow copies at creation time: 9/14/2021 12:26:49 PM
      Shadow Copy ID: {c17ebda1-5da3-4f4a-a3dc-f5920c30ed0f}
         Original Volume: (C:)\\?\Volume{67a44989-8413-4a7c-a616-79385dae8605}\
         Shadow Copy Volume: \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy5
         Originating Machine: ampersand.redmond.corp.microsoft.com
         Service Machine: ampersand.redmond.corp.microsoft.com
         Provider: 'Microsoft Software Shadow Copy provider 1.0'
         Type: ClientAccessibleWriters
         Attributes: Persistent, Client-accessible, No auto release, Differential, Auto recovered

As you inspect the sample output, think about the types of data returned. As you construct your objects you should convert the strings output by the command-line tool to .NET types. For example, you can convert timestamp information to .NET [DateTime] types.

Create a script to parse the output for each command

The ParseShadow.ps1 script is an example of how to parse the output from vssadmin List Shadows. This script becomes the output handler for the Crescendo cmdlet that you create. Crescendo passes the output of the native command to the output handler as a single string. The script breaks the output into blocks of text separated by blank lines. Each block represents an instance of a Shadow. The block is broken into lines and each line is analyzed to extract the data value and added to a PowerShell object.

# ParseShadow.ps1 script file
    param(
        [Parameter(Mandatory)]
        $cmdResults
    )
    # Split output into blocks of text separated by blank lines
    $textBlocks = ($cmdResults | Out-String) -split "`r`n`r`n"

    # For each block of text, parse the lines and create a custom object
    foreach ($block in $textBlocks) {
        if ($block -ne '') {
            $hash = [ordered]@{}
            # Split the block into lines
            $lines = ($block -split "`r`n").Trim()

            foreach ($line in $lines) {
                switch -regex ($line) {
                    'set ID:' {
                        $id = [guid]$line.Split(':')[1].Trim()
                        $hash.Add('SetId',$id)
                        break
                    }
                    'creation time:' {
                        $datetime = [datetime]($line -split 'time:')[1]
                        $hash.Add('CreateTime',$datetime)
                        break
                    }
                    'Copy ID:' {
                        $id = [guid]$line.Split(':')[1].Trim()
                        $hash.Add('CopyId',$id)
                        break
                    }
                    'Original Volume:' {
                        $value = ($line -split 'Volume:')[1].Trim()
                        if ($value -match '^\((?<name>[A-Z]:)\)(?<path>\\{2}.+\\$)') {
                            $volinfo = [pscustomobject]@{
                                Name = $Matches.name
                                Path = $Matches.path
                            }
                        }
                        $hash.Add('OriginalVolume',$volinfo)
                        break
                    }
                    'Copy Volume:' {
                        $hash.Add('ShadowCopyVolume', $line.Split(':')[1].Trim())
                        break
                    }
                    'Machine:' {
                        $parts = $line.Split(':')
                        $hash.Add($parts[0].Replace(' ',''), $parts[1].Trim())
                        break
                    }
                    'Provider:' {
                        $hash.Add('ProviderName',$line.Split(':')[1].Trim(" '"))
                        break
                    }
                    'Type:' {
                        $hash.Add('Type',$line.Split(':')[1].Trim())
                        break
                    }
                    'Attributes' {
                        $attrlist = $line.Split(': ')[1]
                        $hash.Add('Attributes',$attrlist.Split(', '))
                        break
                    }
                }
            }
            [pscustomobject]$hash
        }
    }