0

I want to output some information about devices to .txt file but something went wrong. Could someone tell me what the problem is?

   $devices=("wsu1","wsu2")
$file="C:\reports\file.txt"
 Invoke-Command -ComputerName $devices -ScriptBlock {
Get-WmiObject Win32_OperatingSystem |
Select PSComputerName, Caption, OSArchitecture, Version, BuildNumber | Out-File -FilePath $file
}
 & explorer.exe "C:\reports\"

I retrieve this message from the console:

Cannot bind argument to parameter 'FilePath' because it is null.
    + CategoryInfo          : InvalidData: (:) [Out-File], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.OutFileCommand
Nick S
  • 15
  • 4
  • 5
    `$file` is not defined in the remote scope, it's defined in your local scope. If you want `Invoke-Command` to be able to see that variable defined locally you can use the `$using:` scope modifier: `-FilePath $using:file` – Santiago Squarzon Aug 26 '22 at 16:38
  • Agreed^, or just place your `Out-File` outside of your `Invoke-Command`. – Abraham Zinala Aug 26 '22 at 16:44
  • @SantiagoSquarzon I tried but I did something wrong could you please edit the code and show how it should be :) – Nick S Aug 26 '22 at 17:38
  • @AbrahamZinala I tried that but didn't work – Nick S Aug 26 '22 at 17:39
  • What didn't "work"? Did you get an error? Did it just not output information? `Invoke-Command -ComputerName $devices -ScriptBlock { ... } | Out-File -FilePath $file`, what error did you receive when running this? – Abraham Zinala Aug 26 '22 at 17:47
  • @AbrahamZinala yes not output information inside the file – Nick S Aug 26 '22 at 17:55
  • 1
    hmmmm, weird situation then. Santiagos recommedation is: `| Out-File -FilePath $using:file` If the intention is to place it on the remote computer, otherwise it would have to be outside the loop or referencing your system. – Abraham Zinala Aug 26 '22 at 17:58
  • `$devices=("wsu1","wsu2") $file="C:\reports\file.txt" Invoke-Command -ComputerName $devices -ScriptBlock { Get-WmiObject Win32_OperatingSystem |
    Select PSComputerName, Caption, OSArchitecture, Version, BuildNumber | Out-File -FilePath $using:file } & explorer.exe "C:\reports\" `Did I do that correctly?
    – Nick S Aug 26 '22 at 18:09
  • @NickS Please [edit] your question to elaborate your [mcve] instead posting any code snippet in a comment. – JosefZ Aug 26 '22 at 18:12
  • `Get-ADComputer -filter * -SearchBase "OU=HR,DC=SHELLPRO,DC=LOCAL" -Properties OperatingSystem | Sort Name | Export-Csv "c:\temp\version.csv ` I found another way to find what I need thank you for supporting ;) – Nick S Aug 26 '22 at 18:59

2 Answers2

1
  • Your immediate problem is that you tried to access a local variable, $file in a remotely executing script block, which cannot work, and defaults to $null, causing Out-File to report an error due to lack of an output file name.

    • In any script block that runs in a different runspace (notably remotely, on other machines), you need to the $using: scope in order to use local variable values, such as $using:file in this case - see this answer for details.
  • However, the fact that you're executing & explorer.exe "C:\reports\" in an attempt to show the output file implies that you meant to write to a local file, which necessitates calling Out-File locally, outside the remote script block ({ ... }), in a separate pipeline segment.

    • Note that this approach - piping to a single Out-File call - ensures that all output from the Invoke-Command call, across all computers targeted, is saved to the output file; see this answer for more information.
devices = "wsu1", "wsu2"
$file = "C:\reports\file.txt"

# Note the placement of the Out-File call.
Invoke-Command -ComputerName $devices -ScriptBlock {
    Get-WmiObject Win32_OperatingSystem |
      Select PSComputerName, Caption, OSArchitecture, Version, BuildNumber
  } |
  Out-File -FilePath $file

& explorer.exe "C:\reports\"
mklement0
  • 382,024
  • 64
  • 607
  • 775
-1

This might help... maybe... :)

# Prep some stuff
$Devices = @("wsu1","wsu2")
$File = "C:\reports\file.txt"
$Results = @()

# Work for each device
foreach ($Device in $Devices) {
    $Results += Invoke-Command -ComputerName $Device -ScriptBlock {
        Get-WmiObject Win32_OperatingSystem | Select-Object PSComputerName, Caption, OSArchitecture, Version, BuildNumber
    }
}

# Chuck it in a file
Set-Content -Value $Results -Path $File
Neil
  • 41
  • 4
  • By switching to processing the devices (computers) _one by one, sequentially_, you're forgoing the benefits of calling _in parallel_. – mklement0 Aug 26 '22 at 20:04
  • Note that extending arrays in a loop with `+=` is inefficient, because a _new_ array must be created behind the scenes _in every iteration_, given that arrays are of fixed size; a much more efficient approach is to use a `foreach` loop as an _expression_ and let PowerShell itself collect the outputs in an array: `[array] $outputs = foreach (...) { ... }` - see [this answer](https://stackoverflow.com/a/60708579/45375). In case you need to create arrays manually, e.g. to create _multiple_ ones, use an efficiently extensible list type - see [here](https://stackoverflow.com/a/60029146/45375). – mklement0 Aug 26 '22 at 20:04