Skip to content

DATAZONE Connect

DATAZONE Connect is a generic script runner for third-party system integrations. It runs as a separate Docker container and provides a REST API through which scripts can be executed with variables.

Concept

Connect follows a scripts-first approach: Every integration (DATAZONE ERP, Teams, Slack, Zammad, etc.) is a simple shell script with standardized metadata. This means maximum flexibility with minimum effort.

Architecture

DATAZONE Control                    DATAZONE Connect
+------------------+               +------------------+
| Playbook         |  Webhook Step | Flask API (5002) |
|   Step 1: Script |  ----------> | /api/scripts/run |
|   Step 2: Task   |               |                  |
|   Step 3: Webhook| ------------> | /scripts/        |
+------------------+               |   erp-billing.sh |
                                   |   teams-notify.sh|
                                   +------------------+

How It Works

  1. Scripts are placed in the connect/scripts/ directory
  2. Connect automatically reads metadata from script headers (@name, @description, @var)
  3. Variables are passed to scripts as environment variables
  4. Credentials (passwords, API keys) come from container environment variables, not from scripts
  5. Scripts return structured results via the #RESULT:{json} convention

REST API

Health Check

GET /api/health

Returns the container status and the number of available scripts.

List Scripts

GET /api/scripts

Returns a list of all available scripts with metadata:

json
[
  {
    "name": "ERP Billing",
    "filename": "erp-billing.sh",
    "description": "Creates a TimeEntry in DATAZONE ERP.",
    "vars": [
      {"name": "CUSTOMER_NUMBER", "required": true, "description": "Customer number (e.g., 10001)"},
      {"name": "CUSTOMER_NAME", "required": true, "description": "Customer name"},
      {"name": "TASK", "required": true, "description": "Task description"}
    ]
  }
]

Execute Script

POST /api/scripts/run
Content-Type: application/json

{
  "script": "erp-billing.sh",
  "vars": {
    "CUSTOMER_NUMBER": "10001",
    "CUSTOMER_NAME": "Example GmbH",
    "TASK": "OPNsense Firmware Update"
  }
}

Synchronous execution (default): Waits for the result and returns it directly.

Asynchronous execution: With ?async=true, a job is created whose status can be queried:

POST /api/scripts/run?async=true
-> {"job_id": "abc123", "status": "running"}

GET /api/jobs/abc123
-> {"status": "completed", "exit_code": 0, "result": {...}}

Incoming Webhooks

POST /api/webhooks/incoming/{name}

Receives external webhook calls (e.g., from Zammad, GitLab, etc.) and executes the associated script. The webhook body is passed as a WEBHOOK_BODY environment variable.

Execution Log

GET /api/logs
GET /api/logs?limit=10

Returns recent script executions with status and results.

Creating Scripts

Metadata Format

Each script begins with a standardized header:

bash
#!/bin/bash
###############################################################################
# @name My Script
# @description Brief description of what the script does.
#
# @var REQUIRED_VAR    required  Description of the required variable
# @var OPTIONAL_VAR    optional  Description of the optional variable
###############################################################################
FieldRequiredDescription
@nameYesDisplay name of the script
@descriptionYesBrief description
@varNoVariable with name, required/optional, and description

Structured Return Values

Scripts can return a JSON result via the #RESULT:{json} convention:

bash
echo "#RESULT:{\"success\":true,\"id\":42,\"message\":\"Created\"}"

Connect parses the last line starting with #RESULT: and returns the JSON as the result field in the API response.

Credentials

Credentials are never stored directly in scripts. Instead, they are configured as container environment variables:

bash
# In script: use environment variable
ERP_PASSWORD="${ERP_PASSWORD:?ERP_PASSWORD not set}"

# In docker-compose.yml: define variable
# environment:
#   - ERP_PASSWORD=${ERP_PASSWORD:-}

# In .env: set value
# ERP_PASSWORD=secret123

Included Scripts

ERP Billing

File: erp-billing.sh

Creates a TimeEntry in DATAZONE ERP. Replaces the legacy Nuclos/Sema integration.

VariableRequiredDescription
CUSTOMER_NUMBERYesCustomer number (e.g., 10001)
CUSTOMER_NAMEYesCustomer name
TASKYesTask description
DURATION_HOURSNoDuration in hours (default: 0.5)
HOSTNAMENoHostname of the affected system
MODULENoModule type (opnsense, linux, etc.)
BILLABLENo"true" or "false" (default: true)

Workflow:

  1. JWT login at ${ERP_URL}/api/v1/auth/login with the service user
  2. Resolve customer by number: GET /api/v1/customers/resolve/{CUSTOMER_NUMBER} -> customer_id (UUID)
  3. Create TimeEntry: POST /api/v1/tickets/time-entries/ with customer_id, description, duration

If the customer is not found in ERP, the script fails with HTTP 404 (no fallback booking like the old Nuclos script).

ERP Service User

A dedicated service user must exist in ERP (without MFA!) with the following permissions:

  • customers:read (resolve customers)
  • timeentries:create (create TimeEntry)
  • timeentries:read (optional, for sync)

Environment variables (set in .env):

VariableDescription
ERP_URLERP server URL (e.g., https://erp.datazone.de)
ERP_USERService user email
ERP_PASSWORDService user password
ERP_TENANT_SLUGTenant slug (default: default)

Nuclos Billing (Legacy)

File: nuclos-billing.sh

Legacy Nuclos/Sema integration. Kept for rollback purposes only — use erp-billing.sh for new playbooks.

Teams Notification

File: teams-notify.sh

Sends a message to a Microsoft Teams webhook:

VariableRequiredDescription
MESSAGEYesMessage text
TITLENoMessage title (default: DATAZONE Connect)
COLORNoColor as hex value (default: 0076D7)

Environment variable: TEAMS_WEBHOOK_URL must be set in .env.

Playbook Integration

Connect is integrated via the Webhook step type in playbooks. This allows integrations to be embedded directly in maintenance workflows.

Example: Maintenance with ERP Billing

StepTypeActionOn Error
1VM SnapshotCreate snapshotstop
2TaskPVE_UPDATEcontinue
3Check OnlineWait until onlinestop
4WebhookERP Billing (book 30 min)continue
5WebhookTeams notificationcontinue

Example: Webhook Step Configuration

For the webhook step in the playbook:

FieldValue
URLhttp://datazone-connect:5002/api/scripts/run
MethodPOST
Body{"script":"erp-billing.sh","vars":{"CUSTOMER_NUMBER":"{customer_number}","CUSTOMER_NAME":"{customer_name}","TASK":"Maintenance {hostname}","DURATION_HOURS":"0.5","HOSTNAME":"{hostname}","MODULE":"{module}"}}
HeadersContent-Type: application/json

The placeholders {customer_number}, {customer_name}, {hostname}, and {module} are automatically replaced with data from the current host.

Docker Configuration

Connect runs as a separate container in the Docker network:

yaml
connect:
  build: ./connect
  container_name: datazone-connect
  environment:
    - CONNECT_TOKEN=${CONNECT_TOKEN:-}
    # DATAZONE ERP integration
    - ERP_URL=${ERP_URL:-}
    - ERP_USER=${ERP_USER:-}
    - ERP_PASSWORD=${ERP_PASSWORD:-}
    - ERP_TENANT_SLUG=${ERP_TENANT_SLUG:-default}
    # Webhook URLs
    - TEAMS_WEBHOOK_URL=${TEAMS_WEBHOOK_URL:-}
  volumes:
    - ./connect/scripts:/scripts:ro
  networks:
    - datazone-net
  restart: unless-stopped

Custom Scripts

Simply place custom scripts in the connect/scripts/ directory. The container mounts this directory as a read-only volume. After adding new scripts, no restart is needed — they are automatically detected on the next API call.

Security

Connect is only accessible within the Docker network and not reachable from outside. For additional security, a CONNECT_TOKEN can be configured.

DATAZONE Control Documentation