Invoke-Command: Object reference not set to an instance of an object

Can anyone explain why I’m getting ‘Object reference not set to an instance of an object’? The script is supposed to execute a batch file on a number of remote machines but I get the aforementioned error. I’ve verified the file gets there and arguments are passing through.

Add-Type -AssemblyName System.Windows.Forms
$obj = New-Object System.Windows.Forms.OpenFileDialog
$wsh = New-Object -ComObject 'wscript.shell'

$wsh.Popup("Opening file dialog, select file containing target computers.", 0, $null, 64) > $null

$obj.Title = 'Select file containing list of computers'
$obj.Filter = 'Text Files (*.txt)|*.txt|All Files (*.*)|*.*'
$result = $obj.ShowDialog()

If ($result -eq 'OK')
{
    $computers = Get-Content $obj.FileName

    $wsh.Popup("Opening file dialog, select file to be executed on target computers ", 0, $null, 64) > $null

    $obj.Title = 'Select file'
    $obj.Filter = 'Batch Script (*.bat)|*.bat|All Files (*.*)|*.*'
    $result2 = $obj.ShowDialog()

    $filepath = $obj.FileName
    $filename = Split-Path $obj.FileName -Leaf
    
    If ($result2 -eq 'OK')
    {
        ForEach ($computer in $computers)
        {
            $online = Test-Connection -Count 2 -ComputerName $computer -Quiet
    
            If ($online)
            {
                Write-Host "'$computer' online" -BackgroundColor Black -ForegroundColor Green

                Write-Host 'Copying file...'
		Copy-Item -Path $filepath -Destination "\\$computer\c$\Temp"
                
                Write-Host 'Executing file...'
                Invoke-Command -ComputerName $computer -ArgumentList $filename -ScriptBlock {&"C:\Temp\$($args[0])"}

                Write-Host 'Removing file...'
                Remove-Item -Path "\\$computer\c$\Temp\$filename"
                
                Write-Host
                Write-Host
            }
            Else
            {
                Write-Host "'$computer' offline" -BackgroundColor Black -ForegroundColor Red
            }
        }
    }
}

Thanks in advance!

I have a couple of thoughts here. First, do you need to authenticate at any point while your script is running? (It may be in there, but I’m not seeing it).

Second - do you have any windows event logs for the remote machine? If everything is passing through then it may be failing to run the file for some reason. You can also add lines to your batch files to write to the event log as well, if you’re not seeing anything.

Let me know if this points you in the right direction.

Currently, there is no authentication needed within my batch file. I can’t get it to work at all so I’ve re-wrote my batch file to do this

 echo Hello my name is ‘%computername%’ 

What I would also do is to check and see if the files I try to copy exist, and if so then try and execute them. Lastly, if it’s still not working I’d add in the additional logging to try and find out what exactly is going on.

Just for fun - have you tried running this invoke-command as another user? That may solve the problem outright. Adding a -credential (get-credential) on your invoke-command could potentially do the trick.

I’ve just re-wrote script to accept .ps1 rather than .bat; I can get a .ps1 to do most of the same things as .bat (and most times more :wink: ). I just wanted to put it out there to make sure it wasn’t something obvious I was missing…

Add-Type -AssemblyName System.Windows.Forms
$obj = New-Object System.Windows.Forms.OpenFileDialog
$wsh = New-Object -ComObject 'wscript.shell'

$wsh.Popup("Opening file dialog, select file containing target computers.", 0, $null, 64) > $null

$obj.Title = 'Select file containing list of computers'
$obj.Filter = 'Text Files (*.txt)|*.txt|All Files (*.*)|*.*'
$result = $obj.ShowDialog()

If ($result -eq 'OK')
{
    $computers = Get-Content $obj.FileName

    $wsh.Popup("Opening file dialog, select file to be executed on target computers ", 0, $null, 64) > $null

    $obj.Title = 'Select file'
    $obj.Filter = 'PowerShell Script (*.ps1)|*.ps1'
    $result2 = $obj.ShowDialog()

    $filepath = $obj.FileName
    $filename = Split-Path $obj.FileName -Leaf
    
    If ($result2 -eq 'OK')
    {
        ForEach ($computer in $computers)
        {
            $online = Test-Connection -Count 2 -ComputerName $computer -Quiet
    
            If ($online)
            {
                Write-Host "'$computer' online" -BackgroundColor Black -ForegroundColor Green

                Write-Host 'Copying file...'
				Copy-Item -Path $filepath -Destination "\\$computer\c$\Temp"
                
                Write-Host 'Executing file...'
                Invoke-Command -ComputerName $computer -ArgumentList $filename -ScriptBlock {Set-ExecutionPolicy -ExecutionPolicy Bypass;&"C:\Temp\$($args[0])"}

                Write-Host 'Removing file...'
                Remove-Item -Path "\\$computer\c$\Temp\$filename"
                
                Write-Host
                Write-Host
            }
            Else
            {
                Write-Host "'$computer' offline" -BackgroundColor Black -ForegroundColor Red
            }
        }
    }
}