Skip to content

Print Service – Setup

Complete step-by-step guide to setting up the merchantCENTRAL Print Service: from installing the local print service and connecting it to Business Central to printing your first test label.

Architecture overview

Business Central (Cloud) cannot communicate directly with local printers. MC.PrintService is a lightweight local application installed on the customer's premises that receives print jobs and forwards them to Windows or Zebra printers.

┌──────────────────┐   Print job       ┌──────────────────┐   PDF / ZPL    ┌──────────────┐
│ Business Central │ ────────────────► │  MC.PrintService │ ─────────────► │   Printer    │
│   (Cloud/SaaS)   │                   │  (on-premises)   │                │ Windows/Zebra│
└──────────────────┘                   └──────────────────┘                └──────────────┘

There are three transport modes for the connection from BC to MC.PrintService:

Direct Push (default) Relay (Option C) Poll (Option D)
How it works BC calls the service directly via the fixed public IP BC pushes to a queue in the customer's own Azure The Function polls BC; BC stays passive
Inbound port forwarding required not required not required
Fixed public IP required not required not required
BC makes outbound calls yes yes (job incl. label) no (only an optional wake-ping)
Customer's Azure subscription no yes (~€1–2/month) yes (~€1–2/month)
Status ✅ available ✅ available ✅ available

Which mode should I choose?

Direct Push is the default and the best choice for most customers: free of charge, immediate status feedback, and simple to set up. It requires a fixed public IP and the ability to open a port. Relay and Poll are for customers without a fixed IP / without inbound port forwarding. The difference: with Relay, BC actively pushes the job into Azure; with Poll, BC stays fully passive (the Function fetches the jobs) — ideal when BC must not make outbound web service calls.


Prerequisites

  • [ ] merchantCENTRAL Hub – installed and active
  • [ ] Print Service App – installed (AppSource or deployed by ALTENBRAND)
  • [ ] Module license – at least a demo license for PRINTSERVICE
  • [ ] Windows PC or server on the customer's network for MC.PrintService (Windows 10/11 or Server 2016+)
  • [ ] Printer – Windows printer driver (for PDF) or Zebra printer reachable via TCP/IP (for ZPL)
  • [ ] For Direct Push additionally: fixed public IP and access to the router/firewall

Step 1: Install MC.PrintService

The local print service is installed once on a machine in the customer's network that runs continuously and can reach the printers.

  1. Extract the ZIP package.
  2. Double-click Install.bat → confirm the UAC prompt.
  3. The script installs the service, configures autostart and a firewall rule, and automatically generates an API key that is displayed at the end.
  4. Note the API key – it will be required in Step 3 when configuring Business Central.

The service listens on port 5050 by default. Configuration UI in the browser: http://localhost:5050/config.

Looking up the API key later

The key is stored in appsettings.json in the installation directory under Security:ApiKey. The configuration UI displays at the top whether a key is set.

Option B – Docker (ZPL out of the box only)

cd service
# Set API key (required for remote access):
export MCPS_API_KEY="$(openssl rand -hex 32)"
docker compose up -d

The service is then available at http://localhost:5100. PDF printing inside the container requires a CUPS sidecar; for pure ZPL operation (Zebra printer via TCP) Docker is ideal.

No remote access without an API key

If no Security:ApiKey is configured, the service accepts local requests only (localhost) for security reasons. Requests from Business Central (Cloud) will be rejected with 401. The key is therefore mandatory for remote operation.


Part A: Setting up Direct Push

With Direct Push, Business Central reaches MC.PrintService via the customer's fixed public IP. Three layers of protection secure the endpoint: API key, HTTPS, and a firewall restriction to Business Central.

A1 – Provide HTTPS

Business Central (Cloud) sends print jobs from the Azure data center and validates the server certificate. Self-signed certificates will not work. Choose one of the following options:

Option Description Best suited for
Reverse Proxy (recommended) Caddy, nginx, or IIS in front of the service, subdomain per customer (e.g. print.kunde.de → fixed IP), Let's Encrypt certificate obtained automatically Most customers with their own domain
Let's Encrypt IP certificate Certificate issued directly for the IP address (short-lived ~6 days, automatic renewal via ACME) Customers without a domain
Kestrel with PFX HTTPS directly in the service: Kestrel:Endpoints:Https in appsettings.json with the path to the certificate Simple setups with an existing certificate

Reverse proxy with Caddy

Caddy obtains and renews Let's Encrypt certificates fully automatically. A minimal Caddyfile looks like: print.kunde.de { reverse_proxy localhost:5050 }.

A2 – Configure port forwarding

In your router/firewall, forward the public HTTPS port (e.g. 443) to the machine running MC.PrintService (port 5050, or the port used by the reverse proxy).

A3 – Restrict the firewall to Business Central

To prevent the forwarded port from being exposed to the entire internet, restrict inbound access to the IP addresses used by Business Central. These are consolidated in the Azure service tag Dynamics365BusinessCentral (maintained automatically by Microsoft).

  • Firewalls with service tag support can use the tag directly.
  • Otherwise, download the IP ranges as a JSON list and configure them as a firewall rule.

A4 – Create a printer in Business Central

  1. Search for Print Service Printers in BC → New.
  2. Fill in the fields:
Field Value
Code Unique identifier, e.g. PDF-OFFICE or ZPL-WAREHOUSE1
Description Descriptive name
Service URL Public HTTPS URL of the service, e.g. https://print.kunde.de (for local tests: http://localhost:5050)
Print Method PDF (Windows printer) or ZPL (Zebra thermal printer)
Printer Name For PDF: exact Windows printer name. For ZPL: IP address of the Zebra printer
ZPL Port For ZPL only: TCP port (default 9100)
  1. Enter the API key: Click the API Key field (DrillDown) → enter the key generated in Step 1. It is stored encrypted in the central Credential Store and is never displayed in plain text.

HTTPS required for remote URLs

The Service URL must use https://. http:// is only permitted for localhost/127.0.0.1 (local testing) and will be rejected for remote addresses.

A5 – Test the connection

Run the Test Connection action on the Printer Card. On success, the status changes to Online. Then print a test label (see Next steps).

Error Cause Resolution
Status remains Offline Service not reachable Check port forwarding, reverse proxy, and service status
401 Unauthorized API key missing or incorrect Compare the key in BC with the one in appsettings.json
Certificate error Invalid or self-signed certificate Use a valid certificate (Let's Encrypt)

Part C: Setting up Relay (for customers without a fixed IP)

In Relay mode, the customer hosts the intermediary components in their own Azure. Business Central pushes the print job there; MC.PrintService in agent mode maintains only an outbound connection – no inbound port forwarding, no fixed IP, and no self-managed HTTPS certificate required. The print data flows exclusively through the customer's own Azure subscription, never through ALTENBRAND.

BC  ──►  Azure Function ──►  Service Bus Queue          (customer Azure)
                    │              │
                    │ Blob (label) │ AMQP (outbound 443)
                    ▼              ▼
              Blob Storage    MC.PrintService (agent mode, on-premises)  ──►  Printer

The status return path runs actively from the Function back to Business Central (Managed Identity, no polling) – so in Relay mode a print job also moves from Sending to Printed or Failed.

Prerequisites for Relay

In addition to the general prerequisites above: a customer Azure subscription with permission to create resources and role assignments (role Owner or User Access Administrator), plus Azure CLI and Azure Functions Core Tools v4 on the machine running the deployment.

C1 – Provision the Azure infrastructure

The included Bicep/ARM template (azure/deploy/ in the Print Service repository) creates, in a single step in the customer's subscription: Service Bus (Basic) + queue print-jobs, a Storage Account + blob container labels, and a Function App (Consumption) with a system-assigned Managed Identity plus the required role assignments.

Via Azure CLI:

az group create -n rg-mcps-relay -l westeurope

az deployment group create -g rg-mcps-relay \
  --template-file azure/deploy/main.bicep \
  --parameters \
      namePrefix=mcpsrelay \
      bcEnvironmentUrl="https://api.businesscentral.dynamics.com/v2.0/<tenantId>/<environment>" \
      bcCompanyId="<company-systemid-guid>"

Alternatively, use the "Deploy to Azure" button in azure/README.md. From the deployment outputs, note: functionBaseUrl, statusUrl, managedIdentityPrincipalId, and the Service Bus name.

Finding the company SystemId

bcCompanyId is the SystemId of the BC company. It is available in BC under Companies (field Id) or via the API …/api/v2.0/companies.

C2 – Publish the Function code

The template creates only the empty Function App. Publish the code once:

func azure functionapp publish <functionAppName>   # name from the deployment output

C3 – Register the Managed Identity as a BC Application User

So that the Function may report print status back, its Managed Identity is registered as an application user in Business Central – the agent itself needs no BC access.

  1. In Microsoft Entra ID, open the enterprise application of the Function's Managed Identity (object/principal id = output managedIdentityPrincipalId, display name = Function App name) and note its Application (Client) Id.
  2. In Business Central, search for Microsoft Entra ApplicationsNew → enter the Client Id, State = Enabled.
  3. Assign the permission set ALN MCPS Relay API to the entry. It is deliberately minimal (status callback only), not full admin access.

S2S authentication

This is the only slightly more involved step. It follows the standard service-to-service pattern used throughout merchantCENTRAL (cf. OAuth2 in the hub).

C4 – Retrieve the keys

Function key (entered in BC on the printer):

az functionapp keys list -g rg-mcps-relay -n <functionAppName> --query "functionKeys.default" -o tsv

Service Bus listen SAS (entered in the agent configuration):

az servicebus queue authorization-rule keys list -g rg-mcps-relay \
  --namespace-name <serviceBusName> --queue-name print-jobs --name AgentListen \
  --query primaryConnectionString -o tsv

C5 – Create a printer in Business Central (transport Relay)

  1. Search for Print Service Printers in BC → New.
  2. Set Transport Mode to Relay (Azure Queue). The Direct Push fields (Service URL, API Key) are hidden and the Relay fields appear.
  3. Fill in the fields:
Field Value
Code / Description as for Direct Push
Print Method PDF (Windows printer) or ZPL (Zebra)
Printer Name Windows printer name (PDF) or IP address (ZPL) – the agent prints locally with it
ZPL Port ZPL only (default 9100)
Relay Function URL functionBaseUrl from the deployment (e.g. https://mcpsrelay-func-....azurewebsites.net)
Relay Function Key Click the DrillDown → enter the Function key from C4 (stored encrypted in the Credential Store)

C6 – Start the agent (MC.PrintService) in agent mode

In the appsettings.json of the MC.PrintService installed in Step 1, fill in the Relay section and restart the service:

"Relay": {
  "Enabled": true,
  "ServiceBusConnectionString": "<listen SAS from C4>",
  "QueueName": "print-jobs",
  "MaxDeliveryCount": 5,
  "StatusCallbackUrl": "<statusUrl from C1>",
  "StatusCallbackKey": "<Function key from C4>"
}

The agent runs alongside the HTTP listener – so a single instance can serve Direct Push and Relay printers at the same time. The configuration UI (…/config) shows under "Relay Mode" whether agent mode is active.

C7 – Test

Print a label to the relay printer in BC. Flow: job → Sending → the agent pulls it from the queue, prints, and reports back → Printed.

Error Cause Resolution
Job stays Sending Agent offline / wrong listen SAS Check the agent service, Relay:Enabled, and the listen SAS; after MaxDeliveryCount attempts the job becomes Failed
401 on enqueue Wrong/missing Function key Check the Function key on the printer (C5)
Status does not reach BC Application user/permission missing Check C3 (Entra application + permission set ALN MCPS Relay API)

Costs (borne by the customer, in their Azure)

At 100–1,000 labels per day, costs are driven not by data volumes but by the pricing tier chosen: with Service Bus Basic, total costs are under ~€2/month.

→ Full setup guide with all outputs and troubleshooting: azure/README.md in the Print Service repository. Architecture, authentication, and cost breakdown in detail: docs/relay-mode-architecture.md.


Part D: Setting up Poll (BC stays passive)

The Poll mode uses the same Azure infrastructure and the same agent mode as Relay (Part C) - but the Function fetches the jobs from BC instead of BC pushing them. Business Central makes no outbound web service calls (except an optional tiny wake-ping). Ideal when BC must not be configured for outbound HTTP.

BC (passive)  ◀──Poll/Status── Azure Function ──▶ Service Bus Queue
     │  (opt.) Wake-ping ▲           │ Blob (label)        │
     └─────────────────────┘         ▼                     ▼
                              Blob Storage    MC.PrintService (agent) ──▶ Printer

Differences from Part C

Setup follows Part C (steps 1, 2, 4 and 6 are identical), with three differences:

Step For Poll
Application User (step 3) Assign the permission set ALN MCPS Poll API (instead of ALN MCPS Relay API) - the Function also needs read access to the jobs here
Printer in BC (step 5) Per printer, only Transport Mode = Poll + printer name. The Azure connection is configured once globally under Print Service Setup → Poll Connection (Function URL + Function key), not per printer
Wake-ping (optional) Leave the poll Function URL empty in Setup to keep BC fully passive - the Function then polls only on its timer (default: every 5 minutes). With a URL/key configured, BC signals "there is work" and printing starts immediately

Concurrency

A wake-ping and the timer can poll at the same time. A blob lease ensures only one poll run works at a time; additionally the Function claims each job with optimistic concurrency (ETag), so no job is ever printed twice.

Test

Print a label to the poll printer in BC. Flow: job → Sending (claimed by the Function) → the agent prints → Printed. Without a wake-ping it may take until the next timer run.


Troubleshooting

A detailed overview of the most common issues is available under MC.PrintService – Reference & Troubleshooting.

Quick check:

  1. Is the service running? → Windows Services (services.msc) → "MC.PrintService" must show "Running".
  2. Is it reachable?http://localhost:5050/health in the browser → must return Healthy.
  3. Are printers visible?http://localhost:5050/printers lists all Windows printers.
  4. Is the API key set? → The configuration UI at …/config shows the status at the top.

Next steps