Deploying PowerShell Scripts to GFI MAX

I’ve been working on some PowerShell scripts that I want to deploy to GFI MAX Remote Management, a monitoring tool I use in the MSP side of the business. I’m still working out the kinks, but here are some tips I’ll want to remember later. Maybe they’ll help you too.

Enable PowerShell Scripts on the Target Computers

Before anything will work, you’ll have to enable PowerShell on the target computers. At least on servers, it’s disabled by default. To enable, start PowerShell on the target machine and run set-executionpolicy. When prompted, tell it to change the policy to remotesigned. Update 3/2/2011:  can also be set with Group Policy (see the bottom of this article; be aware of this possible issue).

GFI MAX scripts 1

Checking Output

The biggest challenge in deploying scripts is figuring out why they fail.

If you execute the script as an Automated Task and it fails, you may see the message “Invalid script arguments” in the GFI MAX dashboard. This forum post explains that the dashboard interprets a return code of 1 as “Invalid script arguments”. It seems that PowerShell returns 1 if the the script fails for any reason, so “Invalid script arguments” may or may not be the issue.

I discovered by trial and error that if I instead deploy the script as a 24×7 check and it fails, the failure message itself is returned in the dashboard and in the alert failure email. If you click on the dashboard message, you get the full message. Much more helpful.

GFI MAX scripts 2

Also, by deploying as a 24×7 check to a server on a 5-minute cycle, you’ll get to see output more frequently; in fact, you can open the Advanced Monitoring Agent on the server and execute (though not edit) the task:

GFI MAX scripts 5

Passing Parameters

Here is what I’ve been able to deduce regarding how MAX handles parameters.

1. If you pass parameters without any quotation marks, the parameters will be passed in to the script as is.

2. If you pass parameters surrounded by double quotation marks (“), they will be temporarily converted to single quotation marks so the script launcher can be called. I found this example of how the task is started by digging through MAX’s debug.log file:

cscript “C:\PROGRA~2\ADVANC~1\task_start.js” 156 “powershell.exe -NoLogo -NoProfile -NonInteractive .\1090.ps1 ‘My param'”

Inside task_start.js, we have this line which converts the single quotation marks back to double before passing them to the script:

// Re-insert the double quote character which cannot be passed through command line
sCommand = sCommand.replace(/\'/g,'\"');

(The /g tells it to do a global replace. See this article.)

3. If you pass parameters surrounded by single quotation marks (‘), the parameter string is passed to task_start.js as is. However, task_start.js will again convert those single quotation marks to double, so they will be double when calling PowerShell.

The PowerShell Problem

The problem is, PowerShell doesn’t like double quotation marks around parameters.

I have a little PowerShell script that echoes back one parameter:

[Parameter(Mandatory = $true,
Position = 0,
ValueFromPipelineByPropertyName = $true)]
Write-Host ('Test script output. TestParam = "' + $TestParam + '"')

If I call it with double quotation marks around the parameter, it thinks it’s two parameters, and the script fails since it only accepts one parameter:

GFI MAX scripts 3

If I call it with single quotation marks around the parameter, the script succeeds:

GFI MAX scripts 4

However, MAX never calls a script with single quotation marks, so PowerShell parameters with quotation marks will never work.

Two Workarounds and a Proposal

The first workaround is to avoid parameters containing spaces; then you can pass parameters without any quotation marks.

The second workaround is to use a special character to represent a single quotation mark, then to modify task_start.js on each target machine to convert that character back to the single quotation mark. As a test, I specified the back-quote (`) around my parameter, then added this line to task_start.js:

sCommand = sCommand.replace(/`/g,"'");

This isn’t very safe, or practical, since GFI can replace the task_start.js script at any time.

The proposal is that the GFI engine pass single quotation marks as special strings, perhaps the XML entity ' (apostrophe), all the way through to the task_start.js script, then convert those back to single quotation marks in the JavaScript. For safety and consistency, the same approach could be followed with double quotation marks ("). That should allow both single and double quotation marks to be preserved in the parameter string. This becomes especially important when you want to pass an array as a parameter to PowerShell, e.g. @(“First element”,”Second element”).

Next Problem

The next problem is, even if we get the parameter quoted correctly, task_start.js is hard coded to append an extra parameter to the command before executing it:

oExec = oShell.Exec( sCommand + " -logfile ..\\task_" + nUID + ".log" );

nUID is an internal task ID used by MAX, so for task 156, it would append " –logfile .\\task_156.log". Apparently it wants to save a log file to the Advanced Monitoring Agent folder (one level up from the Scripts folder). But I haven’t allowed for a –logfile parameter in my PowerShell script, so it ends with this error:  “A parameter cannot be found that matches parameter name ‘logfile’.”

Note that PowerShell doesn’t complain about the extra –logfile parameter if the script does not require parameters. But a flexible script probably will require parameters, so we really need to get MAX working with parameters.

If I modify the task_start.js command to omit the –logfile parameter:

oExec = oShell.Exec( sCommand );

it does in fact complete successfully and comes back to the dashboard with the script output:

GFI MAX scripts 6

But I’m nervous about removing the –logfile parameter from the task_start.js script. It might be there for a reason ;).

Stay Tuned

I’ll see if I can get some clarification on the way GFI MAX handles PowerShell scripts with parameters. Stay tuned for updates.

Update March 2, 2011

GFI Support has provided the following suggestions:

  • To enclose a parameter containing spaces, use three double quotation marks on each side of the parameter, e.g. """my param""". I haven’t been able to find this documented, but PowerShell apparently accepts three double quotation marks as one single quotation mark.
  • Yes, GFI MAX always passes two additional strings to the script, –logfile and the path to a log file. GFI suggests writing scripts to accommodate these strings. PowerShell interprets that as one parameter named “LogFile”. If you actually write to the path specified by -logfile, it should be available in the Advanced Monitoring Agent folder, but according to GFI Support, “We tend to use this file for debugging purposes – you are able to write to this yourself, but we do not recommend that you use this file for long term storage of logs.”

Bonus – Script Output

GFI Support has also confirmed that you can output up to 1 MB of plain text from your script and see that output directly in the dashboard. [Update November 7, 2018:  now limited to 10,000 characters—see comments below.] The first line is displayed in the grid view; if you click on that hyperlink, a More Information dialog opens that shows the rest of the output. That’s very cool, and should eliminate the need to send script results by email in most cases.

In fact, if you return a non-zero (“failure”) exit code and deploy the script as a 24×7 check, it will even send you an email containing all the results. The same should work for a daily DSC check (not tested). It does not currently work for an Automated Task, since MAX only sees exit code 1, which it reports as “Invalid Script Arguments.” (MAX reserves return codes 1000 and below:  see this post.)

Even if you return 1001 from PowerShell, it doesn’t get back to MAX. This has to do with how PowerShell returns exit codes as explained in this article. Updating task_start.js to append “;exit $LASTEXITCODE” to the command line when calling PowerShell should allow MAX to “see” the actual PowerShell return code.

15 thoughts on “Deploying PowerShell Scripts to GFI MAX

  1. Thomas Deal

    FYI, as of agent 8.14 the problems calling PowerShell scripts and getting their return values are fixed! I finally bugged them enough :)

  2. Mark Berry Post author

    Thomas, thanks for the comment. Are you referring to the return values from an Automated Task, which I discuss in the second to the last paragraph above? Are you returning codes like 1, 2, 3, or does MAX still reserve codes 1000 and below?

  3. Ruud


    Any idea on how to get the result from the following powershell script back to gfi max? It’s a powershell scipt that check the staus of a running hyper-v vm. I’m not very into powershell scripting yet, but the server is windows 2008r2 and has the openstack hyper-v commands installed. Powershell has to be started with the -importsystemmodules argument. Else it is not awhere of the hyper-v commands. The Gfi max javascript does not start powershell with this argument. I also posted this in the gfi ideas forum.

    $VM = Get-VMstate -VM eksa-nt8
    while ($VM.EnabledState -ne “Running”)
    $VM = Get-VMstate -VM eksa-nt8
    write-host “The VM is not on”
    sleep 5

    write-host “The VM is on”

  4. Mark Berry Post author

    Looks like you can import system modules using a PowerShell command. Can you just add that as the first command in your PowerShell?

    How to Import system modules automatically?

    Get-Module -ListAvailable | Where-Object {$_.Path -like “$PSHOME*”} | Import-Module

    In fact according to that answer, “The -ImportSystemModules switch has no impact in v3, looks like it is going away.”

  5. Ruud


    Thanks that works. Now this is the script:
    Get-Module -ListAvailable | Where-Object {$_.Path -like “$PSHOME*”} | Import-Module
    $VM = Get-VMstate -VM eksa-nt8
    while ($VM.EnabledState -ne “Running”)
    $VM = Get-VMstate -VM eksa-nt8
    write-host “The VM is not on”

    write-host “The VM is on”

    Now the following strugle. If the vm is on or off, GFI runs the script succesfully in both cases. How do I make GFI send an email when the vm is not on?

  6. Ruud

    BTW, with the openstack hyper-v commands installed I meant the CODEPLEX hyper-v cmdlets. Openstack is something entirely different.

  7. Jay

    Hi, found your post while searching for getting a Powershell to report all VMs status on a Hyper-V Host. Tried the following, however getting a blank display on the GFI RM portal:

    Get-Module -ListAvailable | Where-Object {$_.Path -like “$PSHOME*”} | Import-Module

    Although this executes on the server with the powershell execution set to remotesigned I can’t get the results to port over to the GFI RM portal. I’m sure its missing a ‘Write-Host’ variable, as mentioned on

    Your assistance would be greatly appreciated.

  8. Mark Berry Post author

    Sorry Jay, I haven’t kept up with this. The dashboard back end has probably changed since I wrote this a couple years ago. Maybe one of the other commenters will have a suggestion, or you could try GFI’s User Scripting forum:

  9. Pingback: Run Downloaded PowerShell Scripts | MCB Systems

  10. Rob Eberhardt

    Belated thanks for a great write-up, Mark. Wish I’d seen it years ago!

    You just helped me solve a bizarre bug in a new task script I just wrote. I wish GFI/LN/SW would publish these kinds of nuts & bolts (or shield their devs from us less).

    Looks like you’re also an IT+dev who does a lot of the same kind of work as my company. Blog followed!

  11. Mark Berry Post author

    Rob, thanks for documenting that. I saw the new limit go by in a Solarwinds update but did not remember that I’d blogged about the size.

Leave a Reply

Your email address will not be published. Required fields are marked *

Notify me of followup comments via e-mail. You can also subscribe without commenting.

This site uses Akismet to reduce spam. Learn how your comment data is processed.