Pipeline Errors Install-Module

My task (or I should say Playing with Powershell Objects) is to create a custom object with the list of installed modules and update them. My oneliner creates the object and pipe it to Install-Module cmdlet, since it has -InputObject parameter which accepts pipeline ByValue,ByPropertyName I’m expecting below command should work - As per my understanding…

@('SqlServer','ImportExcel') | ForEach-Object {[pscustomobject]@{Name = "$_"}} | Install-Module -Force}

This is erroring out saying Invalid Argument …

After doing some reading, I tried below and it worked.

@('SqlServer','ImportExcel') | Foreach {Install-Module -Name $_ -Force}

Why isn’t #1 statement working as intended? Can someone help me understand?

If you look further into the help:

[-FullyQualifiedName] is looking for ModuleSpecification

It’s most likely that piping to Import-Module requires the FullyQualifiedName, not a short Name. You also notice that it want a ModuleSpecification. The preferred way would probably be to use Get-Module -ListAvailable to see if the module is available before piping to Import-Module:

PS C:\WINDOWS\system32> Get-Module -Name TLS, PKI -ListAvailable

    Directory: C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules

ModuleType Version    Name                                ExportedCommands                                                                                                                        
---------- -------    ----                                ----------------                                                                                                                        
Manifest    PKI                                 {Add-CertificateEnrollmentPolicyServer, Export-Certificate, Export-PfxCertificate, Get-CertificateAutoEnrollmentPolicy...}              
Manifest    TLS                                 {New-TlsSessionTicketKey, Enable-TlsSessionTicketKey, Disable-TlsSessionTicketKey, Export-TlsSessionTicketKey...}                       

PS C:\WINDOWS\system32> Get-Module -Name TLS, DoesNotExist -ListAvailable

    Directory: C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules

ModuleType Version    Name                                ExportedCommands                                                                                                                        
---------- -------    ----                                ----------------                                                                                                                        
Manifest    TLS                                 {New-TlsSessionTicketKey, Enable-TlsSessionTicketKey, Disable-TlsSessionTicketKey, Export-TlsSessionTicketKey...}                       

You can then pipe the found modules to Import-Module:

Get-Module -Name TLS, PKI -ListAvailable | Import-Module -Verbose -Force

Hello Rob,

The cmdlet im piping into is Install-Module. And I don’t see required parameters other than Name or Inputobject in the help.

Unless the parameter you’re passing to has been explicitly defined as [ValueFromPipelineByPropertyName()] in its code, it will not work like this. Also, if any previous parameter has a parameter that is [ValueFromPipeline()] it will attempt to put the entire object into that as well as doing what you’re expecting. This frequently breaks parameter sets and causes errors.

Instead, you can try something like this:

@('SqlServer','ImportExcel') | ForEach-Object {
        Name = $_
} | Install-Module -Name {$_.Name} -Force

If you check Get-Help Install-Module -Full you’ll see that the first positional parameter is:


        Required?                    true
        Position?                    0
        Accept pipeline input?       true (ByValue)
        Parameter set name           Assembly
        Aliases                      None
        Dynamic?                     false

Note - position 0
Note - pipeline input by value

It’s expecting you to provide an object that is some kind of Assembly, so it’s likely expecting direct input from an imported DLL or some such. I can’t say I’ve ever see the -Assembly parameter used.

Funky thing here is that -Name is ALSO position 0 – but there doesn’t appear to be a clear way for the PowerShell parser to determine which parameter set you want, so I’d conjecture it probably defaults to -Assembly.

Just throwing this out there… `install-module -Name ’ so `install-module -Name ‘SqlServer’,‘ImportExcel’ -Confirm:$false -Force` should work. Taking your array and passing could be done, don’t have time to write that up. I would recommend adding the -Confirm:$false -Force so you do not have to confirm all the modules to be install.

So, the docs (Install-Module (PowerShellGet) - PowerShell | Microsoft Docs) indicate that the -Name parameter is indeed supposed to accept module names, in a property named Name, from the pipeline. I would expect this to work:

@('SqlServer','ImportExcel') | ForEach-Object {[pscustomobject]@{Name = "$_"}} | Install-Module -Force}

But some commands can be a little dinky about custom objects. I’d tend to use Trace-Command and get it to show me what it was doing for parameter binding, to figure out what was happening.

If a parameter that can be passed in the pipe byvalue isn’t specified (-InputObject in this case), powershell gets “greedy” and tries to turn other properties in the pipe like Name into what it’s looking for (a [PSObject]InputObject). Perhaps a bug (basic problem mixing byvalue and bypropertyname parameters in pipes · Issue #7159 · PowerShell/PowerShell · GitHub)? Usually the byvalue parameter is a string. Learning is hard (Bruce Payette).

PS C:\Users\js> [pscustomobject]@{Name = 'SqlServer'} | Install-Module -whatif

Install-Module : Invalid value is specified for InputObject parameter.
At line:1 char:41
+ [pscustomobject]@{Name = 'SqlServer'} | Install-Module -whatif
+                                         ~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (@{Name=SqlServer}:PSObject) [Install-Module], ArgumentException
    + FullyQualifiedErrorId : InvalidInputObjectValue,Install-Module



PS C:\Users\js> install-module importexcel,sqlserver

PS C:\Users\js> find-module importexcel,sqlserver | install-module

Maybe you can try to mimic what find-module returns and make your own object. But it’s probably not worth it.

Using “set-psdebug -trace 1”, this looks like the reason install-module doesn’t like your object:

   if ( >>>> ($inputValue.PSTypeNames -notcontains "Microsoft.PowerShell.Commands.PSRepositoryItemInfo") -and
DEBUG: 1826+                      >>>> ThrowError -ExceptionName "System.ArgumentException" `