From c1826bb8ab725c50a4a7c3e0720393a31927dd05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Bere=C5=BCa=C5=84ski?= Date: Wed, 24 Feb 2021 01:03:47 +0100 Subject: [PATCH 1/5] MSFT_PackageManagement: Refactor AdditionalParameters processing out of Get/Set-TargetResource --- .../MSFT_PackageManagement.psm1 | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 b/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 index 0e07c3e2c..86b88b1cb 100644 --- a/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 +++ b/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 @@ -98,15 +98,8 @@ function Get-TargetResource $null = $PSBoundParameters.Remove("Source") $null = $PSBoundParameters.Remove("SourceCredential") - if ($AdditionalParameters) - { - foreach($instance in $AdditionalParameters) - { - Write-Verbose ('AdditionalParameter: {0}, AdditionalParameterValue: {1}' -f $instance.Key, $instance.Value) - $null = $PSBoundParameters.Add($instance.Key, $instance.Value) - } - } - $null = $PSBoundParameters.Remove("AdditionalParameters") + Add-AdditionalParameters -ParametersDictionary $PSBoundParameters -AdditionalParameters $AdditionalParameters + $PSBoundParameters.Remove("AdditionalParameters") $verboseMessage =$localizedData.StartGetPackage -f (GetMessageFromParameterDictionary $PSBoundParameters),$env:PSModulePath Write-Verbose -Message $verboseMessage @@ -351,15 +344,7 @@ function Set-TargetResource $null = $PSBoundParameters.Remove("SourceCredential") } - if ($AdditionalParameters) - { - foreach($instance in $AdditionalParameters) - { - Write-Verbose ('AdditionalParameter: {0}, AdditionalParameterValue: {1}' -f $instance.Key, $instance.Value) - $null = $PSBoundParameters.Add($instance.Key, $instance.Value) - } - } - + Add-AdditionalParameters -ParametersDictionary $PSBoundParameters -AdditionalParameters $AdditionalParameters $PSBoundParameters.Remove("AdditionalParameters") @@ -393,5 +378,29 @@ function Set-TargetResource return $returnValue } +function Add-AdditionalParameters +{ + [CmdletBinding()] + param + ( + [Parameter()] + [System.Collections.IDictionary] + $ParametersDictionary, + + [Parameter()] + [Microsoft.Management.Infrastructure.CimInstance[]] + $AdditionalParameters + ) + + if ($AdditionalParameters) + { + foreach($instance in $AdditionalParameters) + { + Write-Verbose ('AdditionalParameter: {0}, AdditionalParameterValue: {1}' -f $instance.Key, $instance.Value) + $null = $ParametersDictionary.Add($instance.Key, $instance.Value) + } + } +} + Export-ModuleMember -function Get-TargetResource, Set-TargetResource, Test-TargetResource From 6fad704855348e6b65177ddd61f1a641a547d930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Bere=C5=BCa=C5=84ski?= Date: Wed, 24 Feb 2021 01:05:11 +0100 Subject: [PATCH 2/5] MSFT_PackageManagement: Support passing AdditionalParameters typed as switch or bool Syntax: AdditionalParameters = @{ '[switch]InstallUpdate' = 'True' } Fixes https://github.com/OneGet/oneget/issues/327. --- .../MSFT_PackageManagement.psm1 | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 b/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 index 86b88b1cb..937d5ba0c 100644 --- a/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 +++ b/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 @@ -392,12 +392,26 @@ function Add-AdditionalParameters $AdditionalParameters ) + $rxTypePrefix = [regex]'^(?i)\[(?[a-z]+)\]' if ($AdditionalParameters) { foreach($instance in $AdditionalParameters) { Write-Verbose ('AdditionalParameter: {0}, AdditionalParameterValue: {1}' -f $instance.Key, $instance.Value) - $null = $ParametersDictionary.Add($instance.Key, $instance.Value) + $key = $instance.Key + $value = $instance.Value + $match = $rxTypePrefix.Match($key) + if ($match.Success) + { + if (@('switch', 'bool') -icontains $match.Groups['type'].Value) + { + Write-Verbose ('Parsing parameter ''{0}'' value ''{1}'' as bool and removing type prefix ''{2}''' -f $key, $value, $match.Value) + $key = $key.Substring($match.Length) + $value = [bool]::Parse($value) + } + } + + $null = $ParametersDictionary.Add($key, $value) } } } From 905955fad781b0de9c2efe8fd6a934c3b6f7e742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Bere=C5=BCa=C5=84ski?= Date: Wed, 24 Feb 2021 23:02:20 +0100 Subject: [PATCH 3/5] MSFT_PackageManagement: Do not require the user to specify a switch/bool AdditionalParameter type --- .../MSFT_PackageManagement.psm1 | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 b/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 index 937d5ba0c..ced912900 100644 --- a/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 +++ b/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 @@ -98,7 +98,7 @@ function Get-TargetResource $null = $PSBoundParameters.Remove("Source") $null = $PSBoundParameters.Remove("SourceCredential") - Add-AdditionalParameters -ParametersDictionary $PSBoundParameters -AdditionalParameters $AdditionalParameters + Add-AdditionalParameters -ParametersDictionary $PSBoundParameters -AdditionalParameters $AdditionalParameters -IntendedCommand 'PackageManagement\Get-Package' $PSBoundParameters.Remove("AdditionalParameters") $verboseMessage =$localizedData.StartGetPackage -f (GetMessageFromParameterDictionary $PSBoundParameters),$env:PSModulePath @@ -343,9 +343,6 @@ function Set-TargetResource $PSBoundParameters.Add("Credential", $SourceCredential) $null = $PSBoundParameters.Remove("SourceCredential") } - - Add-AdditionalParameters -ParametersDictionary $PSBoundParameters -AdditionalParameters $AdditionalParameters - $PSBoundParameters.Remove("AdditionalParameters") # We do not want others to control the behavior of ErrorAction @@ -353,10 +350,14 @@ function Set-TargetResource $PSBoundParameters.Remove("ErrorAction") if ($Ensure -eq "Present") { + Add-AdditionalParameters -ParametersDictionary $PSBoundParameters -AdditionalParameters $AdditionalParameters -IntendedCommand 'PackageManagement\Install-Package' + $PSBoundParameters.Remove("AdditionalParameters") PackageManagement\Install-Package @PSBoundParameters -ErrorAction Stop } else { + Add-AdditionalParameters -ParametersDictionary $PSBoundParameters -AdditionalParameters $AdditionalParameters -IntendedCommand 'PackageManagement\Uninstall-Package' + $PSBoundParameters.Remove("AdditionalParameters") # we dont source location for uninstalling an already # installed package $PSBoundParameters.Remove("Source") @@ -389,27 +390,34 @@ function Add-AdditionalParameters [Parameter()] [Microsoft.Management.Infrastructure.CimInstance[]] - $AdditionalParameters + $AdditionalParameters, + + [Parameter(Mandatory = $true)] + [string] + $IntendedCommand ) - $rxTypePrefix = [regex]'^(?i)\[(?[a-z]+)\]' if ($AdditionalParameters) { + $cmd = Get-Command -Name $IntendedCommand foreach($instance in $AdditionalParameters) { Write-Verbose ('AdditionalParameter: {0}, AdditionalParameterValue: {1}' -f $instance.Key, $instance.Value) $key = $instance.Key $value = $instance.Value - $match = $rxTypePrefix.Match($key) - if ($match.Success) + $paramMetadata = $cmd.Parameters[$key] + if ($null -ne $paramMetadata) { - if (@('switch', 'bool') -icontains $match.Groups['type'].Value) + if ($paramMetadata.ParameterType -eq [switch] -or $paramMetadata.ParameterType -eq [bool]) { - Write-Verbose ('Parsing parameter ''{0}'' value ''{1}'' as bool and removing type prefix ''{2}''' -f $key, $value, $match.Value) - $key = $key.Substring($match.Length) + Write-Verbose ('Parameter ''{0}'' is typed as ''{1}'', parsing value ''{2}'' as bool' -f $key, $paramMetadata.ParameterType, $value) $value = [bool]::Parse($value) } } + else + { + Write-Warning ('AdditionalParameter ''{0}'' is not present in the metadata of command {1}' -f $key, $IntendedCommand) + } $null = $ParametersDictionary.Add($key, $value) } From 68ddc94787d01738b0f9e616286ebcc1331aac9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Bere=C5=BCa=C5=84ski?= Date: Wed, 15 Sep 2021 22:31:38 +0200 Subject: [PATCH 4/5] MSFT_PackageManagement: Fix conversion of AdditionalParameters for nondefault providers Providers which are not included in PackageManagement, such as DockerMsftProvider, are not loaded automatically. As a consequence, their dynamic parameters are not returned by Get-Command. The fix is to make sure the provider is loaded, which can be done if the ProviderName is specified (always a good idea). Also, in order to improve the fidelity of parameter type detection, it is better to look only at parameters exposed by that provider. Conveniently, for each provider, a separate ParameterSet is defined, named after that provider. --- .../MSFT_PackageManagement.psm1 | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 b/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 index ced912900..41ca4b044 100644 --- a/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 +++ b/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 @@ -399,24 +399,52 @@ function Add-AdditionalParameters if ($AdditionalParameters) { + $providerName = $ParametersDictionary['ProviderName'] + if ([string]::IsNullOrEmpty($providerName)) + { + Write-Warning 'Please specify ProviderName when using AdditionalParameters, otherwise they may not work correctly.' + } + else + { + # trigger import of the provider, so that Get-Command exposes its dynamic parameters + Get-PackageProvider -Name $providerName | Out-Null + } + $cmd = Get-Command -Name $IntendedCommand + $cmdProviderParameterSet = $cmd.ParameterSets | Where-Object { -not [string]::IsNullOrEmpty($providerName) -and $_.Name -eq $providerName } + if ($null -eq $cmdProviderParameterSet) + { + $cmdParametersSource = 'Parameters collection' + Write-Debug 'Using the flat list of command parameters' + $cmdParametersLookup = $cmd.Parameters + } + else + { + $cmdParametersSource = 'parameter set ''{0}''' -f $cmdProviderParameterSet.Name + Write-Debug ('Using the provider-specific command {0}' -f $cmdParametersSource) + $cmdParametersLookup = @{} + $cmdProviderParameterSet.Parameters | ForEach-Object { $cmdParametersLookup[$_.Name] = $_ } + } + foreach($instance in $AdditionalParameters) { - Write-Verbose ('AdditionalParameter: {0}, AdditionalParameterValue: {1}' -f $instance.Key, $instance.Value) + Write-Verbose ('AdditionalParameter: {0}, value: {1}' -f $instance.Key, $instance.Value) $key = $instance.Key $value = $instance.Value - $paramMetadata = $cmd.Parameters[$key] + $paramMetadata = $cmdParametersLookup[$key] if ($null -ne $paramMetadata) { - if ($paramMetadata.ParameterType -eq [switch] -or $paramMetadata.ParameterType -eq [bool]) + $parameterType = $paramMetadata.ParameterType + Write-Debug ('AdditionalParameter: {0} is typed as {1} according to the metadata of command {2} ({3})' -f $key, $parameterType, $IntendedCommand, $cmdParametersSource) + if ($parameterType -eq [switch] -or $parameterType -eq [bool]) { - Write-Verbose ('Parameter ''{0}'' is typed as ''{1}'', parsing value ''{2}'' as bool' -f $key, $paramMetadata.ParameterType, $value) + Write-Debug ('Parsing AdditionalParameter ''{0}'' value ''{1}'' as bool' -f $key, $value) $value = [bool]::Parse($value) } } else { - Write-Warning ('AdditionalParameter ''{0}'' is not present in the metadata of command {1}' -f $key, $IntendedCommand) + Write-Warning ('AdditionalParameter ''{0}'' is not present in the metadata of command {1} ({2}). This probably means that the provider does not support that parameter.' -f $key, $IntendedCommand, $cmdParametersSource) } $null = $ParametersDictionary.Add($key, $value) From 7d69ae06d69310e1db9b8b95ca2df2eb3ac53d96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Bere=C5=BCa=C5=84ski?= Date: Wed, 15 Sep 2021 22:31:59 +0200 Subject: [PATCH 5/5] MSFT_PackageManagement: Handle parameter differences between cmdlets 1) fix cmdlet ParameterSet lookup to account for naming differences 2) add support for AdditionalParameters defined only for (Un)Install-Package and not for Get-Package (such as AcceptLicense for PowerShellGet) --- .../MSFT_PackageManagement.psm1 | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 b/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 index 41ca4b044..150bfe3fd 100644 --- a/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 +++ b/src/Microsoft.PackageManagement.DscResources/MSFT_PackageManagement/MSFT_PackageManagement.psm1 @@ -411,7 +411,21 @@ function Add-AdditionalParameters } $cmd = Get-Command -Name $IntendedCommand - $cmdProviderParameterSet = $cmd.ParameterSets | Where-Object { -not [string]::IsNullOrEmpty($providerName) -and $_.Name -eq $providerName } + $skipUndeclaredParameters = $false + if ($cmd.Verb -eq 'Get') + { + $skipUndeclaredParameters = $true + } + + $cmdProviderParameterSet = $null + if (-not [string]::IsNullOrEmpty($providerName)) + { + $cmdProviderParameterSet = $cmd.ParameterSets ` + | Where-Object { $_.Name -match "^${providerName}(\:PackageBySearch)?$" } ` + | Sort-Object -Property @{ Expression = { $_.Name.Length }; Descending = $true } ` + | Select-Object -First 1 + } + if ($null -eq $cmdProviderParameterSet) { $cmdParametersSource = 'Parameters collection' @@ -444,7 +458,15 @@ function Add-AdditionalParameters } else { - Write-Warning ('AdditionalParameter ''{0}'' is not present in the metadata of command {1} ({2}). This probably means that the provider does not support that parameter.' -f $key, $IntendedCommand, $cmdParametersSource) + if ($skipUndeclaredParameters) + { + Write-Verbose ('AdditionalParameter ''{0}'' is not present in the metadata of command {1} ({2}). This probably means that the provider does not support that parameter. Skipping the parameter.' -f $key, $IntendedCommand, $cmdParametersSource) + continue + } + else + { + Write-Warning ('AdditionalParameter ''{0}'' is not present in the metadata of command {1} ({2}). This probably means that the provider does not support that parameter.' -f $key, $IntendedCommand, $cmdParametersSource) + } } $null = $ParametersDictionary.Add($key, $value)