Thom Schumacher
Thom Schumacher

Reputation: 1583

Dynamic Parameters - with Dynamic ValidateSet

I have a script that I've been working on to provide parsing of SCCM log files. This script takes a computername and a location on disk to build a dynamic parameter list and then present it to the user to choose the log file they want to parse. Trouble is I cannot seem to get the ValidateSet portion of the dynamic parameter to provide values to the user. In addition the script won't display the -log dynamic parameter when attempting to call the function.

When you run it for the first time you are not presented with the dynamic parameter Log as I mentioned above. If you then use -log and then hit tab you’ll get the command completer for the files in the directory you are in. Not what you’d expect; you'd expect that it would present you the Logfile names that were gathered during the dynamic parameter execution.

PSVersion 5.1.14409.1012

So the question is how do I get PowerShell to present the proper Validate set items to the user?

enter image description here

If you issue one of the items in the error log you get the proper behavior: enter image description here

Here are the two functions that i use to make this possible:

function Get-CCMLog
{
    [CmdletBinding()]
    param([Parameter(Mandatory=$true,Position=0)]$ComputerName = '$env:computername', [Parameter(Mandatory=$true,Position=1)]$path = 'c:\windows\ccm\logs')
    DynamicParam
    {
        $ParameterName = 'Log'
        if($path.ToCharArray() -contains ':')
        {

            $FilePath = "\\$ComputerName\$($path -replace ':','$')"
            if(test-path $FilePath)
            {
                $logs = gci "$FilePath\*.log"
                $LogNames = $logs.basename

                $logAttribute = New-Object System.Management.Automation.ParameterAttribute
                $logAttribute.Position = 2
                $logAttribute.Mandatory = $true
                $logAttribute.HelpMessage = 'Pick A log to parse'                

                $logCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
                $logCollection.add($logAttribute)

                $logValidateSet = New-Object System.Management.Automation.ValidateSetAttribute($LogNames)
                $logCollection.add($logValidateSet)

                $logParam = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName,[string],$logCollection)

                $logDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
                $logDictionary.Add($ParameterName,$logParam)
                return $logDictionary
            }
        }
    }
    begin {
        # Bind the parameter to a friendly variable
        $Log = $PsBoundParameters[$ParameterName]
    }

    process {
        # Your code goes here
        #dir -Path $Path
        $sb2 = "$((Get-ChildItem function:get-cmlog).scriptblock)`r`n"
        $sb1 = [scriptblock]::Create($sb2)
        $results = Invoke-Command -ComputerName $ComputerName -ScriptBlock $sb1 -ArgumentList "$path\$log.log"
        [PSCustomObject]@{"$($log)Log"=$results}
    }
}
function Get-CMLog
{
    param(
    [Parameter(Mandatory=$true,
               Position=0,
               ValueFromPipelineByPropertyName=$true)]
    [Alias("FullName")]
    $Path,
    $tail =10
    )
    PROCESS
    {

        if(($Path -isnot [array]) -and (test-path $Path -PathType Container) )
        {
            $Path = Get-ChildItem "$path\*.log"
        }

        foreach ($File in $Path)
        {
            if(!( test-path $file))
            {
                $Path +=(Get-ChildItem "$file*.log").fullname
            }
            $FileName = Split-Path -Path $File -Leaf
            if($tail)
            {
                $lines = Get-Content -Path $File -tail $tail 
            }
            else {
                $lines = get-cotnet -path $file
            }
            ForEach($l in $lines ){
                $l -match '\<\!\[LOG\[(?<Message>.*)?\]LOG\]\!\>\<time=\"(?<Time>.+)(?<TZAdjust>[+|-])(?<TZOffset>\d{2,3})\"\s+date=\"(?<Date>.+)?\"\s+component=\"(?<Component>.+)?\"\s+context="(?<Context>.*)?\"\s+type=\"(?<Type>\d)?\"\s+thread=\"(?<TID>\d+)?\"\s+file=\"(?<Reference>.+)?\"\>' | Out-Null
                    if($matches)
                    {
                        $UTCTime = [datetime]::ParseExact($("$($matches.date) $($matches.time)$($matches.TZAdjust)$($matches.TZOffset/60)"),"MM-dd-yyyy HH:mm:ss.fffz", $null, "AdjustToUniversal")
                        $LocalTime = [datetime]::ParseExact($("$($matches.date) $($matches.time)"),"MM-dd-yyyy HH:mm:ss.fff", $null)
                    }
                    [pscustomobject]@{
                        UTCTime = $UTCTime
                        LocalTime = $LocalTime
                        FileName = $FileName
                        Component = $matches.component
                        Context = $matches.context
                        Type = $matches.type
                        TID = $matches.TI
                        Reference = $matches.reference
                        Message = $matches.message
                }
            }
        }
    }
}

Upvotes: 3

Views: 3717

Answers (2)

Martin Schvartzman
Martin Schvartzman

Reputation: 111

The problem is that you have all the dynamic logic inside scriptblock in the if statement, and it handles the parameter addition only if the path provided contains a semicolon (':'). You could change it to something like:

if($path.ToCharArray() -contains ':') {
    $FilePath = "\\$ComputerName\$($path -replace ':','$')"
} else {
    $FilePath = $path
}

and continue your code from there

Upvotes: 2

Related Questions