There is a PowerShell cmdlet Invoke-MgSubscribeGroup that I want to call this way:
Invoke-MgSubscribeGroup -GroupId da2d17a7-64a5-43e5-9d95-7b70333dd78c
#{ UserId = "ed3d927d-7999-459f-955d-2afc272bd4d4" }
(Split into multiple lines for better readability)
When calling it, I get en error message:
A positional parameter cannot be found that accepts argument "System.Collections.Hashtable".
Since I'm not that deep into PowerShell I must have some false understanding of how to pass a hashtable, or I have misunderstood the documentation that says:
...To create the parameters described below, construct a hash table containing the appropriate properties...
My question
What is the correct syntax to call the Invoke-MgSubscribeGroup cmdlet and pass the user ID?
To elaborate on your own answer:
PowerShell's syntax diagrams - available locally via Invoke-MgSubscribeGroup -? or Get-Command -Syntax Invoke-MgSubscribeGroup - contain all the relevant information.
That said, they're not easy to read, especially locally, and there's room for future improvement.
Quoting from Invoke-MgSubscribeGroup's documentation:
Invoke-MgSubscribeGroup
-GroupId <String>
# ...
Invoke-MgSubscribeGroup
-InputObject <IGroupsIdentity>
# ...
Each (partial) quote represents a distinct parameter set, i.e. a unique combination of parameters representing a distinct feature group.
That -GroupId and -InputObject are in different parameter sets and are exclusive to each, tells you that you cannot use them both in a given invocation (they way you mistakenly tried), i.e., that they are mutually exclusive.
Additionally, given that the parameter names -GroupId and -InputObject are not enclosed in [...] means that you can only pass named, not positional arguments to them - that is, you must precede an argument to bind to these parameters with the parameter name; e.g, -GroupId foo rather than just foo.
By convention, a parameter named -InputObject is typically used to represent values that can be supplied via the pipeline, as evidenced by the parameter's description stating Accept pipeline input: True - locally, you can see this with either Get-Help -Full Invoke-MgSubscribeGroup or - parameter-specifically - with Get-Help Invoke-MgSubscribeGroup -Parameter InputObject
Often, multiple input objects can only (meaningfully) be supplied via the pipeline; that is, -InputObject is often a mere implementation detail whose purpose is to facilitate pipeline input - see GitHub issue #4242.
GitHub issue #4135 proposes making syntax diagrams directly reflect which parameters accept pipeline input.
What complicates matters with respect to Invoke-MgSubscribeGroup's documentation, specifically (which seems to be very sparse in general):
The help topic contains no examples (which you could normally request locally with Get-Help -Examples Invoke-MgSubscribeGroup)
The data type of the -InputObject parameter, <IGroupsIdentity> (Microsoft.Graph.PowerShell.Models.IGroupsIdentity) doesn't seem to have its own documentation; it is only described in the "Notes" section of the help topic, as accepting a hashtable (#{ ... }), along with a list of the supported entries (keys and value types).
All that said: you could have passed your hashtable via the pipeline, as follows:
#{
UserId = 'ed3d927d-7999-459f-955d-2afc272bd4d4'
GroupId = 'da2d17a7-64a5-43e5-9d95-7b70333dd78c'
} | Invoke-MgSubscribeGroup
The advantage of this approach over passing an argument to -InputObject is that it would allow you to pass multiple hashtables to act on.
After further investigating and reading the documentation again and again, I've found the correct syntax:
Invoke-MgSubscribeGroup -InputObject #{
UserId = "ed3d927d-7999-459f-955d-2afc272bd4d4";
GroupId = "da2d17a7-64a5-43e5-9d95-7b70333dd78c" }
(Split into multiple lines for better readability)
I now face permission issues, but this is out-of-scope for this question, since I've asked for the correct syntax only.
Related
Given:
PowerShell 5.1
I'm having a little trouble understanding hashtable and splatting. When splatting are use using a hash table to do that or is it something completely different?
I have the following code:
$hashtable1 = #{}
$hashtable1.add('PROD',#{ FirstName = 'John'; LastName = 'Smith'})
function Main() {
$sel = $hashtable1['PROD']
Function1 $sel
Function2 #sel
}
function Function1([hashtable] $x) {
"Value: $($x.LastName)"
$x.FirstName
}
function Function2([string] $firstName) {
"Value: $($firstName)"
}
Main
There's good information in the existing answers, but let me attempt a focused summary:
The answer to your actual question is:
Yes, #{ FirstName = 'John'; LastName = 'Smith' } is a hashtable too, namely in the form of a declarative hashtable literal - just like #{} is an empty hashtable literal (it constructs an instance that initially has no entries).
A hashtable literal consists of zero or more key-value pairs, with = separating each key from its value, and pairs being separated with ; or newlines.
Keys usually do not require quoting (e.g. FirstName), except if they contain special characters such as spaces or if they're provided via an expression, such as a variable reference; see this answer for details.
This contrasts with adding entries to a hashtable later, programmatically, as your $hashtable1.Add('PROD', ...) method call exemplifies (where PROD is the entry key, and ... is a placeholder for the entry value).
Note that a more convenient alternative to using the .Add() method is to use an index expression or even dot notation (property-like access), though note that it situationally either adds an entry or updates an existing one: $hashtable1['PROD'] = ... or $hashtable1.PROD = ...
The answer to the broader question implied by your question's title:
PowerShell's hashtables are a kind of data structure often called a dictionary or, in other languages, associative array or map. Specifically, they are case-insensitive instances of the .NET [hashtable] (System.Collections.Hashtable) type, which is a collection of unordered key-value pair entries. Hashtables enable efficient lookup of values by their associated keys.
Via syntactic sugar [ordered] #{ ... }, i.e. by placing [ordered] before a hashtable literal, PowerShell offers a case-insensitive ordered dictionary that maintains the entry-definition order and allows access by positional index in addition to the usual key-based access. Such ordered hashtables are case-insensitive instances of the .NET System.Collections.Specialized.OrderedDictionary type.
A quick example:
# Create an ordered hashtable (omit [ordered] for an unordered one).
$dict = [ordered] #{ foo = 1; bar = 'two' }
# All of the following return 'two'
$dict['bar'] # key-based access
$dict.bar # ditto, with dot notation (property-like access)
$dict[1] # index-based access; the 2nd entry's value.
Splatting is an argument-passing technique that enables passing arguments indirectly, via a variable containing a data structure encoding the arguments, which is useful for dynamically constructing arguments and making calls with many arguments more readable.
Typically and robustly - but only when calling PowerShell commands with declared parameters - that data structure is a hashtable, whose entry keys must match the names of the target command's parameters (e.g., key Path targets parameter -Path) and whose entry values specify the value to pass.
In other words: This form of splatting uses a hashtable to implement passing named arguments (parameter values preceded by the name of the target parameter, such as -Path /foo in direct argument passing).
A quick example:
# Define the hashtable of arguments (parameter name-value pairs)
# Note that File = $true is equivalent to the -File switch.
$argsHash = #{ LiteralPath = 'C:\Windows'; File = $true }
# Note the use of "#" instead of "$"; equivalent to:
# Get-ChildItem -LiteralPath 'C:\Windows' -File
Get-ChildItem #argsHash
Alternatively, an array may be used for splatting, comprising parameter values only, which are then passed positionally to the target command.
In other words: This form of splatting uses an array to implement passing positional arguments (parameter values only).
This form is typically only useful:
when calling PowerShell scripts or functions that do not formally declare parameters and access their - invariably positional - arguments via the automatic $args variable
when calling external programs; note that from PowerShell's perspective there's no concept of named arguments when calling external programs, as PowerShell's knows nothing about the parameter syntax in that case, and all arguments are simply placed on the process command line one by one, and it is up to the target program to interpret them as parameter names vs. values.
A quick example:
# Define an array of arguments (parameter values)
$argsArray = 'foo', 'bar'
# Note the use of "#" instead of "$", though due to calling an
# *external program* here, you may use "$" as well; equivalent to:
# cmd /c echo 'foo' 'bar'
cmd /c echo #argsArray
#postanote has some very good links about hashtables and splatting and are good reads. Taking your examples, you have two different functions. One to handle hashtables as a parameter, and the second one that can only handle a single string parameter. The hashtable cannot be used to pass parameters to the second function, e.g.:
PS C:\> Function2 $sel
Value: System.Collections.Hashtable
Conceptually, the real difference between using hashtables and splatting is not about how you are using them to pass information and parameters to functions, but how the functions and their parameters receive the information.
Yes, certain functions can have hashtables and arrays as parameters, but, typically in 98% of the cases, functions don't use hashtables as a named parameter to get its values.
For ex. Copy-Item doesn't use hash tables as a parameter. If it did, would you want to do this every time you want to copy anything:
$hashtable = #{
Path = "C:\Temp\myfile.txt",
Destination = "C:\New Folder\"
}
Copy-Item -Parameters $hashtable
No, instead, you want the parameters as strings, so you can make it a much easier one liner:
Copy-Item -Path "C:\Temp\myfile.txt" -Destination "C:\New Folder\"
It makes more sense to most people to deal with individual strings as opposed to a generic, large, hashtable "config" to pass. Also, by separating the parameters as separate strings, integers, floats, chars, etc. it is also easier to do validation, default values, mandatory/not mandatory parameters, etc.
Now despite saying all that, there is a situation where you have certain functions with lots of parameters (e.g. sending an email message), or you want to do something multiple times (e.g. copying/moving lots of files from a CSV file). In that case, using a hashtable, and/or arrays, and/or arrays of hashtables, would be useful.
This is where splating comes in. It takes a hashtable and instead of treating it like you are passing a single value (i.e. why Function2 $sel returns System.Collections.Hashtable), the # signt tells PowerShell that it is a collection of values, and to use the collection to try and match to the parameters of the function. That's why passing the hashtable to Function2 doesn't work, but splatting works e.g.:
PS C:\> Function2 #sel
Value: John
In this case, it takes the hashtable $sel and by using splatting #sel PowerShell now knows to not pass the hashtable as-is, but to open up the collection and to matche the $sel.FirstName to the -Firstname parameter.
In this answer the author proposed the following snippet:
dir -Path C:\FolderName -Filter *.fileExtension -Recurse | %{$_.FullName}
I can understand the majority of it, but I'm unable to search documentation for the last part. The output of the search is piped | and used in %{} and as $_.
I have experimented around it, %{} is a for-each statement I believe, bing search was not effective. $_ is also somewhat magic: it is a variable, with no name and thus immediately consumed? I don't care much for the .FullName, that part I sorted out. Again, bing search was not effective, nor searching for those char sequences in PowerShell docs.
Can anybody explain it to me?
%{} is not "a thing" - it's two things: % and {}
% is an alias for the ForEach-Object cmdlet:
PS ~> Get-Alias '%'
CommandType Name Version Source
----------- ---- ------- ------
Alias % -> ForEach-Object
... so it resolves to:
... |ForEach-Object { $_.FullName }
ForEach-Object is basically PowerShell's map function - it takes input via the pipeline and applies the operation described in the {} block to each one of them.
$_ is an automatic reference to the current pipeline input item being processed
You can think of it a bit like a foreach($thing in $collection){} loop:
1..10 |ForEach-Object { $_ * 10 }
# produces the same output as
foreach($n in 1..10){
$n * 10
}
Except we can now stick our loop in the middle of a pipeline and have it produce output for immediate consumption:
1..10 |ForEach-Object { $_ * 10 } |Do-SomethingElse
ForEach-Object is not the only thing that makes use of the $_ automatic variable in PowerShell - it's also used for pipeline-binding expressions:
mkdir NewDirectory |cd -Path { $_.FullName }
... as well as property expressions, a type of dynamic property definition supported by a number of cmdlets like Sort-Object:
1..10 |Sort-Object { -$_ } # sort in descending order without specifying -Descending
... Group-Object:
1..10 |Group-Object { $_ % 3 } # group terms by modulo congruence
... and Select-Object:
1..10 |Select-Object #{Name='TimesTen';Expression={$_ * 10}} # Create synthetic properties based on dynamic value calculation over input
To complement Mathias' answer, which explains the specific constructs well, with how you could / couldn't have discovered this information yourself, using PowerShell's own help system:
Relevant help topics and use of the help system:
Note: To get an overview of all aspects of PowerShell's help system, simply run help.
% is a built-in alias for the ForEach-Object cmdlet:
Use Get-Help ForEach-Object to view the help topic in the terminal.
If no local topics are found, you must download them via the Update-Help cmdlet.
Tips:
Add the -Online switch to open the (potentially more current) online version of the topic in your browser.
You can bootstrap your use of Get-Help with Get-Help Get-Help (or even help help):
Cmdlet-specific help comes in detail levels: terse (default, shows the syntax and overview description only), -Detailed (includes parameter descriptions and example commands) and -Full (additionally includes technical parameter information and extended notes).
-Examples can be used to show example commands only.
With keyword-based search (see below), you can limit results to topics of a certain category with the -Category parameter.
For convenience, you can also use the built-in help function, which wraps Get-Help calls with display paging (simply put: by piping the output to the more utility) and defaults to detail level -Full.
{...} is a script block literal, a block of arbitrary PowerShell code that can be invoked on demand:
help about_Script_Blocks shows the topic locally; the about_ prefix indicates that the topic is a conceptual help topic (rather than one covering a specific command); when you use Get-Help to search for a keyword (see below), you can (somewhat obscurely) limit the results to conceptual topics with -Category HelpFile.
Note: As of this writing, about_ topics can not yet be directly viewed online by adding -Online - see GitHub issue #13550 - but it's easy to google them by name.
$_ is a variable, as the $ sigil followed by an identifier implies, and is more specifically an automatic (built-in) variable:
help about_Variables covers variables in general.
help about_Automatic_Variables covers the automatic ones.
How the above can / cannot be discovered based on symbols and aliases alone:
Doing a web search for symbols is notoriously unhelpful.
As an aside: Running distinct syntax constructs such as % and { ... } together without whitespace between them (e.g. %{$_.FullName}) constitutes an additional barrier, and should therefore be avoided.
Narrowing your search by using only PowerShell's help system helps, but only to a limited degree:
%
Because Get-Help is aware of aliases, help % actually works fine and directly shows ForEach-Object's help topic.
help % -Examples shows example commands that include the use of script blocks and the automatic $_ variable.
Even though Get-Help supports keyword-based search, searching for symbol-based terms {} and $_ directly isn't helpful, because even when limiting the search to conceptual (about_-prefixed topics) with -Category HelpFile, there are either too many hits (help '$_' -Category HelpFile) or the relevant topic doesn't show at all (help '{}' -Category HelpFile)
$_ can be discovered indirectly, IF you already know that it is an instance of a variable:
help variables -Category HelpFile happens to take you directly to the relevant (local) about_Automatic_Variables topic,
whereas help variable -Category HelpFile lists the following matching topics about_Variable_Provider, ``, about_Automatic_Variables, about_Preference_Variables, about_Remote_Variables, about_Variables, and about_Environment_Variables
Note: Thanks to PowerShell's pervasive support for wildcard expressions, you could have performed the search also as follows: help about*variable* - be sure to enclose both sides of the search term in *.
$_ can be discovered indirectly, IF you already know that it is an instance of a script (code) block:
help about_*block* takes you directly to the relevant (local) about_Script_Blocks topic.
Potential future improvements:
It would be a great improvement if PowerShell's help system supported focused symbol-based searches.
Similarly, the ability to directly look up operators, such as -match, the regular-expression matching operator, would be helpful:
GitHub issue #11339 proposes just that.
On a related note, GitHub issue #11338 proposes adding the ability to look up documentation for .NET types (online).
This answer contains custom functions Show-OperatorHelp and Show-TypeHelp, which fill that gap for now (also available as Gists).
The documentation for ForEach-object says "When you use the InputObject parameter with ForEach-Object, instead of piping command results to ForEach-Object, the InputObject value is treated as a single object." This behavior can easily be observed directly:
PS C:\WINDOWS\system32> ForEach-Object -InputObject #(1, 2, 3) {write-host $_}
1 2 3
This seems weird. What is the point of a "ForEach" if there is no "each" to do "for" on? Is there really no way to get ForEach-object to act directly on the individual elements of an array without piping? if not, it seems that ForEach-Object with InputObject is completely useless. Is there something I don't understand about that?
In the case of ForEach-Object, or any cmdlet designed to operate on a collection, using the -InputObject as a direct parameter doesn't make sense because the cmdlet is designed to operate on a collection, which needs to be unrolled and processed one element at a time. However, I would also not call the parameter "useless" because it still needs to be defined so it can be set to allow input via the pipeline.
Why is it this way?
-InputObject is, by convention, a generic parameter name for what should be considered to be pipeline input. It's a parameter with [Parameter(ValueFromPipeline = $true)] set to it, and as such is better suited to take input from the pipeline rather passed as a direct argument. The main drawback of passing it in as a direct argument is that the collection is not guaranteed to be unwrapped, and may exhibit some other behavior that may not be intended. From the about_pipelines page linked to above:
When you pipe multiple objects to a command, PowerShell sends the objects to the command one at a time. When you use a command parameter, the objects are sent as a single array object. This minor difference has significant consequences.
To explain the above quote in different words, passing in a collection (e.g. an array or a list) through the pipeline will automatically unroll the collection and pass it to the next command in the pipeline one at a time. The cmdlet does not unroll -InputObject itself, the data is delivered one element at a time. This is why you might see problems when passing a collection to the -InputObject parameter directly - because the cmdlet is probably not designed to unroll a collection itself, it expects each collection element to be handed to it in a piecemeal fashion.
Consider the following example:
# Array of hashes with a common key
$myHash = #{name = 'Alex'}, #{name='Bob'}, #{name = 'Sarah'}
# This works as intended
$myHash | Where-Object { $_.name -match 'alex' }
The above code outputs the following as expected:
Name Value
---- -----
name Alex
But if you pass the hash as InputArgument directly like this:
Where-Object -InputObject $myHash { $_.name -match 'alex' }
It returns the whole collection, because -InputObject was never unrolled as it is when passed in via the pipeline, but in this context $_.name -match 'alex' still returns true. In other words, when providing a collection as a direct parameter to -InputObject, it's treated as a single object rather than executing each time against each element in the collection. This can also give the appearance of working as expected when checking for a false condition against that data set:
Where-Object -InputObject $myHash { $_.name -match 'frodo' }
which ends up returning nothing, because even in this context frodo is not the value of any of the name keys in the collection of hashes.
In short, if something expects the input to be passed in as pipeline input, it's usually, if not always, a safer bet to do it that way, especially when passing in a collection. However, if you are working with a non-collection, then there is likely no issue if you opt to use the -InputObject parameter directly.
Bender the Greatest's helpful answer explains the current behavior well.
For the vast majority of cmdlets, direct use of the -InputObject parameter is indeed pointless and the parameter should be considered an implementation detail whose sole purpose is to facilitate pipeline input.
There are exceptions, however, such as the Get-Member cmdlet, where direct use of -InputObject allows you to inspect the type of a collection itself, whereas providing that collection via the pipeline would report information about its elements' types.
Given how things currently work, it is quite unfortunate that the -InputObject features so prominently in most cmdlets' help topics, alongside "real" parameters, and does not frame the issue with enough clarity (as of this writing): The description should clearly convey the message "Don't use this parameter directly, use the pipeline instead".
This GitHub issue provides an categorized overview of which cmdlets process direct -InputObject arguments how.
Taking a step back:
While technically a breaking change, it would make sense for -InputObject parameters (or any pipeline-binding parameter) to by default accept and enumerate collections even when they're passed by direct argument rather than via the pipeline, in a manner that is transparent to the implementing command.
This would put direct-argument input on par with pipeline input, with the added benefit of the former resulting in faster processing of already-in-memory collections.
I'm not an expert on powershell but today I came across this line of code (note that Write-Output is only used as an example):
"Foo" | Write-Output
I wonder if it is any different from what I would expect:
Write-Output "Foo"
In effect the two statements should be equivalent:
In Write-Output "Foo", "Foo" is implicitly bound to positional parameter -InputObject, which accepts type PSObject[], i.e., an array of objects of any type.
In "Foo" | Write-Output, by virtue of parameter -InputObject being defined as optionally accepting pipeline input (by value, i.e. whole objects), "Foo" is also bound to -InputObject.
I assume you chose Write-Output as an example, but it's worth nothing that there's rarely a good reason to use that cmdlet explicitly - simply omitting it in your examples would yield the same results.
Furthermore, there are several cmdlets where the two forms are not equivalent, namely those where -InputObject is defined as a scalar (with exceptions); consider the following:
1, 2 | Get-Member # reports [System.Int32]
Get-Member -InputObject 1, 2 # reports [System.Object[]]
1, 2 | Get-Member reports the type members for each element in the input array.
Get-Member -InputObject 1, 2, by contrast, reports the members of the array type itself.
This difference in behavior is intentional and documented: using the parameter (-InputObject) allows inspecting collection types as a whole, whereas using the pipeline allows inspecting a collection's individual elements' types.
Note that there are cmdlets that exhibit the same difference in behavior, even though passing collections as a whole to them doesn't make much sense, such as Export-Csv; in such cases, always use the pipeline - see this GitHub issue for background information.
To determine what parameters of a cmdlet accept pipeline input and thus understand what parameter pipeline input will be bound to:
To see the parameters in the context of the full help topic:
Run Get-Help -Full <cmdlet>; using -Full is crucial.
Search for occurrences of true (, which will match parameters that accept:
pipeline input by value (whole object) only: true (ByValue)
pipeline input by property name only: true (ByPropertyName)
either: true (ByValue, ByPropertyName)
More generally, each parameter description has an Accept pipeline input? line item followed by a Boolean.
To extract just the names and their aliases, data types, and binding characteristics (using Rename-Item as an example):
Get-Help Rename-Item -Parameter * | Where-Object pipelineInput -like 'true*' |
Select-Object Name, Aliases, Type, pipelineInput
I am creating my own set of cmdlets. They all need the same state data (like location of DB and credentials for connecting to DB). I assume this must be a common need and wonder what the common idiom for doing this is.
the obvious one is something like
$db = my-make-dbcreds db=xxx creds=yyyy ...
my-verb1 $db | my-verb2 $db -foo 42...
my-verb8 $db bar wiz
.....
but i was wondering about other ways. Can I silently pipe the state from one to another. I know I can do this if state is the only thing I pipe but these cmdlets return data
Can I set up global variables that I use if the user doesnt specify state in the command
Passing the information state through the pipe is a little lost on me. You could update your cmdlets to return objects that the next cmdlet will accept via ValueFromPipeline. When you mentioned
like location of DB and credentials for connecting to DB
the best this I could think that you want is....
SPLATTING!
Splatting is a method of passing a collection of parameter
values to a command as unit. Windows PowerShell associates
each value in the collection with a command parameter.
In its simplest form
$params = #{
Filter = "*.txt"
Path = "C:\temp"
}
Get-ChildItem #params
Create a hashtable of parameters and values and splat them to the command. The you can edit the table as the unique call to the cmdlet would allow.
$params.Path = "C:\eventemperor"
Get-ChildItem #params
I changed the path but left the filter the same. You also dont have to have everything in $params you splat and use other parameters in the same call.
It is just a matter of populating the variables as you see fit and changing them as the case requires.
Spewing on the pipeline
Pretty that is what it is actually called. If you use advanced function parameters you can chain properties from one cmdlet to the next if you really wanted to. FWIW I think splatting is better in your case but have a look at the following.
function One{
param(
[parameter(Mandatory=$true,
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$true)]
[String[]]
$Pastry
)
write-host "You Must Like $Pastry"
Write-Output (New-Object -TypeName PSCustomObject -Property #{Pastry= $pastry})
# If you have at least PowerShell 3.0
# [pscustomobject]#{Pastry= $pastry}
}
Simple function that writes the variable $pastry to the console but also outputs an object for the next pipe. So running the command
"Eclairs" | One | One | Out-Null
We get the following output
You Must Like Eclairs
You Must Like Eclairs
We need to pipe to Out-Null at the end else you would get this.
Pastry
------
{Eclairs}
Perhaps not the best example but you should get the idea. If you wanted to extract information between the pipe calls you could use Tee-Object.
"Eclair" | One | Tee-Object -Variable oneresults | One | Out-Null
$oneresults
Consider Parameter Default Values
Revisiting this concept after trying to find a better way to pass SQL connection information between many function working against the same database. I am not sure if this is the best thing to do but it certainly simplifies thing for me.
The basic idea is to add a rule for your cmdlet or wildcard rule if your cmdlets share a naming convention. For instance I have a series of functions that interact with our ticketing system. They all start with Get-Task.... and all configured with SQL connection information.
$invokeSQLParameters = #{
ServerInstance = "serverName"
Username = $Credentials.UserName
Password = $Credentials.GetNetworkCredential().Password
}
$PSDefaultParameterValues.Add("New-Task*:Connection",$invokeSQLParameters)
$PSDefaultParameterValues.Add("Get-Task*:Connection",$invokeSQLParameters)
So now in my functions I have a parameter called Connection that will always be populated with $invokeSQLParameters as long as the above is done before the call. I still use splatting as well
Invoke-Sqlcmd -Query $getCommentsQuery #Connection
You can read up more about this at about_parameters_default_values