Skip to main content

Automated OIBus Installation

Objective

This procedure enables efficient, large-scale OIBus deployments by combining silent installation with standardized initial configuration applied on first launch.

Key benefits:

  • Scalable deployment across multiple machines from a single disk image
  • Consistent configuration from first launch — no manual steps on each machine
  • Immediate remote management via OIAnalytics registration
  • Reduced manual effort and minimized setup errors

How it works

The approach relies on two scripts working together:

  • install.bat — ships inside the OIBus installation ZIP. It registers OIBus as a Windows service using NSSM and starts it.
  • mass-install.bat — a bootstrap wrapper you add to the disk image. It runs at machine startup, calls install.bat on first boot only, then configures OIBus through its API before registering it with OIAnalytics.

The guard at the top of mass-install.bat checks whether the OIBus Windows service already exists. If it does, the script exits immediately — so cloned machines that have already been initialized are never re-configured accidentally.

Disk Image Preparation

  1. Extract the OIBus installation ZIP to C:\Program Files\OIBus.
  2. Add mass-install.bat to this directory.
  3. Configure mass-install.bat to run at startup (via Task Scheduler or a startup script policy).
  4. Build a disk image before running the script — OIBus is not yet installed at image-capture time.

When a machine boots from this image for the first time, the script installs and configures everything automatically.

The install.bat Script

install.bat is the low-level service installer bundled with OIBus. It:

  1. Verifies it is running as Administrator.
  2. Accepts up to five parameters: -n (service name), -c (data directory), -u/-p (initial admin username/password), and -port (initial HTTP port) — see Windows installation for the full flag reference. If -c is omitted it prompts interactively.
  3. Creates the data directory if it does not exist.
  4. Checks a machine-wide registry ledger (HKLM\SOFTWARE\OIBus\Instances, shared with the graphical installer) to make sure neither the current binaries folder nor the data directory is already used by another OIBus instance, aborting with an error if so. Each instance — whether installed via this script or the graphical installer — must have its own binaries folder and its own data folder.
  5. Registers the OIBus launcher as a Windows service via NSSM, setting the application path, parameters, and working directory.
  6. Starts the service, then records the instance (name, binaries folder, data directory) in the same registry ledger so later installs can detect clashes against it.
  7. Creates a go.bat helper script that stops the service and runs OIBus directly in the console — useful for debugging.

This script is called by mass-install.bat with the appropriate parameters, so no manual interaction is needed in a disk-image context. mass-install.bat only passes -c and -n — it relies on OIBus's default admin credentials (see Phase 1 below) rather than setting them via -u/-p.

The mass-install.bat Script

mass-install.bat orchestrates the full bootstrap sequence on first boot. Each phase is described below.

Phase 1 — Configuration variables

set INSTALL_SCRIPT_PATH=%~dp0install.bat

set USERNAME=admin
set PASSWORD=pass
set OIANALYTICS_URL=https://instance.optimistik.fr/

set SERVICE_NAME=OIBus
set DATA_FOLDER_PATH=C:\OIBusData

%~dp0 resolves to the directory containing mass-install.bat, so INSTALL_SCRIPT_PATH always points to install.bat in the same folder regardless of where the script is launched from.

All five variables are defaults. Every one of them can be overridden at call time via a command-line parameter (see Phase 2). The default credentials (admin / pass) match a fresh OIBus installation.

Phase 2 — Parameter parsing

:PARSE_PARAMETERS
if "%~1"=="" goto PARSE_PARAMETERS_DONE
if "%~1"=="-n" (
set "SERVICE_NAME=%~2"
shift
shift
goto PARSE_PARAMETERS
)
if "%~1"=="-c" (
set "DATA_FOLDER_PATH=%~2"
shift
shift
goto PARSE_PARAMETERS
)
if "%~1"=="-u" (
set "USERNAME=%~2"
shift
shift
goto PARSE_PARAMETERS
)
if "%~1"=="-p" (
set "PASSWORD=%~2"
shift
shift
goto PARSE_PARAMETERS
)
if "%~1"=="-o" (
set "OIANALYTICS_URL=%~2"
shift
shift
goto PARSE_PARAMETERS
)
shift
goto PARSE_PARAMETERS
:PARSE_PARAMETERS_DONE

Each flag overrides its corresponding default:

  • -n — Windows service name (useful when running multiple OIBus instances on the same machine)
  • -c — data directory path
  • -u — OIBus API username
  • -p — OIBus API password
  • -o — OIAnalytics registration URL

All flags are optional; any omitted value falls back to the default declared in Phase 1.

Phase 3 — Input validation

:: The service name ends up as a raw, unquoted argument in several commands below (sc.exe,
:: echo), so anything outside this safe set - especially shell metacharacters like & | > < ^ -
:: could inject arbitrary commands. Reject it outright rather than trying to quote/escape it
:: differently at every use site.
echo %SERVICE_NAME%| findstr /r /v /c:"^[A-Za-z0-9 ._-]*$" >nul
if not ERRORLEVEL 1 (
echo ERROR: Service name "%SERVICE_NAME%" contains characters that are not allowed.
echo Allowed characters: letters, digits, spaces, dots, hyphens and underscores.
exit /b 1
)

:: USERNAME, PASSWORD and OIANALYTICS_URL are embedded in curl arguments and JSON bodies
:: below. Unlike the service name they must accept any character - a real password may
:: legitimately contain & | " etc. - so escape them for safe embedding instead of rejecting
:: anything.
set "ESCAPED_USERNAME=%USERNAME:\=\\%"
set "ESCAPED_USERNAME=%ESCAPED_USERNAME:"=\"%"
set "ESCAPED_PASSWORD=%PASSWORD:\=\\%"
set "ESCAPED_PASSWORD=%ESCAPED_PASSWORD:"=\"%"
set "ESCAPED_OIANALYTICS_URL=%OIANALYTICS_URL:\=\\%"
set "ESCAPED_OIANALYTICS_URL=%ESCAPED_OIANALYTICS_URL:"=\"%"

Without this, a maliciously crafted -n value could inject arbitrary commands (this script runs as Administrator), and a -u/-p/-o value containing &, |, or " — entirely plausible for a real password — would corrupt or hijack the curl calls in the phases below. From here on, use %ESCAPED_USERNAME%, %ESCAPED_PASSWORD%, and %ESCAPED_OIANALYTICS_URL% instead of the raw variables wherever they are embedded in a command or a JSON body.

Phase 4 — Administrator check

net session >nul 2>&1
if %errorLevel% neq 0 (
echo This script must be run as Administrator.
exit /b 1
)

Installing a Windows service requires elevated privileges. If the script is not running as Administrator, it exits immediately with a non-zero code so the failure is visible in the scheduler logs.

Phase 5 — Idempotency guard

sc query "%SERVICE_NAME%" >nul 2>&1
if %errorlevel% equ 0 (
echo %SERVICE_NAME% service is already installed. Skipping installation.
goto END
)

This is the key disk-image safety check. sc query returns 0 only if the named service exists. On first boot the service does not exist, so installation proceeds. On every subsequent boot (or on any machine where OIBus was already installed manually) the script exits early without touching the existing installation.

Phase 6 — Network readiness

:WAIT_FOR_NETWORK
sc query "Dnscache" | find "RUNNING" >nul
if %errorlevel% neq 0 (
timeout /t 5 >nul
goto WAIT_FOR_NETWORK
)

Installation often runs early in the boot sequence before the network stack is fully up. The script waits until the DNS Client service (Dnscache) is RUNNING — a reliable indicator that TCP/IP networking is available — before proceeding. This prevents API calls from failing due to a missing network.

Phase 7 — Installation

if not exist "%DATA_FOLDER_PATH%" mkdir "%DATA_FOLDER_PATH%"

call "%INSTALL_SCRIPT_PATH%" -c "%DATA_FOLDER_PATH%" -n "%SERVICE_NAME%"
if %errorlevel% neq 0 (
echo Error: %SERVICE_NAME% installation failed.
exit /b %errorlevel%
)

Creates the data directory if needed, then calls install.bat with the configured parameters. install.bat registers the OIBus service via NSSM and starts it. Any failure propagates the exit code so the calling scheduler task can detect and report it.

Phase 8 — Service readiness

:WAIT_FOR_OIBUS
for /f "tokens=3 delims=: " %%H in ('sc query "%SERVICE_NAME%" ^| findstr "STATE"') do (
if /I "%%H" NEQ "RUNNING" (
timeout /t 5 >nul
goto WAIT_FOR_OIBUS
)
)

Polls the service state every 5 seconds until sc query reports it as RUNNING. This is necessary because nssm.exe start returns as soon as the service process launches, not when it is fully initialized.

Phase 9 — Web server readiness

:WAIT_FOR_WEB
curl --silent --output NUL --fail http://localhost:2223/api/status
if %errorlevel% neq 0 (
timeout /t 5 >nul
goto WAIT_FOR_WEB
)

Even after the service is RUNNING, the OIBus HTTP server takes a moment to initialize its database and start listening. This loop polls /api/status — an unauthenticated endpoint that exists solely to confirm the server is reachable — every 5 seconds until it returns an HTTP 2xx response, at which point the API is ready to accept configuration calls.

Phase 10 — Initial configuration via the API

With OIBus running and its API reachable, the script creates the initial objects — scan modes, connectors, or any other resources needed for your deployment. The example below creates two scan modes and a Modbus South connector. Note the quoted "%ESCAPED_USERNAME%:%ESCAPED_PASSWORD%" — quoting protects against shell metacharacters, and the escaping from Phase 3 protects against a password containing ":

curl --location --user "%ESCAPED_USERNAME%:%ESCAPED_PASSWORD%" "http://localhost:2223/api/scan-modes" ^
--header "Content-Type: application/json" ^
--header "Accept: application/json" ^
--data "{\"name\": \"Every 5 seconds\", \"description\": \"Trigger every 5 seconds\", \"cron\": \"*/5 * * * * *\"}"

curl --location --user "%ESCAPED_USERNAME%:%ESCAPED_PASSWORD%" "http://localhost:2223/api/scan-modes" ^
--header "Content-Type: application/json" ^
--header "Accept: application/json" ^
--data "{\"name\": \"Every 2 minutes\", \"description\": \"Trigger every 2 minutes\", \"cron\": \"* */2 * * * *\"}"

curl --location --user "%ESCAPED_USERNAME%:%ESCAPED_PASSWORD%" "http://localhost:2223/api/south" ^
--header "Content-Type: application/json" ^
--header "Accept: application/json" ^
--data "{\"name\": \"Modbus\", \"type\": \"modbus\", ...}"

Add, remove, or adapt these calls to match your target configuration. Every object you can create through the OIBus UI has a corresponding POST endpoint.

API reference

For the full list of available endpoints and their request/response schemas, see the API documentation.

Phase 11 — OIAnalytics registration

curl --location --request POST --user "%ESCAPED_USERNAME%:%ESCAPED_PASSWORD%" "http://localhost:2223/api/oianalytics/register" ^
--header "Content-Type: application/json" ^
--data "{\"host\": \"%ESCAPED_OIANALYTICS_URL%\", ...}"

Submits a registration request to OIAnalytics on behalf of this OIBus instance. The registration is not immediately active — it enters a pending state until an OIAnalytics administrator approves it.

Activation code

Once the registration request is received, a 6-character activation code is generated. This code is displayed on the OIBus registration screen and also sent by email to the address configured in OIAnalytics. The administrator must enter it in Configuration → OIBus in OIAnalytics to complete the pairing.

After approval, all subsequent configuration changes can be pushed remotely from OIAnalytics without touching each machine individually.

For the full registration flow, network requirements, and security model, see the OIAnalytics Registration page.

If you do not use OIAnalytics, omit this step.

Full Script

@echo off
:: This script checks if OIBus is already installed. If not, it waits for the network,
:: installs OIBus, waits for the service to start, and registers it with OIAnalytics.

:: Path to install.bat (must be in the same folder as this script)
set INSTALL_SCRIPT_PATH=%~dp0install.bat

setlocal enabledelayedexpansion

:: Default values — all can be overridden via command-line parameters
set SERVICE_NAME=OIBus
set DATA_FOLDER_PATH=C:\OIBusData
set USERNAME=admin
set PASSWORD=pass
set OIANALYTICS_URL=https://instance.optimistik.fr/

:PARSE_PARAMETERS
if "%~1"=="" goto PARSE_PARAMETERS_DONE
if "%~1"=="-n" (
set "SERVICE_NAME=%~2"
shift
shift
goto PARSE_PARAMETERS
)
if "%~1"=="-c" (
set "DATA_FOLDER_PATH=%~2"
shift
shift
goto PARSE_PARAMETERS
)
if "%~1"=="-u" (
set "USERNAME=%~2"
shift
shift
goto PARSE_PARAMETERS
)
if "%~1"=="-p" (
set "PASSWORD=%~2"
shift
shift
goto PARSE_PARAMETERS
)
if "%~1"=="-o" (
set "OIANALYTICS_URL=%~2"
shift
shift
goto PARSE_PARAMETERS
)
shift
goto PARSE_PARAMETERS
:PARSE_PARAMETERS_DONE

:: The service name ends up as a raw, unquoted argument in several commands below (sc.exe,
:: echo), so anything outside this safe set - especially shell metacharacters like & | > < ^ -
:: could inject arbitrary commands. Reject it outright rather than trying to quote/escape it
:: differently at every use site.
echo %SERVICE_NAME%| findstr /r /v /c:"^[A-Za-z0-9 ._-]*$" >nul
if not ERRORLEVEL 1 (
echo ERROR: Service name "%SERVICE_NAME%" contains characters that are not allowed.
echo Allowed characters: letters, digits, spaces, dots, hyphens and underscores.
exit /b 1
)

:: USERNAME, PASSWORD and OIANALYTICS_URL are embedded in curl arguments and JSON bodies
:: below. Unlike the service name they must accept any character - a real password may
:: legitimately contain & | " etc. - so escape them for safe embedding instead of rejecting
:: anything.
set "ESCAPED_USERNAME=%USERNAME:\=\\%"
set "ESCAPED_USERNAME=%ESCAPED_USERNAME:"=\"%"
set "ESCAPED_PASSWORD=%PASSWORD:\=\\%"
set "ESCAPED_PASSWORD=%ESCAPED_PASSWORD:"=\"%"
set "ESCAPED_OIANALYTICS_URL=%OIANALYTICS_URL:\=\\%"
set "ESCAPED_OIANALYTICS_URL=%ESCAPED_OIANALYTICS_URL:"=\"%"

:: Check if running as Admin
net session >nul 2>&1
if %errorLevel% neq 0 (
echo This script must be run as Administrator.
exit /b 1
)

:: Idempotency guard: skip if OIBus service is already installed
sc query "%SERVICE_NAME%" >nul 2>&1
if %errorlevel% equ 0 (
echo %SERVICE_NAME% service is already installed. Skipping installation.
goto END
)

:: Wait for the network to be available
echo Waiting for the network service to start...
:WAIT_FOR_NETWORK
sc query "Dnscache" | find "RUNNING" >nul
if %errorlevel% neq 0 (
timeout /t 5 >nul
goto WAIT_FOR_NETWORK
)
echo Network service is running.

:: Create data directory if it doesn't exist
if not exist "%DATA_FOLDER_PATH%" mkdir "%DATA_FOLDER_PATH%"

:: Install OIBus as a Windows service
call "%INSTALL_SCRIPT_PATH%" -c "%DATA_FOLDER_PATH%" -n "%SERVICE_NAME%"
if %errorlevel% neq 0 (
echo Error: %SERVICE_NAME% installation failed.
exit /b %errorlevel%
)

:: Wait for the OIBus service to reach RUNNING state
echo Waiting for %SERVICE_NAME% service to start...
:WAIT_FOR_OIBUS
for /f "tokens=3 delims=: " %%H in ('sc query "%SERVICE_NAME%" ^| findstr "STATE"') do (
if /I "%%H" NEQ "RUNNING" (
timeout /t 5 >nul
goto WAIT_FOR_OIBUS
)
)
echo %SERVICE_NAME% service is now running.

:: Wait for the OIBus web server to accept requests
echo Waiting for %SERVICE_NAME% web server to be up...
:WAIT_FOR_WEB
curl --silent --output NUL --fail http://localhost:2223/api/status
if %errorlevel% neq 0 (
timeout /t 5 >nul
goto WAIT_FOR_WEB
)
echo %SERVICE_NAME% web server is now up.

:: Create scan modes
echo Creating scan modes...
curl --location --user "%ESCAPED_USERNAME%:%ESCAPED_PASSWORD%" "http://localhost:2223/api/scan-modes" ^
--header "Content-Type: application/json" ^
--header "Accept: application/json" ^
--data "{\"name\": \"Every 5 seconds\", \"description\": \"Trigger every 5 seconds\", \"cron\": \"*/5 * * * * *\"}"
echo.
if %errorlevel% neq 0 (
echo Error: Failed to create scan mode.
exit /b %errorlevel%
)

curl --location --user "%ESCAPED_USERNAME%:%ESCAPED_PASSWORD%" "http://localhost:2223/api/scan-modes" ^
--header "Content-Type: application/json" ^
--header "Accept: application/json" ^
--data "{\"name\": \"Every 2 minutes\", \"description\": \"Trigger every 2 minutes\", \"cron\": \"* */2 * * * *\"}"
echo.
if %errorlevel% neq 0 (
echo Error: Failed to create scan mode.
exit /b %errorlevel%
)

:: Create North connector (OIAnalytics)
echo Creating North connector...
curl --location --user "%ESCAPED_USERNAME%:%ESCAPED_PASSWORD%" "http://localhost:2223/api/north" ^
--header "Content-Type: application/json" ^
--header "Accept: application/json" ^
--data "{\"name\": \"North OIAnalytics\", \"type\": \"oianalytics\", \"description\": \"\", \"enabled\": true, \"settings\": {\"useOiaModule\": true, \"compress\": true, \"timeout\": 60000}, \"caching\": {\"trigger\": {\"scanModeId\": null, \"scanModeName\": \"Every 2 minutes\", \"numberOfElements\": 10000, \"numberOfFiles\": 1}, \"throttling\": {\"runMinDelay\": 200, \"maxSize\": 0, \"maxNumberOfElements\": 50000}, \"error\": {\"retryInterval\": 5000, \"retryCount\": 3, \"retentionDuration\": 0}, \"archive\": {\"enabled\": false, \"retentionDuration\": 72}}, \"transformers\": []}"
echo.
if %errorlevel% neq 0 (
echo Error: Failed to create North connector.
exit /b %errorlevel%
)

:: Create South connector (Modbus)
echo Creating South connector...
curl --location --user "%ESCAPED_USERNAME%:%ESCAPED_PASSWORD%" "http://localhost:2223/api/south" ^
--header "Content-Type: application/json" ^
--header "Accept: application/json" ^
--data "{\"name\": \"Modbus\", \"type\": \"modbus\", \"description\": \"Modbus South\", \"enabled\": true, \"settings\": {\"host\": \"127.0.0.1\", \"port\": 502, \"connectTimeout\": 10000, \"networkTimeout\": 10000, \"retryInterval\": 10000, \"slaveId\": 1, \"addressOffset\": \"modbus\", \"endianness\": \"big-endian\", \"swapBytesInWords\": false, \"swapWordsInDWords\": false, \"batchQuery\": false, \"groupingGap\": 0}, \"groups\": [], \"items\": []}"
echo.
if %errorlevel% neq 0 (
echo Error: Failed to create South connector.
exit /b %errorlevel%
)

:: Register OIBus with OIAnalytics
echo Registering OIBus...
curl --location --request POST --user "%ESCAPED_USERNAME%:%ESCAPED_PASSWORD%" "http://localhost:2223/api/oianalytics/register" ^
--header "Content-Type: application/json" ^
--data "{\"host\": \"%ESCAPED_OIANALYTICS_URL%\", \"useProxy\": false, \"proxyUrl\": null, \"proxyUsername\": null, \"proxyPassword\": null, \"useApiGateway\": false, \"apiGatewayHeaderKey\": null, \"apiGatewayHeaderValue\": null, \"apiGatewayBaseEndpoint\": null, \"acceptUnauthorized\": false, \"commandRefreshInterval\": 60, \"commandRetryInterval\": 10, \"messageRetryInterval\": 10, \"commandPermissions\": {\"testCustomTransformer\": true, \"deleteCustomTransformer\": true, \"updateCustomTransformer\": true, \"createCustomTransformer\": true, \"updateNorthCacheContent\": true, \"getNorthCacheFileContent\": true, \"searchNorthCacheContent\": true, \"updateHistoryCacheContent\": true, \"getHistoryCacheFileContent\": true, \"searchHistoryCacheContent\": true, \"setpoint\": true, \"testNorthConnection\": true, \"deleteNorth\": true, \"updateNorth\": true, \"createNorth\": true, \"testSouthItem\": true, \"testSouthConnection\": true, \"createOrUpdateSouthItemsFromCsv\": true, \"deleteSouth\": true, \"updateSouth\": true, \"createSouth\": true, \"testHistorySouthItem\": true, \"testHistorySouthConnection\": true, \"testHistoryNorthConnection\": true, \"createOrUpdateHistoryItemsFromCsv\": true, \"deleteHistoryQuery\": true, \"updateHistoryQuery\": true, \"createHistoryQuery\": true, \"deleteCertificate\": true, \"updateCertificate\": true, \"createCertificate\": true, \"deleteIpFilter\": true, \"updateIpFilter\": true, \"createIpFilter\": true, \"deleteScanMode\": true, \"updateScanMode\": true, \"createScanMode\": true, \"updateRegistrationSettings\": true, \"updateEngineSettings\": true, \"regenerateCipherKeys\": true, \"restartEngine\": true, \"updateVersion\": true}}"
echo.
if %errorlevel% neq 0 (
echo Error: Failed to register OIBus.
exit /b %errorlevel%
)

:END

Usage

.\mass-install.bat -n "OIBus" -c "C:\OIBusData" -u "admin" -p "pass" -o "https://instance.optimistik.fr/"
ParameterDescriptionDefault
-nName of the OIBus Windows service.OIBus
-cPath to the OIBus data directory.C:\OIBusData
-uOIBus API username.admin
-pOIBus API password.pass
-oOIAnalytics registration URL.https://instance.optimistik.fr/