Settings default parameter values with XML values - powershell

I read somewhere that the param section must be the very first thing that appears in a script or function so this is what I have come up with in order to set the default values of each of the params. Yes, it is unorthodox, but it works.
Param (
[Xml]$xmlObj = (Get-Content "Download-VBK_config.xml"),
[String]$dlFrom = $xmlObj.Configuration.Download.From,
[String]$dlTo = $xmlObj.Configuration.Download.To,
[String]$exTo = $xmlObj.Configuration.Extract.To
)
However, is there a better way I could go about setting a param's default value by loading values from an XML file?

You can leave the parameters without default values, then look at the $PSBoundParameters variable to see what parameters were passed in, and fill the ones in that we're not passed.
Param(
[string]$Param1,
[string]$Param2)
[xml]$defaults =Get-Content file.xml
if(!$PSboundParameters.ContainsKey("Param1"))
{
$Param1 = $defaults.Configuration.Defaults.Param1
}

Related

Pass multiple parameters to function by pipeline

I'm having trouble passing two parameters via pipeline to a function.
function Test{
[cmdletbinding()]
param(
[parameter(ValueFromPipeline=$true, Mandatory=$true,Position=0)]
[string]$jeden,
[parameter(ValueFromPipeline=$true, Mandatory=$true,Position=1)]
[string]$dwa
)
Process{write-host "$jeden PLUS $dwa"}
}
"one", "two"|Test
What I expected as an outcome was
one PLUS two
but what I got was
one PLUS one
two PLUS two
I'm obviously doing something wrong, since both parameters get used twice. Please advise.
I got it to work by creating pscustomobject and piping it to function, where ValueFromPipelineByPropertyName property is set to true for both parameters.
function Test{
[cmdletbinding()]
param(
[parameter(ValueFromPipelineByPropertyName=$true, Mandatory=$true,Position=0)]
[string]$jeden,
[parameter(ValueFromPipelineByPropertyName=$true, Mandatory=$true,Position=1)]
[string]$dwa
)
Process{write-host "$jeden PLUS $dwa"}
}
$Params = [pscustomobject]#{
jeden = “Hello”
dwa = “There”
}
$Params |Test
OUTPUT:
Hello PLUS There
Variable assigning can be skipped and [pscustomobject] can be piped directly.
Have u tried to pass both strings as single object? Seems its ur pipeline is treating dem as 2 obj...
#("one", "two") | Test
EDIT. Try to define test in order to accept array:
function Test {
[cmdletbinding()]
param(
[parameter(ValueFromPipeline=$true, Mandatory=$true,Position=0)]
[string[]]$strings
)
Process {
write-host "$($strings[0]) PLUS $($strings[1])"
}
}

Pass an argument from a module

I need help understanding how to pass on an argument from an imported module.
The module contains some custom arguments such as -one, -two, -three
I am trying to make a GUI using the commands from the module.
eg. If "One" is selected from the drop down menu, pass through the -one command.
However when I do so (using the example below), I get the error: "A positional parameter cannot be found that accepts argument '-one'."
I can see that using the code below, it adds single quotations around the command which probably breaks it.
I know I can run an IF statement (eg if combobox.text = "one", do this), however I would prefer to use a variable instead of having to make multiple if statements or a loop. The use of a variable seems like a simpler option.
I'm learning this language as I go so I'm not quite there yet with the knowledge :)
Thanks for any help. Hope this made sense.
$variable = $comboboxNumbers.Text
#example One is selected from the dropdown
Custom-ADCommand -identity "username" $variable
Below is simple example method:
function Set-SwitchParams {
[CmdletBinding()]
param (
# Parameter help description
[Parameter(Mandatory = $false)]
[switch]
$SwitchA,
[Parameter(Mandatory = $false)]
[switch]
$SwitchB
)
begin {
}
process {
}
end {
if ($SwitchA){
Write-Host "SwitchA is activated"
}
if ($SwitchB){
Write-Host "SwitchB is activated"
}
}
}
Put the method in a PS1 file, e.g. SwitchPlayground.ps1. Then source the file in PowerShell via:
. .\SwitchPlayground.ps1
Afterward, you can play around with the command, e.g.:
Set-SwitchParmas -SwitchA
I'd suggest studying the following links:
about functions basic
about functions advanced
about function parameters
Hope that helps.
An If statement if probably much nicer, but its possible to create a string and then execute the string in powershell.
As a simple example take this string
$string = '#("test","hello","whats up")'
I can then execute it and use it to create an array
$array = invoke-expression $string
Which will create an array with "test", "hello" and "whats up" and store it in $array
PS C:\temp> $string = '#("test","hi","what")'
PS C:\temp> $array = Invoke-Expression $string
PS C:\temp> $array
test
hi
what

How to output the referenced value?

I have the following function.
function Params {
param (
[Parameter(Mandatory = $true)]
[Alias('Param1')]
[AllowNull()]
${Param1[default value]}
)
[ref] $Param1 =
if (${Param1[default value]}) {
${Param1[default value]}
} else {
'default'
}
}
Params
$input1 = $null
"the param input is $([ref]$input1)"
If i input something for parameter on prompt or if i leave it to default value, i get this as output for $([ref]$input)
the param input is System.Management.Automation.PSReference`1[System.Management.Automation.LanguagePrimitives+Null]
Why am i not getting a value instead?
I want this output for example:
the param input is default
I ended up resorting to a different method to achieve what i want:
Defining this at the top of script:
[CmdletBinding()]
Param(
$Param1 = (Read-Host -prompt "Param1")
)
if (!$Param1) { "default" }
"the param input is $Param1"
The [ref] type accelerator (it's not a type accelerator in the usual sense, but it does create PSReference objects, so... it sort of is) gets you, as it tells you, a PSReference object.
In order to retrieve the value from it, you'd need to ask for it specifically. In your code, you can access it by pulling the Value property from the created reference object.
"the param input is $(([ref]$input1).Value)"
However, given that $input1 isn't assigned to, you might have to refactor a bit to get what you're after.

powershell mandatory parameter with default value shown

I am looking for a way to have an PowerShell script ask for an parameter which needs to be mandatory, but shown with an default value, e.g.:
.\psscript
Supply values for the following parameters:
parameter1[default value]:
parameter2[1234]:
I want to ask for input but provide some default values.
If I use the mandatory option it asks for the values nicely but doesn't show the default value or process the given value. If I don't make it mandatory then PowerShell doesn't ask for the value at all.
Here's some script examples I tried:
[CmdletBinding()]
Param(
[parameter(Mandatory=$true)] $SqlServiceAccount = $env:computername + "_sa",
[parameter(Mandatory=$true)] $SqlServiceAccountPwd
)
This script asks for parameters but does not show or process the default value if I just press enter on the first parameter.
[CmdletBinding()]
Param(
[parameter(Mandatory=$false)] $SqlServiceAccount = $env:computername + "_sa",
[parameter(Mandatory=$true)] $SqlServiceAccountPwd
)
This script doesn't ask for the first parameter, but processes the default value.
Here's a short example that might help:
[CmdletBinding()]
Param(
$SqlServiceAccount = (Read-Host -prompt "SqlServiceAccount ($($env:computername + "_sa"))"),
$SqlServiceAccountPwd = (Read-Host -prompt "SqlServiceAccountPwd")
)
if (!$SqlServiceAccount) { $SqlServiceAccount = $env:Computername + "_sa" }
...
By definition: mandatory parameters don't have default values. Even if you provide one, PowerShell will prompt for value unless specified when the command is called. There is however a 'hacky' way to get what you ask for. As variables (and as consequence - parameters) can have any name you wish, it's enough to define command with parameters that match the prompt you would like to see:
function foo {
param (
[Parameter(Mandatory = $true)]
[Alias('Parameter1')]
[AllowNull()]
${Parameter1[default value]},
[Parameter(Mandatory = $true)]
[Alias('Parameter2')]
[AllowNull()]
${Parameter2[1234]}
)
$Parameter1 =
if (${Parameter1[default value]}) {
${Parameter1[default value]}
} else {
'default value'
}
$Parameter2 =
if (${Parameter2[1234]}) {
${Parameter2[1234]}
} else {
1234
}
[PSCustomObject]#{
Parameter1 = $Parameter1
Parameter2 = $Parameter2
}
}
When called w/o parameters, function will present user with prompt that match parameter names. When called with -Parameter1 notDefaultValue and/or with -Parameter2 7, aliases will kick in and assign passed value to the selected parameter. As variables named like that are no fun to work with - it makes sense to assign value (default or passed by the user) to variable that matches our alias/ fake parameter name.
I'd do it this way
param(
[Parameter(Mandatory)][string]$aString
)
if([string]::IsNullOrWhiteSpace($aString))
{
$aString = "A Default Value"
}
In my opinion, if you're using Read-Host in a param() block, then you're doing something wrong. At that point, what's the point of using param() at all?
There isn't a way to do what you want with a mandatory parameter and powershell prompting for you.
You would instead have to make it optional (remove mandatory), then implement the prompting code yourself (Read-Host, but take blank response as a default; something like that).

Powershell initializing global to $null is failing

I am running a GUI that takes user input but can be changed before finalizing if they make a mistake. When the form opens, I am trying to initialize the globals to null. This is failing. I put a breakpoint on the code and looked at the value before and then stepped into it. The value does not change.
So for example, if I run the form and enter "Foo" as my global variable, exit the form, then run the form again, even after the line in question executes, the value of the global is still "Foo". What is going on? I have used this exact code with other GUIs and it never failed (but the values were generated automatically rather than based on user input).
# Define and initialize global variables
$global:ServerName = $null # <-- This fails to reset the variable from the previous run of the form
function ValidateChoices(){
$OKToGo = $true
$TempServerName = $null
try {
# Only Allow Valid NETBios Name with AlphaNumberic and - up to 15 characters
[ValidatePattern("^[A-Za-z\d-]{1,15}$")]$TempServerName = $ServerNameTextbox.Text
$ServerNameTitle.BackColor = ""
$ServerNameTextbox.BackColor = ""
$global:ServerName = $TempServerName
} catch {
$OKToGo = $false
$ServerNameTitle.BackColor = "Pink"
$ServerNameTextbox.BackColor = "Pink"
}
...
if ( $OKToGo ){
"ServerName=" + $global:ServerName | Out-File c:\debug.txt
}
}
Here is the answer: When ValidatePattern is run against a variable, those restrictions are kept and re-evaluated anytime an attempt to change the variable is made. This holds true even if ValidatePattern was not explicitly called. And because it was a global variable, those restrictions rode through multiple iterations of the form. Because $null does not conform to my listed ValidatePattern parameters, the call to change the value was ignored