Is it possible to automatically pass along a switch parameter in a PowerShell Function?

Viewed 291

I created a function with some inspiration from here.

It's working as expected so far but only when an Option Switch is provided when calling the function. What I want to do is just be able type "Get-ExternalIP" without providing a switch. The function should then automatically use the -All switch. Also I'm not sure why the -All switch even works...

I've tried to set the Parameter $All = $true it's not working and VSCode tells me that it's not recommended anyways.

Is there a way to automatically pass along the -All Option Switch when the function is called without any parameters? Would somebody be able to explain why the function returns all info if the -All switch is specified?

Thanks!

Here's my code:

function Get-ExternalIP {
  [CmdletBinding()]
  param (
    [parameter(Mandatory = $false)][switch]$Ip,
    [parameter(Mandatory = $false)][switch]$HostName,
    [parameter(Mandatory = $false)][switch]$City,
    [parameter(Mandatory = $false)][switch]$Region,
    [parameter(Mandatory = $false)][switch]$Country,
    [parameter(Mandatory = $false)][switch]$Location,
    [parameter(Mandatory = $false)][switch]$Provider,
    [parameter(Mandatory = $false)][switch]$PostalCode,
    [parameter(Mandatory = $false)][switch]$TimeZone,
    [parameter(Mandatory = $false)][switch]$All
  )

  $IpInfo = Invoke-RestMethod https://ipinfo.io/json

  switch ($PSBoundParameters.Values) {
    $Ip { $IpInfo.ip }
    $HostName { $IpInfo.hostname }
    $City { $IpInfo.city }
    $Region { $IpInfo.region }
    $Country { $IpInfo.country }
    $Location { $IpInfo.loc }
    $Provider { $IpInfo.org }
    $PostalCode { $IpInfo.postal }
    $TimeZone { $IpInfo.timezone }
    Default { $IpInfo }

  }

}

Get-ExternalIP
4 Answers

You could use parameter sets to default to the All option:

[CmdletBinding(DefaultParameterSetName='Everything')]
param(
    [Parameter(ParameterSetName='Something')][switch]$Ip,
    [Parameter(ParameterSetName='Something')][switch]$HostName,
    [Parameter(ParameterSetName='Something')][switch]$City,
    [Parameter(ParameterSetName='Something')][switch]$Region,
    [Parameter(ParameterSetName='Something')][switch]$Country,
    [Parameter(ParameterSetName='Something')][switch]$Location,
    [Parameter(ParameterSetName='Something')][switch]$Provider,
    [Parameter(ParameterSetName='Something')][switch]$PostalCode,
    [Parameter(ParameterSetName='Something')][switch]$TimeZone,
    [Parameter(ParameterSetName='Everything')][switch]$All
)

# set up dictionary to hold the switch-name-to-ipinfo-names
$Options = [Ordered]@{
  'Ip' = 'ip'
  'HostName' = 'hostname'
  'City' = 'city'
  'Region' = 'region'
  'Country' = 'country'
  'Location' = 'loc'
  'Provider' = 'org'
  'PostalCode' = 'postal'
  'TimeZone' = 'timezone'
}

$IpInfo = Invoke-RestMethod https://ipinfo.io/json

# Select all options
$selected = $Options.Keys
if($PSCmdlet.ParameterSetName -ne 'Everything'){
    # 1 or more switches were passed, select only those options
    $selected = $selected.Where({$PSBoundParameters[$_]})
}

# Create a new object with the selected options as properties
$properties = [ordered]@{}
foreach($prop in $selected){
  $properties[$prop] = $IpInfo.($Options[$prop])
}

# return the new object
return [pscustomobject]$properties

By using the parameter set name to determine whether to output everything (and then return), the behavior is the same regardless of whether the user passes -All, or no switches at all.

I would use the $PSBoundParameters.Keys as your switches are properly named to match the returned info:

function Get-ExternalIP {
    [CmdletBinding()]
    param (
        [parameter(Mandatory=$false)][switch]$Ip,
        [parameter(Mandatory=$false)][switch]$HostName,
        [parameter(Mandatory=$false)][switch]$City,
        [parameter(Mandatory=$false)][switch]$Region,
        [parameter(Mandatory=$false)][switch]$Country,
        [parameter(Mandatory=$false)][switch]$Location,
        [parameter(Mandatory=$false)][switch]$Provider,
        [parameter(Mandatory=$false)][switch]$PostalCode,
        [parameter(Mandatory=$false)][switch]$TimeZone,
        [parameter(Mandatory=$false)][switch]$All
    )
    $IpInfo = Invoke-RestMethod https://ipinfo.io/json
    if ($All) { return $IpInfo }

    # exclude Common Parameters
    $commonParams = 'Debug','ErrorAction','ErrorVariable','InformationAction','InformationVariable','OutVariable',
                    'OutBuffer','PipelineVariable','Verbose','WarningAction','WarningVariable','WhatIf','Confirm'

    $items = (@($PSBoundParameters.Keys) | Where-Object { $commonParams -notcontains $_ }) -replace
             'Location', 'loc' -replace 'Provider', 'org' -replace 'PostalCode', 'postal'

    $IpInfo | Select-Object $items
}

As mklement0 commented, there is a much better way of retrieving the used switches than shown in the above code.

Instead of filtering out the known Common Parameters, it makes more sense to check the $PSBoundParameter keys against the properties returned in the $IpInfo object.

$items = ($PSBoundParameters.Keys -replace 'Location', 'loc' -replace 'Provider', 'org' -replace 'PostalCode', 'postal').Where({ $_ -in $IpInfo.psobject.Properties.Name })

Mathias R. Jessen's answer and Theo's answer provide elegant solutions; let me complement them with an answer to your specific question:

Would somebody be able to explain why the function returns all info if the -All switch is specified?

Your switch statement has a Default branch, which is invoked if none of the other branches' conditions are met; since you don't have a branch for the -All switch value, the Default handler kicks in if
-All was specified
.

By contrast, if you don't pass any argument, the switch statement is never entered, because, for collection-valued input, the switch statement implicitly loops over the elements of that collection. With no arguments passed, $PSBoundParameters.Values is an empty collection, so there is nothing to enumerate.

The parameter-set approach in Mathias' answer is the simplest solution to this problem, which has the added advantage of making the -All switch and the specific property-name switches mutually exclusive.

If you print out what $psboundparameters.values is, you'll see it's empty when you don't use "-all", otherwise it has one value of $true.

Related