ansible-collections / community.windows

Windows community collection for Ansible
https://galaxy.ansible.com/community/windows
GNU General Public License v3.0
206 stars 160 forks source link

Issue #147 #588

Closed mfortin closed 2 weeks ago

mfortin commented 3 weeks ago
SUMMARY

Attempting to fix Issue #147 as a confirmation is required when it should not.

ISSUE TYPE
COMPONENT NAME

win_psmodule

ADDITIONAL INFORMATION

Before:

The full traceback is:
Exception calling "ShouldContinue" with "2" argument(s): "A command that prompts the user failed because the host program or the command type does not support user interaction. The host was attempting to request confirmation with the following message: PowerShellGet requires NuGet provider version '2.8.5.201' or newer to interact with NuGet-based repositories. The NuGet provider must be available in 'C:\Program Files\PackageManagement\ProviderAssemblies' or
'C:\Users\user\AppData\Local\PackageManagement\ProviderAssemblies'. You can also install the NuGet provider by running 'Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force'. Do you want PowerShellGet to install and import the NuGet provider now?"
At line:131 char:13
+             $null = $job | Receive-Job -AutoRemoveJob -Wait -ErrorAct ...
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Install-NuGetClientBinaries], MethodInvocationException
    + FullyQualifiedErrorId : HostException,Install-NuGetClientBinaries

at Install-NuGetClientBinaries, C:\Program Files\WindowsPowerShell\Modules\PowerShellGet\1.0.0.1\PSModule.psm1: line 7392
at Install-Module, C:\Program Files\WindowsPowerShell\Modules\PowerShellGet\1.0.0.1\PSModule.psm1: line 1725
at , : line 26
fatal: [10.0.0.11]: FAILED! => {
    "changed": true,
    "msg": "Problems adding a prerequisite module PackageManagement or PowerShellGet: Exception calling \"ShouldContinue\" with \"2\" argument(s): \"A command that prompts the user failed because the host program or the command type does not support user interaction. The host was attempting to request confirmation with the following message: PowerShellGet requires NuGet provider version '2.8.5.201' or newer to interact with NuGet-based repositories. The NuGet provider must be available in 'C:\\Program Files\\PackageManagement\\ProviderAssemblies' or 'C:\\Users\\user\\AppData\\Local\\PackageManagement\\ProviderAssemblies'. You can also install the NuGet provider by running 'Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force'. Do you want PowerShellGet to install and import the NuGet provider now?\"",
    "nuget_changed": false,
    "output": "",
    "repository_changed": false
}

After

changed: [10.0.0.11] => {
    "changed": true,
    "nuget_changed": false,
    "output": "Module Microsoft.PowerShell.PSResourceGet installed",
    "repository_changed": false
}
jborean93 commented 2 weeks ago

Thanks for the PR, are you able to create a changelog fragment for this bugfix Thanks for the bugfix, is it possible to add a changelog fragment to document this bugfix https://docs.ansible.com/ansible/latest/community/development_process.html#creating-changelog-fragments.

Just to confirm this fixes a problem when running in check mode and the nuget provider needed to be installed?

mfortin commented 2 weeks ago

@jborean93 I added the fragment.

The problem also occurs when not running in check mode. Install-PrereqModule function checks and installs PackageManagement and PowerShellGet. However, it doesn't check if Nuget package provider is installed prior to this installation. If it isn't installed, there is a confirmation dialog that waits for user input to confirm installation of this other dependency. By running Install-NugetProvider as part of the Install-PrereqModule, it no longer is a dependency of another package and can be automatically accepted and installation of the module succeeds.

jborean93 commented 2 weeks ago

Sorry I misread the change and thought it was just adding -CheckMode. We'll have to move the nuget provider pre-req check and install into the Start-Job below https://github.com/ansible-collections/community.windows/blob/main/plugins/modules/win_psmodule.ps1#L104. Unfortunately we cannot call any PackageManagement or PowerShellGet functions until the actual pre-reqs have been satisfied. If we were to call any PackageManagement function before this we could be inadvertently loading an older version of either package causing problems later on when trying to use the newer one.

Keep in mind the code inside the Start-Job is standalone and won't have access to any of the functions defined in the module.

mfortin commented 2 weeks ago

I tested it and it works out just fine.

Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -WhatIf:$CheckMode | Out-Null Needs to be done before the Start-Job, or at least before Install-Module PackageManagement.

jborean93 commented 2 weeks ago

I tested it and it works out just fine.

The issue that goes into more detail for this is https://github.com/ansible-collections/community.windows/issues/487 but I'll try to explain it again.

It's a complicated scenario that doesn't happen for all scenarios but for some we still need to handle. The problem is using any of the cmdlets from PackageManagement before we've checked to ensure we have our minimum requirement will cause PowerShell to load that older version in the process and we cannot unload or load the newer one. This is ok for some situations but others where Install-Module might pass through extra arguments to PackageManagement during an install and if PackageManagement isn't new enough to understand these parameters it fails. IIRC this was seen with the -AcceptLicense parameter. To compat this we put all the pre-requisite work into a sub-process that we are spawning through Start-Job. Through this we can ensure that PackageManagement and PowerShellGet are installed to the minimums we require when we load it into the main module process.

In the case you are trying to fix, calling Install-PackageProvider before we have ensured PackageManagement is up to date we will load that older version which doesn't support things like -AcceptLicense. We need to do the checks for Install-NugetProvider into the job as well. The simplest solution is to add the following to the top of the job's actions

$nugetProvider = Get-PackageProvider -ListAvailable | Where-Object { ($_.name -eq 'Nuget') -and ($_.version -ge "2.8.5.201") }
if (-not($nugetProvider)) {
    Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force | Out-Null
}

We don't need to worry about check mode here because the job won't be run in check mode.