Utils.ps1
function Add-DynamicParameter { <# .SYNOPSIS Add a new dynamic parameter to the parameter sets. .PARAMETER Name The name of the parameter to add. .PARAMETER Type The type of the parameter to add. Switch is supported as well, but not recommended (they should be defined as static parameters). .PARAMETER ParamDict The dictionary containing the parameter definitions. This value is passed by reference. .PARAMETER Mandatory Indicates if the parameter is mandatory. .PARAMETER Position The position of the parameter. The first parmeter must have position 0. .PARAMETER ValidateSet The validate set to use. .EXAMPLE Populate the parameter set with 3 parameters. Add-DynamicParameter 'DynamicParam' String $runtimeParameterDictionary -Mandatory $True -Position 0 -ValidateSet @("Dyn1", "Dyn2", "Dyn3") Add-DynamicParameter 'DynamicParamInt' Int $runtimeParameterDictionary -Mandatory $True -Position 1 Add-DynamicParameter 'DynamicParamSwitch' Switch $runtimeParameterDictionary -Mandatory $False -Position 2 .NOTES If at least one dynamic parameter is required, all non switch parameters should be dynamic. Otherwise positional attribute set will not work. #> param( [Parameter(Mandatory=$True)] [String] $Name, [Parameter(Mandatory=$True)] [System.Type] $Type, [Parameter(Mandatory=$True)] [System.Management.Automation.RuntimeDefinedParameterDictionary][ref] $ParamDict, [Boolean] $Mandatory = $false, [Int] $Position = $null, [Array] $ValidateSet = $null ) process { $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] # Create and set the parameters' attributes $parameterAttribute = New-Object System.Management.Automation.ParameterAttribute $parameterAttribute.Mandatory = $Mandatory if($null -ne $Position) { $parameterAttribute.Position = $Position } # Add the attributes to the attributes collection $attributeCollection.Add($parameterAttribute) # Add the validation set to the attributes collection if(($null -ne $ValidateSet) -and ($ValidateSet.Count -gt 0)) { $validateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($ValidateSet) $attributeCollection.Add($validateSetAttribute) } # Create and return the dynamic parameter $runtimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($Name, $Type, $attributeCollection) $ParamDict.Add($Name, $runtimeParameter) } } function Show-SelectDialogue([array] $Options, [string] $Header) { <# .SYNOPSIS Show a selection dialog for the given values. .DESCRIPTION This function will show an input selection for all values that are defined. .PARAMETER Options The options to display. .PARAMETER Header The question to display. .OUTPUTS The selected value or $null if no element was selected. #> Write-InformationColored -InformationAction 'Continue' "$($Header):" Write-InformationColored -InformationAction 'Continue' "" $indexPathMap = @{} if($Options.Count -eq 0) { return $null } if($Options.Count -eq 1) { return $Options[0] } $i = 1 foreach ($option in $Options) { Write-InformationColored -InformationAction 'Continue' "[$i] $option" $indexPathMap[$i] = $option $i++ } $selectedIndex = Read-Host -Prompt " " Write-Verbose "Got selected index $selectedIndex and possibilities $($Options.Count)" if(-not($selectedIndex -match '^[0-9]+$')) { Write-Error "Invalid index specified" return $null } $selectedIndex = [int]($selectedIndex) if(($selectedIndex -lt 1) -or ($selectedIndex -gt $Options.Length)) { Write-Error "Invalid index specified" return $null } Write-Verbose "The selected option is $($indexPathMap[$selectedIndex])" Write-Verbose "Calculated selected index $selectedIndex - for possibilities $Options" return $indexPathMap[$selectedIndex] } function Show-ConfirmDialogue([string] $Message) { <# .SYNOPSIS Show a dialogue that asks for confirmation. .DESCRIPTION This function will show the displayed message and requires a yes/no response from the user. .PARAMETER Header The question to display. .OUTPUTS $True if the user has answered "yes". #> $result = Read-Host "$Message [y/n]" if($result.ToLower() -ne "y") { return $false } return $true } function Test-PathPartOfEnvironmentVariable([String] $Path, [String] $Variable) { <# .SYNOPSIS Check if a path is part of an environment variable. .DESCRIPTION The function will check if the given path is part of the specified environment variable. .PARAMETER Path The path to check. .PARAMETER Variable The environment variable to consider. .OUTPUTS $True if the path was identified in the variable. #> $variableValue = [System.Environment]::GetEnvironmentVariable($Variable) if(-not $variableValue) { return $false } $Path = [System.IO.Path]::GetFullPath($Path) # We normalize the path foreach($part in $variableValue.Split([IO.Path]::PathSeparator)) { if([String]::IsNullOrEmpty($part)) { continue } $part = [System.IO.Path]::GetFullPath($part) if($part.Equals($Path, [System.StringComparison]::InvariantCultureIgnoreCase)) { return $true } } return $false } function Write-InformationColored { <# Taken from https://blog.kieranties.com/2018/03/26/write-information-with-colours .SYNOPSIS Writes messages to the information stream, optionally with color when written to the host. .DESCRIPTION An alternative to Write-Host which will write to the information stream and the host (optionally in colors specified) but will honor the $InformationPreference of the calling context. In PowerShell 5.0+ Write-Host calls through to Write-Information but will _always_ treats $InformationPreference as 'Continue', so the caller cannot use other options to the preference variable as intended. #> [CmdletBinding()] param( [Parameter(Mandatory)] [Object] $MessageData, [ConsoleColor] $ForegroundColor = $Host.UI.RawUI.ForegroundColor, # Make sure we use the current colours by default [ConsoleColor] $BackgroundColor = $Host.UI.RawUI.BackgroundColor, [Switch] $NoNewline ) $msg = [System.Management.Automation.HostInformationMessage]@{ Message = $MessageData ForegroundColor = $ForegroundColor BackgroundColor = $BackgroundColor NoNewline = $NoNewline.IsPresent } Write-Information $msg } function Resolve-NotExistingPath { <# .SYNOPSIS Calls Resolve-Path but works for files that don't exist. .REMARKS From http://devhawk.net/blog/2010/1/22/fixing-powershells-busted-resolve-path-cmdlet #> param ( [string] $FilePath ) $FilePath = Resolve-Path $FilePath -ErrorAction SilentlyContinue ` -ErrorVariable _frperror if (-not($FilePath)) { $FilePath = $_frperror[0].TargetObject } return $FilePath } class ParameterInfoKeyComparator : System.Collections.Generic.IEqualityComparer[Tuple[string, string]] { [bool] Equals([Tuple[string, string]] $t1, [Tuple[string, string]] $t2) { if (($null -eq $t1) -and ($null -eq $t2)) { return $true; } if (($null -eq $t1) -or ($null -eq $t2)) { return $false; } return (($t1.Item1?.ToLower() -eq $t2.Item1?.ToLower()) -and ($t1.Item2?.ToLower() -eq $t2.Item2?.ToLower())) } [int] GetHashCode([Tuple[string, string]] $value) { $result = 0 if($null -ne $value.Item1) { $result = $result -bxor $value.Item1.ToLower().GetHashCode() } if($null -ne $value.Item2) { $result = $result -bxor $value.Item2.ToLower().GetHashCode() } return $result } } |