Run Ninite Pro with Nice Logs from MAXfocus

I’ve deployed Ninite Pro to update non-Windows applications on computers that I manage. I’m running it in silent mode but I wanted some decent logging so I can review what it did. This is especially useful when run from the SolarWindows RMM dashboard as a Scheduled Task:  the output is visible from the dashboard.

The script takes two parameters:

Param 1 Path to local or network copy of NiniteOne.exe.

Param 2 Optional path to a NiniteDownloads cache.  This can be on a network share, in which case the MAXfocus agent must run as a user with network access (not as the default SYSTEM account).  Defaults to %temp%\NiniteDownloads if not specified.  Fails if not accessible.

Example (type on one line):

NiniteUpdate.cmd "%SystemDrive%\Scripts\Helpers" "\\SRV01\NiniteDownloads"

Behavior

Ninite is run three times:  first in /audit mode, then in /updateonly mode, then again in /audit mode. Output is written to the NiniteUpdateSummary.log file in the same folder as the script. “Not installed” messages are omitted, so you get a nice summary of what is installed. When the script completes, the NiniteUpdateSummary.log file is written to stdout (which is what is displayed in the MAXfocus dashboard). Additionally, the full result of the final audit (including the “Not installed” messages) is saved as NiniteAuditAfterUpdate.log.

There is a hard-coded variable in the script called CommonParams that excludes Microsoft programs (I use Microsoft Update for those), some other programs that were not updating, and TeamViewer (since that update can clobber the RMM Take Control function). I also set it to /disableshortcuts. You may want to adjust these parameters and exclusions; see the Ninite command-line switch reference and their list of apps.

Sample Output

If you run this script from MAXfocus, you’ll want to run it as an automated task—it’s too long to run as a DSC check. I let it run for an hour. After it completes, the Automated Task window will look like this:

NinitePro Automated Task Output

If you scroll in the Details area, you’ll see the full output:

--------------------------------------------------------------
Ninite - Audit before updating - Tue 12/16/2014 - 4:00:51.31
Partial
Air : OK - 15.0.0.356
Firefox : OK - 34.0.5
Flash : Update - 15.0.0.246 -> 16.0.0.235
Reader : Update - 10.1.7 -> 11.0.10
Skype : Update - 6.22.0.107 -> 7.0.0.102
-------------------------------------------------------------- 
Ninite - Update Only - Tue 12/16/2014 - 4:00:59.83
OK
Flash : OK
Skype : OK
Reader : OK
--------------------------------------------------------------
Ninite - Audit after updating - Tue 12/16/2014 - 4:07:55.61
OK
Air : OK - 15.0.0.356
Firefox : OK - 34.0.5
Flash : OK - 16.0.0.235
Reader : OK - 11.0.10
Skype : OK - 7.0.0.102
-------------------------------------------------------------- 
Exiting with ExitCode 0

Update May 30, 2019 and July 29, 2019

Important You must get the Ninite executable onto each machine where you want to run this script. Solarwinds RMM does not offer the option of hosting binary files, so you’ll have to have your own static web host (a standard web site, web-enabled Azure storage account, etc.), then you’ll need to use something like the SetUpScripts.ps1 script from this post to download the script to the computer. Keep in mind that scripts in RMM run with highest (SYSTEM account) privileges, so you want your script host to be absolutely secure and unhackable.

As an alternative to automated download, you can just manually copy the executable to a specific folder, e.g. using Take Control File Transfer.

There are several enhancements since the last release in 2015.

Logging

If you’re using the SetUpScripts.ps1 script from this post, you’ll have that environment variable and path. If the path is not there, this script simply writes the output to the same folder where NinitePro.exe is located.

Executable Name

The name of the Ninite executable has been changed from NiniteOne.exe to NinitePro.exe. If you need a different name, you can modify the line of the script that begins with set ProgramExe=.

Event Logging

By default, the script now writes to the path specified by %IT_Scripts%LogFiles if that path exists.

The script now writes to the Application event log. One information, warning, or error event is written each time the script is run. Deploy the script as a daily automated task, then create two daily (DSC) event log checks in the RMM dashboard:

  • Alert if Application log does not contain any event with source “Ninite Script”. This will tell you if the script failed to run that day.
  • Alert if Application log contains any Warning or Error event with the source “Ninite Script”. This tells you if a program failed to update, e.g. because it was locked. Check the output in the Tasks tab for more details.

These are the Application events that may be created, all with Source = “Ninite Script”:

Information 101: “Ninite: All programs are up to date. See log at %s.”
Information 102: “Ninite: %i program(s) skipped (possibly unsupported). See log at %s.”
Warning 151: “Ninite: %i program(s) skipped (running/locked). See log at %s.”
Warning 152: “Ninite: %i program(s) skipped but update needed. See log at %s.”
Error 191: “Ninite: Script failed with exit code %i. See log at %s.”

The Script

Save this as NiniteUpdate.cmd. For easier reading and editing, open it in Notepad++. See comments in the script re. various updates since its initial release. As always, test and use at your own risk!

@echo off
REM Run Ninite /updateonly on the local computer, optionally caching to local or network path.
REM
REM Copyright 2019 by Mark Berry, MCB Systems, www.mcbsys.com.  
REM Free for personal or commercial use.  May not be sold.
REM No warranties.  Use at your own risk.
REM
REM Param 1:  Path to local or network copy of NiniteOne.exe.  
REM           Path only; assumes "NiniteOne.exe" for program name.
REM
REM Param 2:  Path to NiniteDownloads cache.  Can be on a network share.  Defaults 
REM           to %temp%\NiniteDownloads if not specified.  Fails if not accessible.
REM
REM Assumes you have administrative permissions on the local PC.
REM
REM Creates/replaces NiniteUpdateSummary.log in %IT_Scripts%\LogFiles or, if that
REM doesn't exist, in %ProgramPath%.
REM
REM 12/11/2014 Initial script.
REM
REM 12/13/2014 Modify to create and save NiniteUpdateSummary.log as combined log file.
REM            Individual Ninite runs write to NiniteTemp.log, deleted after use.
REM            Final audit saved to NiniteAuditAfterUpdate.log.
REM
REM 12/16/2014 Add exclusions for Avast and AVG (can't update security software), CCleaner 
REM            (vendor doesn't allow update) and Messenger (discontinued).
REM
REM 01/31/2015 Exclude ".NET 3.5.2", a new Microsoft program.
REM
REM 03/16/2015 Print program and cache path at beginning of NiniteUpdateSummary.log.
REM
REM 04/02/2015 Show parameters in log.  
REM            Don't try to write to log if log path (%ProgramPath%) not found.
REM
REM 04/27/2015 - Fix bug that was putting cache in C:\Program Files (x86)\Advanced Monitoring Agent\scripts\-logfile
REM              folder when no cache location was specified and script was run from Max RM.
REM            - Set default cache location to NiniteOne.exe folder (param 1) + \NiniteDownloads.
REM
REM 11/04/2015 - Max RM agent 9.12.3 introduced a bug that encloses parameters in single quotation marks if they
REM              were passed in double quotation marks.  Strip out single quotation marks around parameters.
REM
REM 02/24/2017 - Exclude .NET 4.6, 4.6.1, and 4.6.2.
REM
REM 02/02/2019 - Exclude .NET 4.7, 4.7.1, and 4.7.2.
REM            - Write log to %IT_Scripts%LogFiles if it exists.
REM
REM 05/30/2019 - Write events to Windows Application event log indicating success, warning, or failure.
REM            - Remove lines that deleted logs in old path.
REM            - Changed default name of executable from NiniteOne.exe to NinitePro.exe.
REM
REM 07/29/2019 - Detect "Update" still pending in audit after update.  That means Ninite is skipping some
REM              updates, e.g. for Skype Classic or Oracle Java.  Report this as Warning event 152.

REM ===========================================================================================
REM Set up variables
REM ===========================================================================================
REM This script can be adapted to run another program by modifying the next two lines
set ProgramExe=NinitePro.exe
REM Exit with 0; exceptions below
set /A exitcode=0

REM ===========================================================================================
REM Check for parameters
REM ===========================================================================================
if ###%1###==###### goto NoParam1
goto Param1Found

:NoParam1
echo.
echo Missing parameter(s)
echo.
echo Usage:  NiniteUpdate.cmd PathToNinite [NiniteDownloadsCache]
echo.
echo         Example:
echo.
echo         NiniteUpdate.cmd "C:\Scripts\Helpers" "\\SRV01\NiniteDownloads"
echo.
set /A exitcode=1001
goto End

:Param1Found
REM Strip double quotation marks, if any, from parameter
set ProgramPath=%~1
REM Now strip single quotation marks, if any, added by RMM caller bug. 
REM See http://ss64.com/nt/syntax-dequote.html.
set ProgramPath=###%ProgramPath%###
set ProgramPath=%ProgramPath:'###=%
set ProgramPath=%ProgramPath:###'=%
set ProgramPath=%ProgramPath:###=%

if ###%2###==###### goto NoParam2
REM RMM adds a -logfile parameter as the last parameter.  Ignore that.
if ###%2###==###-logfile### goto NoParam2
goto Param2Found

:NoParam2
set CachePath=%ProgramPath%\NiniteDownloads
goto EchoParams

:Param2Found
REM Strip double quotation marks, if any, from parameter
set CachePath=%~2
REM Now strip single quotation marks, if any, added by RMM caller bug. 
REM See http://ss64.com/nt/syntax-dequote.html.
set CachePath=###%CachePath%###
set CachePath=%CachePath:'###=%
set CachePath=%CachePath:###'=%
set CachePath=%CachePath:###=%

:EchoParams
REM ===========================================================================================
REM Echo parsed parameters
REM ===========================================================================================
REM echo   ProgramPath: %ProgramPath%
REM echo    ProgramExe: %ProgramExe%
REM echo     CachePath: %CachePath%
REM echo.

REM ===========================================================================================
REM Check for program path
REM ===========================================================================================
if exist "%ProgramPath%" goto ProgramPathExists
echo Program path "%ProgramPath%" does not exist. Exiting.
set /A exitcode=1002
goto End

:ProgramPathExists
REM ===========================================================================================
REM Check for program executable
REM ===========================================================================================
if exist "%ProgramPath%\%ProgramExe%" goto ProgramExists
echo Program executable "%ProgramPath%\%ProgramExe%" does not exist. Exiting.
set /A exitcode=1003
goto End

:ProgramExists
REM ===========================================================================================
REM Check for cache path
REM ===========================================================================================
REM First try to create it if it doesn't exist
if not exist "%CachePath%" md "%CachePath%"
REM If it still doesn't exist, e.g. if it's on an inaccessible network share, fail
if exist "%CachePath%" goto CachePathExists
echo Downloads cache path "%CachePath%" does not exist and could not create it. Exiting.
set /A exitcode=1004
goto End

:CachePathExists
REM ===========================================================================================
REM Set log path
REM ===========================================================================================

REM Set new log location, giving priority to %IT_Scripts%LogFiles.
set LogPath=%IT_Scripts%LogFiles
if not exist "%LogPath%" set LogPath=%ProgramPath%

REM Send all output to NiniteUpdateSummary.log file.  First redirect overwrites previous log; the rest append.
set LogFullPath=%LogPath%\NiniteUpdateSummary.log

REM ===========================================================================================
REM Run Ninite three times with various parameters
REM ===========================================================================================
set CommonParams=/silent "%LogPath%\NiniteTemp.log" /disableshortcuts /exclude ".NET" ".NET 3.5" ".NET 4" ".NET 4.5" ".NET 4.5.1" ".NET 4.5.2" ".NET 4.6" ".NET 4.6.1" ".NET 4.6.2" ".NET 4.7" ".NET 4.7.1" ".NET 4.7.2" Avast AVG CCleaner Essentials Messenger Office OneDrive OpenOffice Silverlight SkyDrive TeamViewer
set CacheParam=/cachepath "%CachePath%"

echo NiniteUpdate.cmd > "%LogFullPath%"
echo. >> "%LogFullPath%"
echo       Program: %ProgramPath%\%ProgramExe% >> "%LogFullPath%"
echo   Cache param: %CacheParam% >> "%LogFullPath%"
echo Common params: %CommonParams% >> "%LogFullPath%"
echo      Log file: %LogFullPath% >> "%LogFullPath%"

REM ===========================================================================================
REM Audit before update
REM ===========================================================================================
echo. >> "%LogFullPath%"
echo -------------------------------------------------------------- >> "%LogFullPath%"
echo Ninite - /audit before updating - %date% - %time% >> "%LogFullPath%"
echo. >> "%LogFullPath%"
"%ProgramPath%\%ProgramExe%" /audit %CommonParams% %CacheParam%
set /a errorcode=%errorlevel%
if %errorcode% EQU 0 goto LogAuditBeforeUpdate
echo Running first %ProgramExe% /audit failed with ErrorLevel %errorcode%.  Exiting.
set /A exitcode=1100+%errorcode%
goto WriteToEventLogAndEnd

:LogAuditBeforeUpdate
REM Print lines that do NOT contain the string ": Not installed"
type "%LogPath%\NiniteTemp.log" | find /V ": Not installed" >> "%LogFullPath%"
del "%LogPath%\NiniteTemp.log"

REM ===========================================================================================
REM Update
REM ===========================================================================================
echo. >> "%LogFullPath%"
echo -------------------------------------------------------------- >> "%LogFullPath%"
echo Ninite - /updateonly - %date% - %time% >> "%LogFullPath%"
echo. >> "%LogFullPath%"
"%ProgramPath%\%ProgramExe%" /updateonly %CommonParams% %CacheParam%
set /a errorcode=%errorlevel%
if %errorcode% EQU 0 goto LogUpdate
echo Running %ProgramExe% /updateonly failed with ErrorLevel %errorcode%.  Exiting.
set /A exitcode=1100+%errorcode%
goto WriteToEventLogAndEnd

:LogUpdate
REM Count occurrences of "Skipped (program running/locked)" and save in variable
REM "Find" emits a line with three space-separated "tokens."  Save the count appears from the third token.
for /f "tokens=3" %%f in ('find /c ": Skipped (program running/locked)" "%LogPath%\NiniteTemp.log"') do set /a SkippedLocked=%%f 

type "%LogPath%\NiniteTemp.log" >> "%LogFullPath%"
del "%LogPath%\NiniteTemp.log"

REM ===========================================================================================
REM Audit after update
REM ===========================================================================================
echo. >> "%LogFullPath%"
echo -------------------------------------------------------------- >> "%LogFullPath%"
echo Ninite - /audit after updating - %date% - %time% >> "%LogFullPath%"
echo. >> "%LogFullPath%"
"%ProgramPath%\%ProgramExe%" /audit %CommonParams% %CacheParam%
set /a errorcode=%errorlevel%
if %errorcode% EQU 0 goto LogAuditAfterUpdate
echo Running second %ProgramExe% /audit failed with ErrorLevel %errorcode%.  Exiting.
set /A exitcode=1100+%errorcode%
goto WriteToEventLogAndEnd

:LogAuditAfterUpdate
REM In an audit run after doing updates, "Update" means Ninite could/would not update (e.g. Skype Classic, Oracle Java)
REM Count occurrences of "Update" and save in variable.  Will report as Warning event 152.
for /f "tokens=3" %%f in ('find /c ": Update" "%LogPath%\NiniteTemp.log"') do set /a SkippedUpdate=%%f 

REM In an audit run, "Skipped" means Ninite is ignoring it, maybe because it's no longer supported
REM The programs that could not be updated contain the string "->" but we already counted SkippedLocked above
REM Count occurrences of "Skipped" and save in variable.  Will report as Information event 102.
for /f "tokens=3" %%f in ('find /c ": Skipped" "%LogPath%\NiniteTemp.log"') do set /a SkippedOther=%%f 

type "%LogPath%\NiniteTemp.log" | find /V ": Not installed" >> "%LogFullPath%"
REM Save final full log as NiniteAuditAfterUpdate.log (delete old audit first)
del "%LogPath%\NiniteAuditAfterUpdate.log"
ren "%LogPath%\NiniteTemp.log" "NiniteAuditAfterUpdate.log"

:WriteToEventLogAndEnd
REM echo exitcode = %exitcode%
REM echo SkippedLocked = %SkippedLocked%
REM echo SkippedOther = %SkippedUpdate%
REM echo SkippedOther = %SkippedOther%

REM %message% was not expanding in "eventcreate".  Using delayed variable expansion and !message! fixes that.
setlocal EnableDelayedExpansion

REM Write only one event to event log. First check for Error condition, then Warning, then Information.
REM Redirect output of "eventcreate" to nul so it doesn't write to stdout/stderr.
if %exitcode% GTR 0 (
set message=Script failed with exit code %ExitCode%.
eventcreate /l Application /t Error /so "Ninite Script" /id 191 /d "Ninite: !message! See log at !LogFullPath!." >nul 2>&1
echo "Script failed with exit code %ExitCode%" >> "%LogFullPath%"
goto PrintLogAndEnd
)
if %SkippedLocked% GTR 0 (
REM Note we need to escape the closing parentheses with a caret or it would end the if statement
set message=%SkippedLocked% program(s^) skipped (running/locked^).
eventcreate /l Application /t Warning /so "Ninite Script" /id 151 /d "Ninite: !message! See log at !LogFullPath!." >nul 2>&1
goto PrintLogAndEnd
)
if %SkippedUpdate% GTR 0 (
set message=%SkippedUpdate% program(s^) skipped but update needed.
eventcreate /l Application /t Warning /so "Ninite Script" /id 152 /d "Ninite: !message! See log at !LogFullPath!." >nul 2>&1
goto PrintLogAndEnd
)
if %SkippedOther% GTR 0 (
set message=%SkippedOther% program(s^) skipped (possibly unsupported^).
eventcreate /l Application /t Information /so "Ninite Script" /id 102 /d "Ninite: !message! See log at !LogFullPath!." >nul 2>&1
goto PrintLogAndEnd
)
set message=All programs are up to date.
eventcreate /l Application /t Information /so "Ninite Script" /id 101 /d "Ninite: !message! See log at !LogFullPath!." >nul 2>&1
goto PrintLogAndEnd

:PrintLogAndEnd
echo. >> "%LogFullPath%"
echo -------------------------------------------------------------- >> "%LogFullPath%"
echo. >> "%LogFullPath%"
REM Output a summary as first line to appear in the RMM dashboard.
echo %message%
echo --------------------------------------------------------------
REM Type log to stdout so it will appear in RMM dashboard
type "%LogFullPath%"
echo Exiting with ExitCode %exitcode% >> "%LogFullPath%"


:End
REM Display ExitCode in stdout even if log file not available
echo Exiting with ExitCode %exitcode%
exit /b %exitcode%

Need help with custom scripting? Contact MCB Systems.

9 thoughts on “Run Ninite Pro with Nice Logs from MAXfocus

  1. Ian

    Hi Mark,

    This is a great script. I have not worked with the Automated Tasks in MaxFocus that much. Can you elaborate how you actually run it in the Dashboard?

    Great work!

    Thanks,
    Ian

  2. Mark Berry Post author

    Ian, you will need to get the NiniteOne.exe program onto the machine where the script will run. You can do this manually, or write a script to download from a server.

    Because this script can take several minutes to run, I set it up as a Windows Automated Task (as opposed to running it as a DSC or 24×7 Script Check). Basic instructions are available in the Dashboard Help: https://dashboard.systemmonitor.us/helpcontents/index.html?add_a_check.htm.

    If you’d like direct support configuring all this, we can set up a short consult. Contact me: http://www.mcbsys.com/contact.

  3. Ian

    Thanks Mark. I have read through the Basic Instructions already and was more wondering about how you executed the script using but you put me on the right path. I’ll play around with it and see if I can get it to work. Knowing that you copy it to the local system is helpful as apposed to having the Agent push it down.

  4. JON

    Hello
    Would like to consult with you to get this script working in GFI. Would that be possible?

  5. Mike

    Hi Mark. I would like to discuss this script with you if you have some time. Let me know if you are still available and willing. Thank you.

  6. Wayne

    Is there a reason you like using Ninite over the third-party patching in Max/Solarwinds? I haven’t done a side by side comparison yet but it seems like they both cover most of the same products.

  7. Mark Berry Post author

    @Wayne, a couple years ago, I tried desperately to get MaxRM patching to work at a client site. After several weeks (or was it months?) of failures, I gave up and switched to native Windows Update for Windows patches (later switched to WSUS) and Ninite for 3rd-party patches. You may have better luck with MaxRM–it’s been a couple years, as I say, and this client had some special circumstances.

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.