I know how to add switch parameters to PowerShell scripts, but the name of the switch is always a (valid) identifier like -Enable because the name is generated from the backing PowerShell variable.
[CmdletBinding()]
param(
[switch] $Enable = $false
)
Some tools have switches like -2008. Normally, one would name the switch $2008 but this is not a valid identifier.
How can I implement such a switch as a boolean value in PowerShell?
Or in other words: How to specify a different parameter name then the backing variable?
Edit 1
I wasn't aware of number only variables (which is very strange for a programming language...). Anyhow, I created that example:
function foo
{ [CmdletBinding()]
param(
[switch] $2008
)
Write-Host "2008=$2008"
}
For this code, which is accepted as valid PowerShell, I get a auto completion as wanted. But when providing that parameter, I get this error message:
foo : Es wurde kein Positionsparameter gefunden, der das Argument "-2008" akzeptiert.
In Zeile:1 Zeichen:1
+ foo -2008
+ ~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [foo], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,foo
Translation: No positional parameter was found, that accepts the argument "-2008".
The purpose of the script is to provide translations / wrappers for command line interfaces.
Here is a set of standardized executables with parameters:
vcom.exe -2008 design.vhdl
vsim.exe -2008 design
Translation:
ghdl.exe -a --std=2008 design.vhdl
ghdl.exe -e --std=2008 design
ghdl.exe -r --std=2008 design
I would like to keep the feature of auto completion for parameters, otherwise I could process all remaining parameters and translate them by hand.
PowerShell doesn't support numeric parameters (See this answer).
Could a validateset be an acceptable solution to your problem ?
Validate set does benefit from the autocompletion feature and this is the next best thing, in my opinion, of what you wanted.
Foo & Foos — both are the same, except Foos accept multiple parameters.
foo -std 2008
foos -std 2008,2009,2010
function foo
{ [CmdletBinding()]
param(
[ValidateSet("2008",
"2009",
"2010",
"2011","2012")][INT]$std
)
Write-Host "std:$std"
}
function foos
{ [CmdletBinding()]
param(
[ValidateSet("2008",
"2009",
"2010",
"2011","2012")][INT[]]$std
)
$std | foreach {Write-Host "std: $_"}
}
foo -std 2008
foos -std 2008,2009,2010
Related
I am getting an error in my powershell script for a runbook on Azure:
Write-Error : A positional parameter cannot be found that accepts argument '+'.
At Test-Update-IndexesForallShardsFromShardManagerRunbook:61 char:61
+
+ CategoryInfo : InvalidArgument: (:) [Write-Error], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.WriteErrorCommand
Based on the logs I see on my Azure autmation account from a job that ran, I pinpointed the origin of the error in my script somewhere in the following code:
$updateStatisticSql = "UPDATE STATISTICS [$Using:tableName] ( [$Using:statName] );"
$CmdUpdateStats=new-object system.Data.SqlClient.SqlCommand($updateStatisticSql, $Conn1)
$CmdUpdateStats.CommandTimeout=1500
Try
{
$Ds=New-Object system.Data.DataSet
$Da=New-Object system.Data.SqlClient.SqlDataAdapter($CmdUpdateStats)
[void]$Da.fill($Ds)
}
Catch
{
# Will catch the exception here so other statistics can be processed.
Write-Error "Statistic " + $tableName + "(" + $statName + ") could not be updated. Investigate the statistic."
}
It seems after adding logging after each line, that it doesn't log after the "fill" function, so I assume something is going wrong there. But I am not seeing the relation between the error and this function. It also doesn't seem a script breaking error, since it never goes into the catch and the rest of the scripts runs fine. I also validated that the statistics are updated, even though the error I am getting.
So the error you are seeing is because you are trying to build a string using concatenation which means you have spaces and spaces are used to delimit parameters when calling cmdlets. Put all the concatenation into parens:
Write-Error ("Statistic " + $tableName + "(" + $statName + ") could not be updated. Investigate the statistic.")
I've got a release definition in vsts that needs access to a password I've defined as a secret variable in a variable group. I've linked that group to this release definition but when I run the task the parameter ends up blank. Is there anything special I need to do to get the value of a secret variable?
Definition of the powershell task that uses the password
Linked variable group
Error output:
2017-08-09T14:01:17.9262375Z ##[command]. 'C:\agent_work\r1\a\$(AGENT.BUILDDIRECTORY)\RCV\deploys\common\Get-FromArtifactory.ps1' -repoUsername developer -repoPassword -repoPath libs-snapshot-local
2017-08-09T14:01:18.8168538Z ##[error]C:\agent_work\r1\a\$(AGENT.BUILDDIRECTORY)\RCV\deploys\common\Get-FromArtifactory.ps1 : Missing an argument for parameter 'repoPassword'. Specify a parameter of type 'System.String' and try again.
Edited to add more info as requested.
This is the start of my Get-FromArtifactory.ps1
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[String]
$repoUsername,
[Parameter()]
[String]
$repoPassword,
# Other params
)
#setup credentials object for arty access
$secPassword = $repoPassword | ConvertTo-SecureString -asPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($repoUsername, $secPassword)
Edit 15/08/2017 - I've updated it to use quotes as suggested by #Marina-MSFT but it's still just passing in blank.
Amended script to print out part of password:
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[String]
$repoUsername,
[Parameter()]
[String]
$repoPassword,
#other params
)
write-host "pword $repoPassword"
write-host "1st char: $($repoPassword[0])"
2017-08-15T09:23:00.8606081Z ##[command]. 'C:\agent_work\r1\a\RCV\deploys\common\Get-FromArtifactory.ps1' -repoUsername developer -repoPassword "" -repoPath libs-snapshot-local -artifactName ipo-deposit-accounts_2.11 -artifactPath uk/gov/ipo -artifactVersion 0.1-SNAPSHOT -downloadDir C:\agent_work\r1\a
2017-08-15T09:23:02.3606174Z pword
2017-08-15T09:23:02.3606174Z 1st char:
Same with single quotes
2017-08-15T09:22:10.6573655Z ##[command]. 'C:\agent_work\r1\a\RCV\deploys\common\Get-FromArtifactory.ps1' -repoUsername developer -repoPassword '' -repoPath libs-snapshot-local -artifactName ipo-deposit-accounts_2.11 -artifactPath uk/gov/ipo -artifactVersion 0.1-SNAPSHOT -downloadDir C:\agent_work\r1\a
2017-08-15T09:22:11.2198723Z pword
2017-08-15T09:22:11.3761178Z 1st char:
I've got it working now, there were 2 things going on.
Having . in the name of a secret variable in a variable group
doesn't work. I recreated this issue in a separate release
definition/variable group.
Even after I removed the . in my var
names I still had to recreate the variable group for it to work.
Bug for issue with . in variable name raised here: https://developercommunity.visualstudio.com/content/problem/94475/secret-variable-in-variable-not-group-not-availabl.html
This question already has answers here:
Function return value in PowerShell
(10 answers)
Closed 5 years ago.
I've written a PowerShell script to perform some pre-installation setup for a series of patches I'm deploying to client computers across our estate and I'm hitting a bit of an odd issue that I can't wrap my head around.
The setup patch checks the 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe.config' file due to a "feature" of PowerShell 2.0 whereby the application uses .NET Framework 2.0.0 by default instead of 4.5.2, preventing certain functions from being executed. If the file doesn't exist or the evaluated values don't match a specification, I add the XML file and provide the necessary values.
The command I run is as follows:
$psConfigDir = "C:\Windows\System32\WindowsPowerShell\v1.0"
$psConfigFileName = "powershell.exe.config"
[boolean]$psExeXml = Set-PSRuntimeConfigs -FilePath ( [String]::Format("{0}\{1}", $psConfigDir, $psConfigFileName) ) -CLRVersions #("v4.0.30319", "v2.0.50727")
...and the Set-PSRuntimeConfigs method is found in a PowerShell Module I created with the code below:
Function Set-PSRuntimeConfigs {
[CmdletBinding()]
Param(
[String]$FilePath,
[System.Collections.ArrayList]$CLRVersions
)
Try {
$xmlWriter = New-Object System.Xml.XmlTextWriter($FilePath, $null)
$xmlWriter.Formatting = "Indented"
$xmlWriter.Indentation = 4
$xmlWriter.WriteStartDocument()
$xmlWriter.WriteStartElement("configuration")
$xmlWriter.WriteStartElement("startup")
$xmlWriter.WriteAttributeString("useLegacyV2RuntimeActivationPolicy", $true)
$CLRVersions | ForEach-Object {
$xmlWriter.WriteStartElement("supportedRuntime")
$xmlWriter.WriteAttributeString("version", $_)
$xmlWriter.WriteEndElement()
}
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndDocument()
$xmlWriter.Close()
$xmlWriter.Dispose()
return $true
} Catch {
echo "ERROR: Exception occurred during XML write process!"
echo "ERROR: Exception message: $($_.Exception.Message)"
return $false
}
}
However, the function is returning an InvalidCastException when trying to assign the result of the function to the $psExeXml variable. Oddly, PowerShell returns with an error stating that [System.Object()] cannot be converted to type [Boolean] despite the fact that only $true or $false is returned from the function.
My first thought is that an exception was being thrown by the function due to a code issue but the function is written to report the error in the prompt and just return $false in that case... Regardless, I'm stuck and can't figure out where to proceed with this...
If the function produces any output then the result will be an array containing the strings that were output and then the final element will be your boolean.
So for this code:
echo "ERROR: Exception occurred during XML write process!"
echo "ERROR: Exception message: $($_.Exception.Message)"
return $false
the function returns an array of two strings and a boolean.
I am writing a PowerShell function which can take pipeline input. Specifically I am testing it with Import-CSV. Many of the params are not mandatory, which means sometimes the CSV will not have those columns. For boolean values this is working as expected, but with string values, a missing CSV field yields a copy of the row object in the string field.
Here is an example problem parameter:
[Parameter(Mandatory=$False,
ValueFromPipeline=$True,
ValueFromPipelinebyPropertyName=$True,
HelpMessage="TCP Port of the remote server")]
[Alias('Port', 'Remote Server Port')]
[string]$RemoteServerPort = "5500",
Now, if the field is missing, I would expect the value to be "5500" as specified, but instead I get:
$RemoteServerPort = #{Name=KG; IP=10.1.1.1; Username=Admin; Password=}
I've done some looking around, but frankly I'm not even sure what to search for.
This is because you specified ValueFromPipeline=$True so that PoSh coerces the piped object to a string if it cannot bind the parameter by property name. You could solve that by removing ValueFromPipeline=$True from this parameter and introduce another one to be bound to the piped object, i.e. something like this
function MyTestFunc() {
param(
[Parameter(ValueFromPipeline=$True)]
[object]$PipedObj,
[Parameter(Mandatory=$False,
ValueFromPipelinebyPropertyName=$True,
HelpMessage="TCP Port of the remote server")]
[Alias('Port', 'Remote Server Port')]
[string]$RemoteServerPort = "5500"
)
Write-Host "Port: $RemoteServerPort / Obj: $PipedObj"
}
$o1 = [pscustomobject]#{"Server" = "123"; "Port" = "12345"}
$o2 = [pscustomobject]#{"Server" = "1234"; "OS" = "Win3.1"}
$o1 | MyTestFunc
$o2 | MyTestFunc
Will result in
Port: 12345 / Obj: #{Server=123; Port=12345}
Port: 5500 / Obj: #{Server=1234; OS=Win3.1}
A way to see in detail what is actually happening behind the scenes is to use Trace-Command like so
Trace-Command ParameterBinding {$o2 | MyTestFunc} -PSHost
We needed to retrieve the information in active directory concerning 'Terminal Services'. For this I've created a function that works fine most of the time. However, with some users we have issues.
The code:
Function Get-ADTSProfile {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,Position=0)]
[String] $DistinguishedName,
[parameter(Mandatory=$true,Position=1)]
[ValidateNotNullOrEmpty()]
[ValidateSet('UserProfile','AllowLogon','HomeDirectory','HomeDrive')]
[String]$Property
)
Begin {
$User = [ADSI]"LDAP://$DistinguishedName"
}
Process {
Switch ($Property) {
'AllowLogon' {if ($($User.psbase.InvokeGet('allowLogon')) -eq '1'){$True}else{$False}}
'HomeDirectory' {$User.psbase.InvokeGet('TerminalServicesHomeDirectory')}
'HomeDrive' {$User.psbase.InvokeGet('TerminalServicesHomeDrive')}
'UserProfile' {$User.psbase.InvokeGet('TerminalServicesProfilePath')}
}
}
}
The error:
Get-ADTSProfile -DistinguishedName 'CN=test\, test (Den Bosch) NLD,OU=Users,OU=Disabled,OU=NLD,OU=EU,DC=domain,DC=net' -Property 'UserProfile'
Exception calling "InvokeGet" with "1" argument(s): "The directory property cannot be fo
und in the cache.
"
At S:\Test\Brecht\Testie.ps1:84 char:38
+ 'UserProfile' {$User.psbase.InvokeGet('TerminalServicesPro ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodTargetInvocation
I can't really figure out why it works on some and not on all..
I've been working on a recent project that uses ADSI to set and read Terminal Services attributes. From my testing anytime you perform a "InvokeGet({TS Attribute})" a COM exception will be thrown with the message "The directory property cannot be found in cache"
This seems to occur only when the "userParameters" attribute is not set in AD. Maybe the attribute internally checks the ADSI cache for userParameters? So i'm thinking logically you could check the DirectoryEntry for userParameters first, then try and read the properties, or else set it to construct the blob
if ($user.Properties.Contains("userParameters"))
{
#Read the Property from ADSI
Write-Host $user.InvokeGet("TerminalServicesProfilePath")
} else {
#Set the property to construct the userParameter blob
$user.InvokeSet("TerminalServicesProfilePath", "\\somepath")
$user.CommitChanges()
}
Even if the userParameters attribute is not set, you can still perform an InvokeSet to have it constructed