My understanding is that splatting variables is the preferred/recommended way to make longer function calls in PowerShell scripts. However, I use vscode as my primary IDE and understandably, extracting the parameters into a hashtable and splatting them makes intellisense unusable.
Is there any library/framework/vscode extension for splatting that allows the use of intellisense by way of naming convention or something like that?
I know this is a relatively old question but whilst looking for similar I came across Editor Services Command Suite which looks like it may be useful. It allows you to write the command out and then convert it to a splatted version:
ESCS github repo
It was this blog post by Rob Sewell (sqldbawithabeard) which brought it to my attention: blog post
Related
I'm new to PowerShell scripting and am looking to create ps1 scripts that I can used as cmdlets. My background is in using strongly typed variables, but I'm struggling to find how (or if) it is possible to ensure that all user variables in a script are explicitly typed. Some languages only allow explicitly typed variables. VBA allows the directive "Option Explicit" and I was hoping to find some way to achieve the same in any PSH scripts I create.
I've done a lot of searching (google, stackoverflow etc.) but not found anything. If there is no way to force all variable definitions to be explicitly typed, I'll have to write a cmdlet to parse my scripts to find any implicitly typed variables ... but hoping for a better solution.
I don't think you can do that in PowerShell. Closest you can get is to use Set-StrictMode which will, among other things, prohibit use of uninitialized variables.
But if you want to parse the scripts, maybe don't write your own solution. Use PSScriptAnalyzer module. It has a lot of built-in rules, unfortunately none for checking explicit types. But you can define your own rules, and maybe someone already created the one you're looking for and posted it somewhere.
9/10 times if you are trying to use the Invoke-Expression cmdlet, there is a better way. Building the arguments to a command dynamically? Use an array of arguments. Building the arguments to a cmdlet? Use splatting with an array or hashtable. Your command has a space in the path to it? Use the call operator (&).
This might seem open ended, but Invoke-Expression is an easily accessible cmdlet where the answer is almost always to never use it. But the cmdlet exists for a reason, is not deprecated, and most criticisms of its use state something similar to, "it's almost never the right answer", but never states when it is acceptable to use it. In what case is it acceptable to use Invoke-Expression? Or to word it a bit less openly, how was Invoke-Expression designed to be used?
To quote from a PowerShell team blog post titled Invoke-Expression considered harmful (emphasis added):
The bottom line: Invoke-Expression is a powerful and useful command for some scenarios such as creating new scripts at runtime, but in general, if you find yourself using Invoke-Expression, you should ask yourself, or maybe a respected colleague if there is a better way.
EBGreen notes:
Or to phrase it another way, It [Invoke-Expression] is ok to use as long as a user is never involved in any part of generating the string that will be invoked. But even then, not using it will enforce better habits than using it would.
In short:
As a matter of habit, always consider a different (usually more robust and secure) solution first.
If you do find that Invoke-Expression is your only choice, carefully consider the security implications: if a string from an (untrusted) outside source (e.g., user input) is passed directly to Invoke-Expression, arbitrary commands may be executed.
Therefore: Only use Invoke-Expression if you fully control or implicitly trust the input.
Note: As of Windows PowerShell v5.1 / PowerShell Core v6.1.0, the official Invoke-Expression help topic doesn't provide such guidance; this GitHub issue suggests rectifying that.
Rare examples of justified (safe) use of Invoke-Expression:
Creating PSv5+ custom classes dynamically:
so that the class can be used in a remote session.
so that the set of properties can be created based on conditions at runtime.
Using Invoke-Expression in combination with Write-Output:
to parse a string with embedded quoting, with extra precautions.
to parse command lines stored in a file, if trusted.
Using Invoke-Expression for nested property access:
via a property path stored in a string.
While I did find a lot of information on how to name Cmdlets and functions in the Cmdlet Development Guidelines I did not find any information on whether functions should be named in upper or in lower case.
What is the convention here?
(I do understand that Cmdlets themselves are generally named in upper case, even though they are not case-sensitive when it comes to executing.)
Naming convention can be tricky. While a fixed naming convention may provide aesthetics or simplified usage, it is not required to be followed. In general, a naming convention that I advocate for is the one that is used already in Powershell. As functions are created on verb-noun base, each word starts with a capital letter or if it is an abbreviation - all capitals, or if it is a proprietary - then as it is accordingly.
I have, for example, created some functions for myself:
Get-ServerDiag
Mount-TrueCryptVolumes
Start-RDP
Generate-RandomPassword
Nuke-Environment
You can imagine what these functions do, it is rather clear, straightforward and compliant with built-in Powershell functions. I do however have exceptions which come from "importing" a several Unix commands to Powershell (like killall, pidof etc...) You can always use a Set-Alias if you prefer to write something else.
This question, however important, is discussable as there does not seem to be the 'one, best way'. It is all, in the end, up to personal preferences.
A function is mostly a script-based cmdlet. A cmdlet is written in ex. C#, while a function is written as a script. Because of this similarity, I recommend using the same style as cmdlets for my "standalone-functions" so that it blends in with the other PowerShell cmdlets. Ex. I had a filecount(per folder)-function that I used often. I called it Get-FileCount.
However, I usually name helper functions(functions you only use in other functions) using a simpler name like convertsidtousername etc.
You could use aliases to create short names for a function.
I've got a PowerShell function Download-File, which uses WebClient.DownloadFile -- hence the name.
When I attempt to turn my .ps1 script into a .psm1 module, PowerShell warns me that "Download" is not in the list of recommended verbs.
What's a good alternative? Get- seems to be about getting properties, rather than contents (apart from Get-Content, oddly). Receive- seems a bit too passive for my liking (i.e. the script blocks until the information is sent) -- which doesn't fit well.
Ideas?
For me, 'Get' is the most natural verb. It gets everthing, not just properties. For the noun I would use something like 'WebFile', you can easily guess what you get and where it is coming from.
Wouldn't this work:
Start-Download -Url http://blah/
Or use Invoke-Download, as per the recommendations for synchronous operations. Start is for asynchronous.
What about New-Download or New-WebDownload or New-DownloadFile??
Maybe Request-TeamCityArtifacts?
https://learn.microsoft.com/en-us/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands?view=powershell-7.2
There is no synonym for download.
Copy is the closest I've found, being documented as a synonym of the unapproved Clone.
Reading the Microsoft documentation for creating PowerShell Cmdlets, I notice there is no example code for F#; instead, there is a message saying that "This language is not supported or no code example is available.".
Is F# not supported for creating Cmdlets, or aren't there any examples in F#?
Take a look at this:
Writing PowerShell Cmdlets in F#. Not exactly a complete example of how to do this but it certainly seems to be possible to write cmdlets in F#.
EDIT:
There is a more extensive example here but the contextual information is a little bit out of date. Actually the contextual information is a bit out of date on both links but the first link is more recent.