Search code examples
functionpowershellvariablesinvoke-command

How to pass variable value to Invoke-Command


I am trying to get the list of machines which are in particular state (Saved, Running, Stopped). I am passing the state of the machine as an argument in a function.

Function global:Resource-Summary
{
    Param(
    [parameter(mandatory=$true)] $ProgramName,
    [parameter(mandatory=$true)] $ServerName
    )

    PROCESS
    {

    Foreach ($Server in $ServerName)
    {

      Invoke-Command -ComputerName $Server -ScriptBlock {
      $VMs = Get-VM

      $colVMs = @()

      foreach ($VM in $VMs)
      {

      $objVM = New-Object System.Object
      $objVM | Add-Member -MemberType NoteProperty -Name VMName -Value $VM.VMName 
      $objVM | Add-Member -MemberType NoteProperty -Name VMNotes -Value $VM.Notes
      $objVM | Add-Member -MemberType NoteProperty -Name VMState -Value $VM.State

    $colVMs += $objVM
      }

     $a = @{Expression={$_.VMName};Label='VM Name'}, `
          @{Expression={$_.VMNotes};Label='VM Description'}, `
          @{Expression={$_.VMState};Label='State'}

     "Program Name : $ProgramName"
     $colVMs |Where-Object {($_.VMState -eq '$ProgramName')} | Format-Table $a -AutoSize 

       } -ArgumentList $ProgramName
     }
  }
}

When I run Resource-Summary -ProgramName Running -ServerName Demo

I do not get any value.

When I replace $ProgramName with RUNNING I get the expected output.


Solution

  • For reference, see e.g. this post on how Pass arguments to a scriptblock in powershell .

    The problem in your script is how you call the script block, this is explained in more detail in the link above, but you need to pass any "external" input to it the same way as if you'd call it like a function.

    You are doing this partially correctly, you are using the -ArgumentList parameter to send $ProgramName to the scriptslock but you haven't specified in the scriptblock how to access it.

    For example, check out

    Invoke-Command -ArgumentList "Application" -ScriptBlock { 
        param($log)
        Get-EventLog $log
    }
    

    Here -ArgumentList contains the input, and inside the scriptblock, $log is assigned its value.

    Updating your script to take that into account:

    Function global:Resource-Summary
    {
        Param(
        [parameter(mandatory=$true)] $ProgramName,
        [parameter(mandatory=$true)] $ServerName
        )
    
        PROCESS
        {
            Foreach ($Server in $ServerName)
            {
    
                Invoke-Command -ComputerName $Server -ScriptBlock {
                    param($name)
                    $VMs = Get-VM
    
                    $colVMs = @()
    
                    foreach ($VM in $VMs)
                    {
    
                    $objVM = New-Object System.Object
                    $objVM | Add-Member -MemberType NoteProperty -Name VMName -Value $VM.VMName 
                    $objVM | Add-Member -MemberType NoteProperty -Name VMNotes -Value $VM.Notes
                    $objVM | Add-Member -MemberType NoteProperty -Name VMState -Value $VM.State
    
                    $colVMs += $objVM
                    }
    
                    $a = @{Expression={$_.VMName};Label='VM Name'}, `
                      @{Expression={$_.VMNotes};Label='VM Description'}, `
                      @{Expression={$_.VMState};Label='State'}
    
                    "Program Name : $ProgramName"
                    $colVMs |Where-Object {($_.VMState -eq "$name") | Format-Table $a -AutoSize 
    
                    } 
                } -ArgumentList $ProgramName
            }
        }
    }
    

    Also, in my opinion, the -ArgumentList is on the wrong line, it needs to be after the following closing bracket, one line done. This way it's on the same cmdlet as Invoke-Command and the scriptblock.