Alternative to "Sort" as a PowerShell verb? - powershell

I have a PowerShell function Sort-VersionLabels. When I add this function to a module, Import-Module complains:
WARNING: Some imported command names include unapproved verbs which might make
them less discoverable. Use the Verbose parameter for more detail or type
Get-Verb to see the list of approved verbs.
According to this, Sort is a "reserved verb".
What could be a good (and approved) alternative?
Update
The function takes a array of version numbers in the form: <major>.<minor>.<revision>[-<milestone[nr]>]. Milestone can be dev, alpha, beta or stable (in that order). So the standard Sort-Object function won't work.
It outputs the sorted array to the pipe line.

I think something like ConvertTo-SortedVersionLabels, while a little bit awkward, uses an approved and non-reserved verb but is still clear.
You could also make sorting a parameter to a different function, like Get-VersionLabels -Sorted.
How you would work that in depends on your module as a whole and whether you have such a function to modify. It's unclear from your current post, but if you edit it with more details we might be able to provide more suggestions.

The core of this issue will generate opinionated results. This creates a conundrum since you are looking for something specific that the current answers have been unable to address. I understand that you are looking for a solution that logically fits your function while being in the standard verb list, which is admirable. To continue from an earlier comment I made I am going to try and state a case for all the approved verbs that might fit your situation. I will refer to the Approved Verbs List linked in your question frequently and will use "AVL" for brevity going forward.
Group: The comments on the AVL refers to using this in place of Arrange. Arrange being a synonym for Sort would be a good fit. Sticking with the recommendation then we should use Group
Set: It is a synonym for Sort. However, in the AVL, it is associated with Write, Reset, Assign, or Configure which are not related to your cmdlet. Still, it is in the list and could fit if you are willing to put aside the discombobulation that it creates with existing PowerShell cmdlets.
I dont really have a number 3.
Update: This is a weak case but the AVL refers its use as a way to maintain [a cmdlets] state [and] accuracy.
Order/Organize: Not in the AVL but I find these very fitting and dont currently conflict with any existing verbs.
Ultimately, AVL be damned and do whatever you want. Sort is a very good fit for what you are trying to do. You can also just use -DisableNameChecking when importing your module. It is only a warning after all. Briatist's answer is also good in my opinion.
Bonus from comments
Not that you asked for it, but when you said we have to enable name checking I thought about this. Just for fun!
$reservedVerbs = "ForEach","Format","Group","Sort","Tee"
$approvedVerbList = (Get-Verb).Verb
Get-Command -Module Microsoft.WSMan.Management | ForEach-Object{
If ($approvedVerbList -notcontains ($_.Name -split "-")[0]){
Write-Warning "$($_.Name) does not use an approved verb."
}
If ($reservedVerbs -contains ($_.Name -split "-")[0]){
Write-Warning "$($_.Name) is using a reserved verb."
}
}

Whenever I need a verb that is not an approved PowerShell verb, I use Invoke-* instead. So in your case, you could name it Invoke-SortVersionLabels

You shouldn't need a special cmdlet at all. If a VersionLabel is an object, just take the collection and pipe it to Sort-Object using the property(ies) you need.
# Assuming a versionlabel has a 'Name' Property...
$VersionLabelCollection | Sort-Object -Property:Name

Related

PowerShell - How to locate an object(s) properties and methods? (They are not in the help files)

I have a few questions about the following code.
Get-DBAAgentJob -SqlInstance *instancename* | Where-Object { $_.HasSchedule -Match "False" }| Out-GridView
In the Where-Object, there is $.HasSchedule. What is '$.HasSchedule'? I looked in the help for Where-Object and online and I don't understand what that is. Is it a function?
What does this syntax signify/do $_ ?
What all can I filter for in Where-Object other than .HasSchedule? Also,where I can find out how to figure that out please? If it's not in help or books online or a google search, I'm not sure. My google search algorithm is probably not good enough to get me in the ballpark.
I'm curious what are all the things I can filter on in the Where-Object in this line of code. For example, instead of has schedule, if I wanted to look where the job is not enabled, is there a .NotEnabled?
Thanks for the help.
The Where-Object clause is a way to filter objects returned from a cmdlet on a certain property.
In your example, it is filtering objects on the HasSchedule property. The example's filter says this property needs to be False in order for the objects to get piped through the pipeline where the next cmdlet takes them as input.
It tests the objects using the $_ Automatic variable, which represents each object in sequence that is coming in from the Get-DbaAgentJob cmdlet.
Usually, to find out what an object would look like, you can simply google for it.
In this case, if you look for Get-DBAAgentJob, you will find this page, where you can look at the function itself.
Here you can find what properties each returned object has:
ComputerName, InstanceName, SqlInstance, Name, Category, OwnerLoginName, IsEnabled, LastRunDate, DateCreated, HasSchedule, OperatorToEmail.
As you can see, there is a property IsEnabled, so you can filter on Not enabled with
Where-Object { -not $_.IsEnabled }
See: PowerShell Logical Operators
If you click the home page for dbatools you'll fine a section called docs where you can learn more.
Browse for free ebooks on PowerShell
Hope that helps
In case someone finds this useful in the future, #Theo's answer was helpful in giving me the base understanding of my question.
I learned more on this today and I will post it to help others in the future.
To answer my question:
The .hasSchedule is one of the many properties of Get-DBAAgentJob.
The . 'dot'. "The most common way to get the values of the properties of an object is to use the dot method." Books Online (BOL)
"All Properties and Methods for a given object are called members... Help files for any given command do not tell you what kinds of objects, properties, and methods are available, the only way to tell is to use the Get-Member' cmdlet. Learning PowerShell Jonathan Hassall
This code will tell you all of the properties and methods for a member, in this case the one I was interested in learning more about.
Get-DBAAgentJob -SqlInstance instancename | get-member
This shows me all the properties and methods available, including hasSchedule and isenabled
BOL: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_properties?view=powershell-7

How to share data between cmdlets in a module?

I'm currently working on a module in PowerShell which uses a standard REST API in the background. For that, I wrote a Connect-Server cmdlet that retrieves an auth key for later calls.
My question is: Is there any best practice regarding sharing the data with other cmdlets? I know I could easily just return it from the Connect function and pass it to the following cmdlet, but that's not what I'm looking for.
Until now, I've been using global variables for that exchange of data. But as I've read in some best practice guidelines you should try not to pollute the global scope.
Other solutions I've seen use Get and Set cmdlets, but I don't think that's the best PowerShell way of doing it.
So are there any other ways of solving that?
The normal way is to return data from one cmdlet and store it either in a variable or forward it to the pipeline. Another way of sharing data might be serializing (ConverTo-Json, ConvertTo-Csv, ...) it to a file (located in e.g. $env:TEMP, or created via New-Temporaryfile), and deserializing it back again in another cmdlet (at cost of DISK I/O). Personally I'm always the result in a variable for lather usage and inject it in the next cmdlet (or use the pipeline).
Using global variables is not the best idea since you don't know on which parameters your cmdlet/function depends on.
So as the guys at PoshCode stated, the best way to do such a thing is using a variable in script scope as this is available for all cmdlets in the module but not visible for users.

Why automation framework require proxy function for every powershell cmdlet?

In my new project team, for each powershell cmdlet they have written proxy function. When i asked the reason for this practice, they said that it is a normal way that automation framework would be written. They also said that If powershell cmdlet is changed then we do not need to worry ,we can just change one function.
I never saw powershell cmdlets functionality or names changed.
For example, In SQL powershell module they previously used snapin then they changed to module. but still the cmdlets are same. No change in cmdlet signature. May be extra arguments would have added.
Because of this proxy functions , even small tasks taking long time. Is their fear baseless or correct? Is there any incident where powershell cmdlets name or parameter changed?
I guess they want to be extra safe. Powershell would have breaking changes here and here sometimes but I doubt that what your team is doing would be impacted by those (given the rare nature of these events). For instance my several years old scripts continue to function properly up to present day (and they were mostly developed against PS 2-3).
I would say that this is overengineering, but I cant really blame them for that.
4c74356b41 makes some good points, but I wonder if there's a simpler approach.
Bear with me while I restate the situation, just to ensure I understand it.
My understanding of the issue is that usage of a certain cmdlet may be strewn about the code base of your automation framework.
One day, in a new release of PowerShell or that module, the implementation changes; could be internal only, could be parameters (signature) or even cmdlet name that changes.
The problem then, is you would have to change the implementation all throughout your code.
So with proxy functions, you don't prevent this issue; a breaking change will break your framework, but the idea is that fixing it would be simpler because you can fix up your own proxy function implementation, in one place, and then all of the code will be fixed.
Other Options
Because of the way command discovery works in PowerShell, you can override existing commands by defining functions or aliases with the same name.
So for example let's say that Get-Service had a breaking change and you used it all over (no proxy functions).
Instead of changing all your code, you can define your own Get-Service function, and the code will use that instead. It's basically the same thing you're doing now, except you don't have to implement hundreds of "empty" proxy functions.
For better naming, you can name your function Get-FrameworkService (or something) and then just define an alias for Get-Service to Get-FrameworkService. It's a bit easier to test that way.
One disadvantage with this is that reading the code could be unclear, because when you see Get-Service somewhere it's not immediately obvious that it could have been overwritten, which makes it a bit less straightforward if you really wanted to call the current original version.
For that, I recommend importing all of the modules you'll be using with -Prefix and then making all (potentially) overridable calls use the prefix, so there's a clear demarcation.
This even works with a lot of the "built-in" commands, so you could re-import the module with a prefix:
Import-Module Microsoft.PowerShell.Utility -Prefix Overridable -Force
TL;DR
So the short answer:
avoid making lots and lots of pass-thru proxy functions
import all modules with prefix
when needed create a new function to override functionality of another
then add an alias for prefixed_name -> override_function
Import-Module Microsoft.PowerShell.Utility -Prefix Overridable -Force
Compare-OverridableObject $a $b
No need for a proxy here; later when you want to override it:
function Compare-CanonicalObject { <# Stuff #> }
New-Alias Compare-OverridableObject Compare-CanonicalObject
Anywhere in the code that you see a direct call like:
Compare-Object $c $d
Then you know: either this intentionally calls the current implementation of that command (which in other places could be overridden), or this command should never be overridden.
Advantages:
Clarity: looking at the code tells you whether an override could exist.
Testability: writing tests is clearer and easier for overridden commands because they have their own unique name
Discoverability: all overridden commands can be discovered by searching for aliases with the right name pattern i.e. Get-Alias *-Overridable*
Much less code
All overrides and their aliases can be packaged into modules

How do I determine all acceptable values for object properties in Powershell?

I've gotten to a point in working with Powershell that I don't need to have a book or reference manual by my side for most tasks. I still have to search here or MSDN from time to time but it is far less frequent nowadays.
A lot of information on Powershell is readily available but you just need to know where to look. And this is where I am stuck.
What I want to do is list all acceptable property values for a given object without having to pull up the MSDN documentation (which is dryer than insulated ceiling tiles). Don't misunderstand me here; I'm not asking to list current or default properties for an object, I just want to list a property of an object and see what options I have available.
Here is an example of what I am not talking about:
New-Object System.Diagnostics.ProcessStartInfo "PowerShell" | gm
or:
(New-Object System.Diagnostics.ProcessStartInfo "Powershell").FileName
Those second one queries the current or default value for that particular named property. The first one will list a table with Name, MemberType, and Definition. Definition will give the type, handle, parameters, and\or {get; set;} (depending on whether the MemberType is a method, property, event, etc).
Say I would like to set that particular property--how do I know what values are valid and which are not?
What I am talking about would be something along these lines:
(New-Object Windows.Forms.Form).FormBorderStyle | gm
or:
$form = New-Object Windows.Forms.Form
$form.FormBorderStyle | gm
For the FormBorderStyle, valid assignable values are: None, FixedSingle, Fixed3D, FixedDialog, Sizable, FixedToolWindow, or SizableToolWindow. The only way I found these was through an the text rendered during an exception. How would I go about finding those values without having to rely on exceptions?
Ideally, I don't want to have to keep searching MSDN, TechNet, or StackOverflow. Is there a cmdlet or query that I can use within Powershell (outside of get-help or man) to give me that information? Get-Help\man aren't very useful when it comes to a smaller and more defined scope.
Without saying "Google it", "RTFM", or "get good", what advice do you all have?
Thanks.
In this specific case, FormBorderStyle is an enum, so this will work:
[enum]::getvalues((New-Object Windows.Forms.Form).FormBorderStyle.gettype())
Quick note: my sentence does not imply that this will not work in other cases. The same syntax is valid for any enum.
Credit to #TheMadTechnician, another possible syntax:
[Windows.Forms.FormBorderStyle].GetEnumNames()
It's also possible to combine both, if you can't bother to look up the enum's name:
(New-Object Windows.Forms.Form).FormBorderStyle.gettype().GetEnumNames()

What are good guidelines for naming PowerShell verbs?

I'm early on in my PowerShell learning, and I'm wondering if there are some good guidelines for verbs in Posh for cmdlets (or advanced functions, whatever they're called in CTP3).
If I do a get-verb I can see the lot of them. But I'm still not sure how I should lay out my modules.
Here's the example I'm running into right now. I have a little script that asks Perforce: if I were to sync, what files would change and how big are they? It outputs a summary of sizes and a mini-tree of folders for where the changes will occur (as well as how many would need resolving).
Is that a query-p4sync? Or is it a 'sync-p4 -whatif'? Or something else?
Before I start writing a lot of these scripts I want to make sure I name them right.
You can find a list of common verbs on MSDN along with a description what they should be used for.
Here's an updated list of approved verbs on the Windows PowerShell Blog, as of July 15.
From your use of the word "modules", I'm going to guess you are using V2 of PowerShell, which allows you to take advantage of Advanced Functions.
Advanced functions provide a way to attribute your function to provide native support for -WhatIf and -Confirm
function Sync-PerforceRepository()
{
[cmdletbinding(SupportShouldProcess=$true)]
param (...) #add your parameters
Begin
{
#setup code here
}
Process
{
if ($pscmdlet.ShouldProcess($ObjectBeingProcessed,"String Describing Action Happening")
{
#Process logic here
}
}
End
{
#Cleanup code
}
}