Get-WebInput.ps1
function Get-WebInput { <# .Synopsis Get the Web Request parameters for a PowerShell command .Description Get the Web Request parameters for a PowerShell command. Script Blocks parameters will automatically be run, and text values will be converted to their native types. .Example Get-WebInput -CommandMetaData (Get-Command Get-Command) -DenyParameter ArgumentList .Link Request-CommandInput #> [OutputType([Hashtable])] param( # The metadata of the command that is being wrapped [Parameter(Mandatory=$true,ValueFromPipeline=$true)] [Management.Automation.CommandMetaData] $CommandMetaData, # The parameter set within the command [string] $ParameterSet, # Explicitly allowed parameters (by default, all are allowed unless they are explictly denied) [string[]] $AllowedParameter, # Explicitly denied parameters. [string[]] $DenyParameter, # Any aliases for parameter names. [Hashtable]$ParameterAlias, # A UI element containing that will contain all of the values. If this option is used, the module ShowUI should also be loaded. $Control ) process { $webParameters = @{} $safecommandName = $commandMetaData.Name.Replace("-", "") $webParameterNames = $commandMetaData.Parameters.Keys $webParameterNames = $webParameterNames | Where-Object { $DenyParameter -notcontains $_ } | ForEach-Object -Begin { if ($ParameterAlias) { foreach ($k in $ParameterAlias.Keys) { $k } } } -Process { if ($_ -ne "URL") { } else { } "$($CommandMetaData.Name)_$_" } $help = Get-Help -Name $CommandMetaData.Name -ErrorAction SilentlyContinue if ($request.Params -is [Hashtable]) { $paramNames = $request.Params.Keys } else { $paramNames = @($request.Params) + ($request.Files) + @($request.Headers) } if ($Control) { if (-not $ExecutionContext.SessionState.InvokeCommand.GetCommand("Get-ChildControl", "All")) { return } $uiValue = @{} $uivalue = Get-ChildControl -Control $control -OutputNamedControl foreach ($kv in @($uivalue.GetEnumerator())) { if (($kv.Key -notlike "${SafeCommandName}_*")) { $uiValue.Remove($kv.Key) } } foreach ($kv in @($uiValue.GetEnumerator())) { if ($kv.Value.Text) { $uiValue[$kv.Key] = $kv.Value.Text } elseif ($kv.Value.SelectedItems) { $uiValue[$kv.Key] = $kv.Value.SelectedItems } elseif ($kv.Value -is [Windows.Controls.Checkbox] -and $kv.Value.IsChecked) { $uiValue[$kv.Key] = $kv.Value.IsChecked } elseif ($kv.Value.Password) { $uiValue[$kv.Key] = $kv.Value.Password } else { $uiValue.Remove($kv.Key) } } $webParameterNames = $webParameterNames | ForEach-Object { $_.Replace("-","") } $paramNames = $uiValue.Keys | ForEach-Object { $_.Trim() } } foreach ($param in $paramNames) { if ($webParameterNames -notcontains $param) { continue } $parameterHelp = foreach ($p in $help.Parameters.Parameter) { if ($p.Name -eq $parameter) { $p.Description | Select-Object -ExpandProperty Text } } #$parameterVisibleHelp = $parameterHelp -split ("[`n`r]") |? { $_ -notlike "|*" } $pipeworksDirectives = @{} foreach ($line in $parameterHelp -split ("[`n`r]")) { if ($line -like "|*") { $directiveEnd= $line.IndexofAny(": `n`r".ToCharArray()) if ($directiveEnd -ne -1) { $name, $rest = $line.Substring(1, $directiveEnd -1).Trim(), $line.Substring($directiveEnd +1).Trim() $pipeworksDirectives.$Name = $rest } else { $name = $line.Substring(1).Trim() $pipeworksDirectives.$Name = $true } } } if ($request.Params -is [Hashtable]) { $value = $request.Params[$param] } elseif ($request) { $value = $request[$param] if ((-not $value) -and $request.Files) { $value = $request.Files[$param] } if ((-not $value) -and $request.Headers) { $value = $request.Headers[$param] } } elseif ($uiValue) { $value = $uiValue[$param] } if (-not $value) { if ([string]::IsNullOrEmpty($value)) { continue } if ($value -ne 0){ # Do not skip the the value is really 0 continue } } if ($value -and $value.Trim()[-1] -eq '=') { # Make everything handle base64 input (as long as it's not to short to be an accident) $valueFromBase64 = try { [Text.Encoding]::Unicode.GetString([Convert]::FromBase64String($value)) } catch { } } # If the value was passed in base64, convert it if ($valueFromBase64) { $value = $valueFromBase64 } # If there was no value, quit if (-not $value) { $expectedparameterType = $commandMetaData.Parameters[$realParamName].ParameterType if (-not ([int], [uint32], [double] -contains $expectedparameterType)) { continue } } if ($parameterAlias -and $parameterAlias[$param]) { $realParamName = $parameterAlias[$param] } else { $realParamName= $param -iReplace "$($CommandMetaData.Name)_", "" -ireplace "$($commandMetaData.Name.Replace('-',''))_", "" } #region Coerce Type $expectedparameterType = $commandMetaData.Parameters[$realParamName].ParameterType if ($pipeworksDirectives.FileName) { if ($request.Files) { $value = $request.Files.AllKeys -as $expectedparameterType continue } continue } if ($expectedParameterType -eq [ScriptBlock]) { # Script Blocks are converted after being trimmed. $valueAsType = [ScriptBlock]::Create($value.Trim()) if ($valueAsType -and ($valueAsType.ToString().Length -gt 1)) { $webParameters[$realParamName] = $valueAsType } } elseif ($expectedParameterType -eq [Security.SecureString]) { $trimmedValue = $value.Trim() if ($trimmedValue) { $webParameters[$realParamName] = ConvertTo-SecureString -AsPlainText -Force $trimmedValue } } elseif ([switch], [bool] -contains $expectedParameterType) { # Switches and bools do a check for false, otherwise true if ($value -ilike "false") { $webParameters[$realParamName] = $false } else { $webParameters[$realParamName] = $true } } elseif ($ExpectedParameterType -eq [Hashtable] -or $expectedparameterType -eq [Hashtable[]] -or $expectedparameterType -eq [PSObject] -or $expectedparameterType -eq [PSObject[]]) { $trimmedValue = $value.Trim().Trim([Environment]::newline).Trim() if (($TrimmedValue.StartsWith("[") -or $trimmedValue.StartsWith("{")) -and $PSVersionTable.PSVersion -ge '3.0') { # JSON $webParameters[$realParamName] = ConvertFrom-Json -InputObject $trimmedValue } elseif ($trimmedValue -like "*@{*") { # PowerShell Hashtable $asScriptBlock = try { [ScriptBlock]::Create($trimmedValue) } catch { } if (-not $asScriptBlock) { continue } # If it's a script block, make a data language block around it, and catch $asDataLanguage= try { [ScriptBlock]::Create("data { $asScriptBlock }") } catch { } if (-not $asDataLanguage) { continue } # Run the data language block if ($expectedparameterType -eq [PSObject] -or $expectedparameterType -eq [PSObject[]]) { $webParameters[$realParamName] = foreach ($d in & $asDataLanguage) { $typeName = if ($d.PSTypeName) { $d.PSTypeName $d.Remove("PSTypeName") } else { "" } $o = New-Object PSObject -Property $d if ($typename) { $o.pstypenames.clear() $o.pstypenames.add($typeName) } $o } } else { $webParameters[$realParamName] = & $asDataLanguage } } elseif ($trimmedValue) { $multivalue = $value -split "-{3,}" $webParameters[$realParamName] = foreach ($mv in $multivalue) { $fromStringData = ConvertFrom-StringData -StringData $mv if ($fromStringData -and ($expectedparameterType -eq [PSObject] -or $expectedparameterType -eq [PSObject[]])) { $d = New-Object PSObject -Property $fromStringData $typeName = if ($d.PSTypeName) { $d.PSTypeName $d.Remove("PSTypeName") } else { "" } $o = New-Object PSObject -Property $d if ($typename) { $o.pstypenames.clear() $o.pstypenames.add($typeName) } $o } else { $fromStringData } } } } elseif ($ExpectedParameterType.IsArray) { # If it's an array, split each line and coerce the line into the correct type if ($expectedparameterType -eq [string[]]) { # String arrays are split on | or newlines $valueAsType = @($value -split "[$([Environment]::NewLine)|]" -ne '' | ForEach-Object { $_.Trim() }) -as $expectedParameterType } elseif ($expectedParameterType -eq [Byte[]]) { # If it's a byte array, try to read the input stream for the value $is = $value.InputStream if (-not $is) { continue } $buffer = New-Object Byte[] $is.Length $read = $is.Read($buffer, 0, $is.Length) $null = $read $valueAsType = $buffer } else { # Everything else is split on |, newlines, or commas $valueAsType = @($value -split "[$([Environment]::NewLine)|,]" | ForEach-Object { $_.Trim() }) -as $expectedParameterType } if ($valueAsType) { $webParameters[$realParamName] = $valueAsType } } else { # In the default case, we can just coerce the value. # PowerShell's casting magic will handle the rest. if ($expectedParameterType) { $valueAsType = $value -as $expectedparameterType if ($valueAsType) { $webParameters[$realParamName] = $valueAsType } } else { $webParameters[$realParamName] = $value } } #endregion Coerce Type } $finalParams = @{} foreach ($wp in $webParameters.GetEnumerator()) { if (-not $wp) {continue } if (-not $wp.Value) { continue } $finalParams[$wp.Key] = $wp.Value } $finalParams } } |