Parameters issue in script - powershell

Can someone tell what I am doing wrong in the below I wrote:
function set-harden {
[CmdletBinding(DefaultParameterSetName='NormalHardening')]
param (
[Parameter(ParameterSetName='DoNotRemoveFromDomain')]
[Parameter(ParameterSetName='PermitHTTP' ,Mandatory=$True)]
[Parameter(ParameterSetName='PermitHTTPS' ,Mandatory=$True)]
[switch]$DONOTRemovefromdomain,
[Parameter(ParameterSetName='PermitHTTP')]
[Parameter(ParameterSetName='DoNotRemoveFromDomain')]
[switch]$Permithttp,
[Parameter(ParameterSetName='PermitHTTPS')]
[Parameter(ParameterSetName='DoNotRemoveFromDomain')]
[switch]$Permithttps,
[Parameter(ParameterSetName='NormalHardening')]
$NormalHardening
)}
If($NormalHardening -eq ""){
Write-Host "Excellent!"
}
All I want to do is to let the user select -DONOTRemovefromdomain or -Permithttp or even -Permithttps. There could be a variety of options the user has to choose from.
When I run this below I get an error:
PS C:\Temp> set-harden -DONOTRemovefromdomain -Permithttp
set-harden : Parameter set cannot be resolved using the specified named parameters.
At line:1 char:1
+ set-harden -DONOTRemovefromdomain -Permithttp
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [set-harden], ParameterBindingException
+ FullyQualifiedErrorId : AmbiguousParameterSet,set-harden
Also, if I do not specify anything (so it should just go to the parameter NormalHardening) I get an nothing back:
PS C:\Temp> set-harden
PS C:\Temp>

You've specified two flags, DONOTRemovefromDomain and Permithttp that belong to two parameter sets, DoNotRemoveFromDomain and PermitHttp. The command parser has no way of knowing which parameter set you mean, so you get an error.
The reason you don't get an error when you don't specify anything is because you've set the default parameter set explicitly to NormalHardening. You've not set the Mandatory flag on the single parameter in this parameter set, and by default parameters are not mandatory so you're not seeing an error.
Instead of having all these parameter sets why not just have 2, one for the default and one for all the flags you want to set:
function set-harden {
[CmdletBinding(DefaultParameterSetName='NormalHardening')]
param (
[Parameter(ParameterSetName='Options')]
[switch]$DONOTRemovefromdomain,
[Parameter(ParameterSetName='Options')]
[switch]$Permithttp,
[Parameter(ParameterSetName='Options')]
[switch]$Permithttps,
[Parameter(ParameterSetName='NormalHardening')]
$NormalHardening
)}
If($PSCmdlet.ParameterSetName -eq "Options"){
Write-Host "Excellent!"
}
How, if the parameter set name is set to Options you can check and apply the flags. If it's set to NormalHarding then you know to use the $NormalHardening parameter.

Sean gave a good answer already about what's going on in your specific case, but I want to include some tips for troubleshooting parameter sets.
Get Help
Or more specifically, Get-Help. The parameter set syntax is automatically generated from the param block, so running Get-Help myFunction will show you how PowerShell is interpreting your parameter sets (how many, which parameters are mandatory or not in each set, etc.).
Trace the Call
If the sets look right but you're getting errors and aren't sure why, let PowerShell show you how it's binding parameters:
Trace-Command -Name ParameterBinding -Expression { Set-Harden -Permithttp } -PSHost
That can give you great insight on what's going on, and lead you to how you might fix that (or help you realize that you can't).

Related

Using PowerShell, how can I use Substring to extract the computer name from output

I am new to PowerShell but I found I can use Substring to count to the right or left of a string within a variable. It appears though it is not supported for the output I am receiving. I am hoping someone can point me in the right direction. Thank you for any help.
Code to retrieve the computer name.
$compname = WmiObject -class Win32_ComputerSystem | Select-Object Name
$compname
$compname.Substring(9,0)
Here is the result and error:
Name
Computer-PC
Method invocation failed because [Selected.System.Management.ManagementObject] does not contain a method named 'Substring'.
At line:3 char:1
$compname.Substring(9,0)
+ CategoryInfo : InvalidOperation: (Substring:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
This error occurs because you're trying to use the Substring method on an object.
Take a look, if i do the same query that you did, it returns me an object with "Name" property:
And as the powershell error shows, you cannot call the substring method directly to an object. You must do it on a string, in this case, the property name. To solve you problem, you just need to call "Name" property in your query. Something like this:
$computerName = (Get-WmiObject Win32_ComputerSystem).Name
After that, you will be able to use "Substring" method because that query returns a string:
If any other problem occurs, i will be glad to help you :)

Param in CmdletBinding not mandatory and error/misspelled checking

Hey all I have a powershell script that I need to send some parameters to. However, sometimes I do not need to send any or maybe one out of the two.
[CmdletBinding()]
param (
[Parameter(ParameterSetName='SkipAutoLoader')][switch]$SkipAutoLoader,
[Parameter(ParameterSetName='AppPool')][switch]$AppPool
)
[more code here....]
if (-not $SkipAutoLoader) {
$services += "Auto Loader Service"
}
[more code here....]
The above works just fine as long as I have either:
.\Start-AkkServides.ps1 -SkipAutoLoader
or
.\Start-AllServices -AppPool
If I have both together:
.\Start-AllServices -SkipAutoLoader -AppPool
It errors out.
C:\src\Start-AllServices.ps1 : Parameter set cannot be resolved using the specified named parameters.
At line:1 char:1
+ .\Start-AllServices.ps1 -SkipAutoLoader -AppPool
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Start-AllServices.ps1], ParameterBindingException
+ FullyQualifiedErrorId : AmbiguousParameterSet,Start-AllServices.ps1
I also want to be able to determine if the param has the valid -SkipAutoLoader and/or -AppPool but with something else tagged like -SkipAutoLoader43
I would like to say that -SkipAutoLoaderbob is an invalid param. But do not show an error if either param is not present.
Loads Fine:
.\Start-AllServides.ps1 -SkipAutoLoader
Loads Fine:
.\Start-AllServides.ps1 -AppPool
Does Not Load Fine/causes error:
.\Start-AllServides.ps1 -SkipAutoLoader -AppPool
Does Not Load Fine/casues error:
.\Start-AllServides.ps1
Does not say param is not valid:
.\Start-AllServides.ps1 -SkipAutoLoaderbob
Does not say param is not valid:
.\Start-AllServides.ps1 -AppPool7
Possible to do with powershell?
Your parameters are in separate parameter sets (only[1]), so by design they cannot be used together.
If your parameters can be freely combined, you don't need to define parameter sets at all.
Since your script is an advanced one, thanks to [CmdletBinding()] and/or [Parameter()] attributes, calling with non-declared parameter names is automatically prevented.
However, the lack of a default parameter-set designation in the [CmdletBinding()] attribute causes a more fundamental error if you only specify an unsupported parameter name (such as -SkipAutoLoaderbob): PowerShell then doesn't know which of the two defined parameter sets to select, because no declared parameter can be bound (before even considering whether the parameter name given is valid, perhaps surprisingly)
Use [CmdletBinding(DefaultParameterSetName='AppPool')], for instance, to designate the default parameter set.
Assuming that your two parameters can be freely combined and neither is mandatory (which [switch] parameters shouldn't be anyway), your code can be simplified to:
[CmdletBinding()]
param (
[switch] $SkipAutoLoader,
[switch] $AppPool
)
# Output the name of the active parameter set.
$PSCmdlet.ParameterSetName
Note that non-mandatory parameters without explicit parameter-set membership (and other non-default parameter properties) do not require [Parameter()] attributes.
When you invoke the script, you'll see that PowerShell implicitly defines a parameter set in the absence of explicitly declared ones, named __AllParameterSets.
[1] Note that a parameter can belong to multiple explicitly specified parameter sets, via multiple [Parameter(ParameterSetName= '...')] attributes. Any parameter without an explicit parameter-set membership is implicitly part of all parameter sets.

The input object cannot be bound to any parameters?

I'm trying to set user extension properties from a powershell code with an input coming from a CSV file.
I'm getting this error:
Set-AzureADUserExtension : The input object cannot be bound to any
parameters for the command either because the command does not take
pipeline input or the input and its properties do not match any of
the parameters that take pipeline input. At line:14 char:17
... $user | Set-AzureADUserExtension -ObjectId $upn -ExtensionName "e ...
CategoryInfo : InvalidArgument: (class User { ...Type: Member } :PSObject) [Set-AzureADUserExtension],
ParameterBindingException
FullyQualifiedErrorId : InputObjectNotBound,Microsoft.Open.AzureAD.Graph.PowerShell.Custom.SetAzureADUserExtension
I'm kind of new to this, so it's for sure not the best.
Does anyone have any suggestions?
Thanks!
If you are looking for help you will need to share your code, not just the error. I verified for you that Set-AzureADUserExtension does take pipeline input for all properties.
What I can't tell is where you set $UPN or why you are piping $user to the command since I can't see your code.

Questions/Problems related to set-winuserlanguagelist command

I'm building a code to fix keyboard layout situation on windows 10. With automated solution, I decided to use powershell. But the problem is that I'm pretty new in it and face certain problems. I managed to dig a script to change keyboard layouts, however it changes only to one language. As I try to create array with 2 languages:
$langlist=$lang_en,$lang_ru
set-winuserlanguagelist $langlist
It simply returns me next error:
Set-WinUserLanguageList : Cannot convert 'Microsoft.InternationalSettings.Commands.WinUserLanguage' to the type
'Microsoft.InternationalSettings.Commands.WinUserLanguage' required by parameter 'LanguageList'. Specified method is
not supported.
At line:1 char:25
+ set-winuserlanguagelist $langlist
+ ~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Set-WinUserLanguageList], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgument,Microsoft.InternationalSettings.Commands.SetWinUserLanguageListCommand
When I tried to use next command: $test = Get-WinUserLanguageList, the command works well with set-winuserlanguagelist.
The full script:
$keys='0809:00020409', '0419:00000419'
$lang_en=new-winuserlanguagelist en-gb
$lang_en[0].inputmethodtips.clear()
$lang_en[0].inputmethodtips.add($keys[0])
$lang_ru=new-winuserlanguagelist ru
$lang_ru[0].inputmethodtips.clear()
$lang_ru[0].inputmethodtips.add($keys[1])
$langlist=$lang_en,$lang_ru
set-winuserlanguagelist $langlist
Please check the following commented code snippets:
PS D:\PShell> ### Type mismatch
$langlist=$lang_en,$lang_ru
### Note the difference in type:
$langlist.gettype().Name ### Object[]
(Get-WinUserLanguageList).gettype().Name ### List`1
Object[]
List`1
PS D:\PShell> ### Use the following:
$langlist = Get-WinUserLanguageList
$langlist.Clear()
$langlist.Add($lang_en[0])
$langlist.Add($lang_ru[0])
$langlist.gettype().Name ### List`1
List`1
PS D:\PShell> <### The next cmdlet should work now:
set-winuserlanguagelist $langlist
<##>
The problem is that you're using New-WinUserLanguageList twice, with each call returning a list, so that $langlist = $lang_en, $lang_ru mistakenly created an array of (single-item) lists rather than a single list with two items, which caused the (nonsensically-sounding) type-mismatch error you saw.
Very awkwardly, however, cmdlet New-WinUserLanguageList only allows you to specify one language, even though it returns a list type ([Collections.Generic.List[Microsoft.InternationalSettings.Commands.WinUserLanguage]]).
That is, the following should work, but doesn't:
# Try to create the list with *2* entries
$langlist = New-WinUserLanguageList en-gb, ru # !! Doesn't work, parameter type is [string]
Instead, you have to initialize with 1 language and then add additional ones later, using the .Add() method:
# Create the list with initially just 'en-gb'...
$langlist = New-WinUserLanguageList en-gb
# ... and then add the other language, 'ru'
# Because the list is strongly typed, it is sufficient to pass the language
# identifier, which implicitly creates a new
# [Microsoft.InternationalSettings.Commands.WinUserLanguage] instance.
$langlist.Add('ru')
# Now you can modify the properties of $langlist[0] (en-gb)
# and $langlist[1] (ru)
# ...
# ... and pass the list of modified languages to Set-WinUserLanguageList:
Set-WinUserLanguageList $langlist
Alternatively, to avoid the .Add() call, you could have used:
$langlist = (New-WinUserLanguageList en-gb)[0], (New-WinUserLanguageList ru)[0]
Even though $langlist is then technically an array (a [System.Object[]] instance whose elements are of type Microsoft.InternationalSettings.Commands.WinUserLanguage), passing it to Set-WinUserLanguageList works, because it is implicitly converted to the required list type.

Is there a way to make a Powershell function ignore a default parameter's value if its parameter set is not in use?

I understand from this answer that when you have default parameters and multiple parameter sets in a Powershell function, the default parameter values will be instantiated even if the parameter set in use is not the one in which they are inserted.
Is there a way to avoid this?
For example, in the function below, assuming that there is a really expensive calculation used to compute the default value of $FirstParameter, I would like to avoid using it when it is not necessary:
function PrintStuff {
[CmdletBinding(DefaultParameterSetName='FirstSet')]
Param(
[Parameter(ParameterSetName='FirstSet')]
[String]$FirstParameter=(ReallyExpensiveFunction),
[Parameter(ParameterSetName='SecondSet')]
[String]$SecondParameter
)
if (-not ($FirstParameter -eq $null)) {Write-Host $FirstParameter}
Write-Host "$($PSCmdlet.ParameterSetName)"
}
function ReallyExpensiveFunction {
# Very expensive calculation
"I Am First"
}
However, at the moment running it would still give me the results below:
PS C:\> PrintStuff
# I Am First
# FirstSet
PS C:\> PrintStuff -SecondParameter "don't print this"
# I Am First
# SecondSet
As per above, when SecondSet is used $FirstParameter is still being defined. Is there a way to get only SecondSet printed when the second parameter set is used?
Bear in mind, I am looking to find out if there is a solution which would allow me to keep ReallyExpensiveFunction as the default value for $FirstParameter, and avoid solutions which would involve transferring the logic to the body of the function, such as:
...
Param(
[Parameter(ParameterSetName='FirstSet')]
[String]$FirstParameter,
...
)
if ($PSCmdlet.ParameterSetName -eq 'FirstSet' -and ($FirstParameter -eq '')) {
$FirstParameter = ReallyExpensiveFunction
}
...
Sorry if the pitch is too specific, but I am curious to find out if this is possible.
Unfortunately, the answer is no. ParameterSet allows to present a simpler interface to user for complex argument sets by filtering out the non-relevant ones. However, PowerShell goes through each parameter, whether it is in the selected parameterset or not and assign the default value to the parameter, if you specify one. So, simply put in the context of your question, ParameterSet may be thought as just a filter for presentation.