Populate parameter list from XML

后端 未结 3 841
盖世英雄少女心
盖世英雄少女心 2021-01-05 22:29

I need to pull a list of IDs into a powershell paramater validateset like so:

function Do-Stuff {
   [Cmdletbinding()] 
   param(
        [ValidateSet(\"Sea         


        
相关标签:
3条回答
  • 2021-01-05 23:07

    If your city names only contain alphanumeric characters, it might be a cleaner solution to create a custom enum type from the names, and specify your parameter value as that type:

    Add-Type -TypeDefinition @"
       // very simple enum type
       public enum ValidSites
       {
          Seattle,
          NewYork,
          London,
          Atlanta
       }
    "@
    
    function Do-Stuff {
       [Cmdletbinding()] 
       param( [ValidSites]$Site )
    }
    

    It should have the same effect as using ValidateSet (including enabling tab completion of the possible values), without having to modify a list set in the function itself.

    0 讨论(0)
  • 2021-01-05 23:12

    Had to to this for a recent project. Didn't realize how easy this was with an enum, thanks mjolinor!

    An alternative is to use Dynamic Parameters. Find help using Get-Help:

    #Look for the Dynamic Parameters section in here
    Get-Help about_Functions_Advanced_Parameters
    

    Other resources:

    • about_Functions_Advanced_Parameters Online help
    • This code from jrich523
    • New-DynamicParam, mostly borrowed from jrich523

    Current function definition:

    Function New-DynamicParam {
    <#
        .SYNOPSIS
            Helper function to simplify creating dynamic parameters
    
        .DESCRIPTION
            Helper function to simplify creating dynamic parameters
    
            Example use cases:
                Include parameters only if your environment dictates it
                Include parameters depending on the value of a user-specified parameter
                Provide tab completion and intellisense for parameters, depending on the environment
    
            Please keep in mind that all dynamic parameters you create will not have corresponding variables created.
               One of the examples illustrates a generic method for populating appropriate variables from dynamic parameters
               Alternatively, manually reference $PSBoundParameters for the dynamic parameter value
    
        .NOTES
            Credit to http://jrich523.wordpress.com/2013/05/30/powershell-simple-way-to-add-dynamic-parameters-to-advanced-function/
                Added logic to make option set optional
                Added logic to add RuntimeDefinedParameter to existing DPDictionary
                Added a little comment based help
    
        .PARAMETER Name
            Name of the dynamic parameter
    
        .PARAMETER ValidateSet
            If specified, set the ValidateSet attribute of this dynamic parameter
    
        .PARAMETER Mandatory
            If specified, set the Mandatory attribute for this dynamic parameter
    
        .PARAMETER ParameterSetName
            If specified, set the ParameterSet attribute for this dynamic parameter
    
        .PARAMETER Position
            If specified, set the Position attribute for this dynamic parameter
    
        .PARAMETER ValueFromPipelineByPropertyName
            If specified, set the ValueFromPipelineByPropertyName attribute for this dynamic parameter
    
        .PARAMETER HelpMessage
            If specified, set the HelpMessage for this dynamic parameter
    
        .PARAMETER DPDictionary
            If specified, add resulting RuntimeDefinedParameter to an existing RuntimeDefinedParameterDictionary (appropriate for multiple dynamic parameters)
            If not specified, create and return a RuntimeDefinedParameterDictionary (appropriate for a single dynamic parameter)
    
        .EXAMPLE
    
            function Show-Free
            {
                [CmdletBinding()]
                Param()
                DynamicParam {
                    $options = @( gwmi win32_volume | %{$_.driveletter} | sort )
                    New-DynamicParam -Name Drive -ValidateSet $options -Position 0 -Mandatory
                }
                begin{
                    #have to manually populate
                    $drive = $PSBoundParameters.drive
                }
                process{
                    $vol = gwmi win32_volume -Filter "driveletter='$drive'"
                    "{0:N2}% free on {1}" -f ($vol.Capacity / $vol.FreeSpace),$drive
                }
            } #Show-Free
    
            Show-Free -Drive <tab>
    
        # This example illustrates the use of New-DynamicParam to create a single dynamic parameter
        # The Drive parameter ValidateSet populates with all available volumes on the computer for handy tab completion / intellisense
    
        .EXAMPLE
    
        # I found many cases where I needed to add many dynamic parameters
        # The DPDictionary parameter lets you specify an existing dictionary
        # The block of code in the Begin block loops through bound parameters and defines variables if they don't exist
    
            Function Test-DynPar{
                [cmdletbinding()]
                param(
                    [string[]]$x = $Null
                )
                DynamicParam
                {
                    #Create the RuntimeDefinedParameterDictionary
                    $Dictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
    
                    New-DynamicParam -Name AlwaysParam -options @( gwmi win32_volume | %{$_.driveletter} | sort ) -DPDictionary $Dictionary
    
                    #Add dynamic parameters to $dictionary
                    if($x -eq 1)
                    {
                        New-DynamicParam -Name X1Param1 -Options 1,2 -mandatory -DPDictionary $Dictionary
                        New-DynamicParam -Name X1Param2 -DPDictionary $Dictionary
                        New-DynamicParam -Name X3Param3 -DPDictionary $Dictionary
                    }
                    else
                    {
                        New-DynamicParam -Name OtherParam1 -mandatory -DPDictionary $Dictionary
                        New-DynamicParam -Name OtherParam2 -DPDictionary $Dictionary
                        New-DynamicParam -Name OtherParam3 -DPDictionary $Dictionary
                    }
    
                    #return RuntimeDefinedParameterDictionary
                    $Dictionary
                }
                Begin
                {
                    #This standard block of code loops through bound parameters...
                    #If no corresponding variable exists, one is created
                        foreach($param in $PSBoundParameters.Keys)
                        {
                            if (-not ( Get-Variable -name $param -scope 0 -ErrorAction SilentlyContinue ) )
                            {
                                New-Variable -Name $Param -Value $PSBoundParameters.$param
                                Write-Verbose "Adding variable for dynamic parameter '$param' with value '$($PSBoundParameters.$param)'"
                            }
                        }
    
                    #Appropriate variables should now be defined and accessible
                        Get-Variable -scope 0
                }
            }
    
        # This example illustrates the creation of many dynamic parameters using New-DynamicParam
            # You must create a RuntimeDefinedParameterDictionary object ($dictionary here)
            # To each New-DynamicParam call, add the -DPDictionary parameter pointing to this RuntimeDefinedParameterDictionary
            # At the end of the DynamicParam block, return the RuntimeDefinedParameterDictionary
            # Initialize all bound parameters using the provided block or similar code
    #>
    param(
    
        [string]
        $Name,
    
        [string[]]
        $ValidateSet,
    
        [switch]
        $Mandatory,
    
        [string]
        $ParameterSetName="__AllParameterSets",
    
        [int]
        $Position,
    
        [switch]
        $ValueFromPipelineByPropertyName,
    
        [string]
        $HelpMessage,
    
        [validatescript({
            if(-not ( $_ -is [System.Management.Automation.RuntimeDefinedParameterDictionary] -or -not $_) )
            {
                Throw "DPDictionary must be a System.Management.Automation.RuntimeDefinedParameterDictionary object, or not exist"
            }
            $True
        })]
        $DPDictionary = $false
    
    )
        #Create attribute object, add attributes, add to collection   
            $ParamAttr = New-Object System.Management.Automation.ParameterAttribute
            $ParamAttr.ParameterSetName = $ParameterSetName
            if($mandatory)
            {
                $ParamAttr.Mandatory = $True
            }
            if($Position -ne $null)
            {
                $ParamAttr.Position=$Position
            }
            if($ValueFromPipelineByPropertyName)
            {
                $ParamAttr.ValueFromPipelineByPropertyName = $True
            }
            if($HelpMessage)
            {
                $ParamAttr.HelpMessage = $HelpMessage
            }
    
            $AttributeCollection = New-Object 'Collections.ObjectModel.Collection[System.Attribute]'
            $AttributeCollection.Add($ParamAttr)
    
        #param validation set if specified
            if($ValidateSet)
            {
                $ParamOptions = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList $ValidateSet
                $AttributeCollection.Add($ParamOptions)
            }
    
    
        #Create the dynamic parameter
            $Parameter = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter -ArgumentList @($Name, [string], $AttributeCollection)
    
        #Add the dynamic parameter to an existing dynamic parameter dictionary, or create the dictionary and add it
            if($DPDictionary)
            {
                $DPDictionary.Add($Name, $Parameter)
            }
            else
            {
                $Dictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
                $Dictionary.Add($Name, $Parameter)
                $Dictionary
            }
    }
    

    An example using this function:

    #Function has already been added to this session...
    function Do-Stuff {
        [cmdletbinding()] 
        param()
        DynamicParam
        {
            #Example borrowing from TheMadTechnician: 
            #New-DynamicParam -Name Site -ValidateSet $(([xml](gc c:\temp\config.xml)).getElementsByTagName("City").Name) -Mandatory
    
            #I don't have that file... simplification
            New-DynamicParam -Name Site -ValidateSet "Seattle", "NewYork", "London", "Atlanta" -Mandatory
        }
        Begin
        {
            #This standard block of code loops through bound parameters...
            #If no corresponding variable exists, one is created
                foreach($param in $PSBoundParameters.Keys)
                {
                    if (-not ( Get-Variable -name $param -scope 0 -ErrorAction SilentlyContinue ) )
                    {
                        New-Variable -Name $param -Value $PSBoundParameters.$param
                        Write-Verbose "Adding variable for dynamic parameter '$param' with value '$($PSBoundParameters.$param)'"
                    }
                }
            $Site
        }    
    }
    

    And finally, the end result:

    Example illustrating IntelliSense with DynamicParam

    One more example that has a few other dynamic parameters

    Function New-LabMachine
    {
        [cmdletbinding()]
        param(
            [ValidateSet("ADDSNew","ADDSExisting")]
            [string[]]
            $Role
        )
        DynamicParam
        {
            #Define dynamicparam dictionary.  Create a hashtable for splatting params
                $Dictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
                $dict = @{DPDictionary = $Dictionary}
    
            #Add dynamic parameters to populate and validate Lab names
                $Labs = "Lab1", "Lab2" #@( Get-Lab | Select -ExpandProperty LabName -ErrorAction SilentlyContinue )
                New-DynamicParam -Name LabName -Mandatory -ValidateSet $Labs @dict
    
            if($Role -contains 'ADDSNew')
            {            
                #AD Forest info
                    New-DynamicParam -Name DomainName -Mandatory @dict -HelpMessage "Provide domain name for first domain in forest"
                    New-DynamicParam -Name ForestMode -Mandatory -ValidateSet "Win2008","Win2008R2","Win2012","Win2012R2" @dict
            }
            if($Role -contains 'ADDSExisting')
            {
                New-DynamicParam -Name DomainName -Mandatory @dict
                New-DynamicParam -Name username -Mandatory @dict
                New-DynamicParam -Name password -Mandatory @dict
            }
    
            #Return the dictionary for dynamic params
            $Dictionary
        }
        Begin
        {
            #This standard block of code loops through bound parameters...
            #If no corresponding variable exists, one is created
                foreach($param in $PSBoundParameters.Keys )
                {
                    if (-not ( Get-Variable -name $param -scope 0 -ErrorAction SilentlyContinue ) -and "Verbose", "Debug" -notcontains $param )
                    {
                        New-Variable -Name $Param -Value $PSBoundParameters.$param -Description DynParam
                        Write-Verbose "Adding variable for dynamic parameter '$param' with value '$($PSBoundParameters.$param)'"
                    }
                }
    
            #Display the bound parameters      
            $PSBoundParameters.keys | ForEach-Object {Get-Variable -Name $_}
        }
    }
    

    And the results:

    enter image description here

    From my perspective, these can be very helpful to the end user. I typically use them to provide IntelliSense and tab completion support similar to what you are aiming for. As long as they provide you more value than their slight overhead and a little extra complexity, they are worth it : )

    My apologies for the wall of text! Cheers!

    0 讨论(0)
  • 2021-01-05 23:29

    Alternatively, if you want to use an file to validate against you can use ValidateScript instead of ValidateSet like such:

    function Do-Stuff {
       [Cmdletbinding()] 
       param(
            [ValidateScript({if(([xml](gc c:\temp\config.xml)).getElementsByTagName("City").Name -contains $_){$true}else{throw ([xml](gc c:\temp\config.xml)).getElementsByTagName("City").Name}} )]
            [String]$Site
        )
    

    That's based on the ridiculously simplistic XML file:

    <?xml version="1.0"?>
    <City_Validation_List>
        <City Name="Seattle"></City>
        <City Name="Atlanta"></City>
    </City_Validation_List>
    

    So, anyway, you can use ValidateScript to run a scriptblock as validation, in which you could load things from an XML file. ValidateSet on the other hand has to have a pre-defined array of strings to validate against.

    0 讨论(0)
提交回复
热议问题