Docs/Infohub (Flow)/Templates and Federation from the CLI
Infohub (Flow)How-to

Templates and Federation from the CLI

Release, instantiate, derive, link, delink and update — plus creating a template server, registering with Bootstrap, and issuing tokens.

Updated 15 Oct 2026Infohub Docs

Templating is where CLI automation pays off most, because a template fleet is exactly the repetitive work a script should do. Federation adds the multi-instance case, and 7.2.0 gained several verbs for it.

The template operations#

Operation Purpose
--release / --unrelease Publish a template, or withdraw it (--entity-type metric, metricfolder, eventscheme, measure or Chart, plus --entity-id)
--instantiate Create model instances from a template (--source-entity-type, --source-entity-id, --destination-entity-type, --destination-entity-id)
--derive Create a new template from an existing one — the same verb into a template-side destination
--list-optional List a template's optional members, with --source-entity-type/--source-entity-id
--create-template Promote a model folder, metric or event scheme into a template-side folder
--link-entity Attach existing local content to a template (--entity-type, --entity-id, --template-id)
--delink-entity Detach an instance to plain local content
--update-from-template Push template changes into one instance
--update-instances-of Update every instance of one template
--batch-update-from-template Update the entities listed in --entity-ids-file or --batch-file; --continue-on-error keeps going past a failure
--list-out-of-sync List instances whose recorded template version differs from the template's (--entity-type required)
--update-all-out-of-sync Update everything stale; --dry-run reports without writing
--list-delinked / --cleanup-delinked Show, and remove, properties an instance has delinked from its template

The entity type spellings are shared across the family: folder or metricfolder, metric, event or eventscheme, measure, in any case.

Release before instantiate — always#

# mandatory: an unreleased source is refused
--release --entity-type metric --entity-id <id>

# then instantiate
--instantiate --source-entity-type Metric --source-entity-id <id> \
    --destination-entity-type MetricFolder --destination-entity-id <dest> \
    --naming-pattern "Unit {0}" --instance-count 3

Instantiating a template that has not been released is refused with exit 1: "Source Metric ID n is not a released template. Unreleased template members are skipped on instantiate, so this would create nothing", followed by the exact --release command to run. For a template-server source the check reads the served model, and the message says to release on the publishing instance. Earlier 7.2.0 builds created nothing and exited 0.

Important

Assert the instance count, do not trust the exit code. An instantiation that creates zero entities because matching instances already exist in the destination still exits 0, with a warning. A build script that trusts the exit code carries on and then builds charts against measures that do not exist, producing a cascade of confusing failures a hundred steps later.

--instantiate ...
if ($LASTEXITCODE -ne 0) { throw "instantiate failed" }

$made = & $exe @conn --list-metrics --metric-folder-id $dest --output-format json | ConvertFrom-Json
if (@($made | Where-Object { $_.Name -like 'Unit *' }).Count -ne 3) { throw "expected 3 instances in folder $dest" }

Fail loudly at the instantiation, not vaguely later.

--conflict-handling defaults to Rename for --instantiate, --derive and --create-template (and to Skip for --import-model). Pass Skip or Overwrite to change it.

Engine and platform#

An instantiation needs a default data engine and platform for what it creates. The GUI's defaults live in the workstation's settings file, which a headless run cannot read, so the CLI uses --engine-id and --platform-id when given, falls back to the instance's only engine and only platform when there is exactly one of each, and otherwise refuses: "No default data engine and/or platform could be resolved". A freshly created instance has one of each; the moment your script adds a second, pass both IDs.

Name and attribute patterns#

{0} is the sequence number — starting at 1, or at --naming-start — usable in both the name pattern and the attribute value patterns:

--instantiate --source-entity-type Metric --source-entity-id <id> \
    --destination-entity-type MetricFolder --destination-entity-id <dest> \
    --naming-pattern "Unit {0}" --instance-count 3 \
    --model-attribute-patterns "Unit Number=0{0}0"

So a count of 3 produces Unit 1, Unit 2, Unit 3, with attribute values 010, 020, 030. Several attributes are comma-separated: "Region=North{0},Year=202{0}".

For a single instance whose name and attribute do not follow the sequence, instantiate with a count of 1 and explicit values.

Two things to remember:

Attribute value format must match the source system exactly. If tags are named 010-FT-001, the attribute must be 010, not 10. Leading zeros belong in the pattern.

Measures do not participate in attribute-driven naming. Template measure names come through as authored. Instance identity comes from the containing metric and folder.

Resolving instance IDs afterwards#

You did not create the instance entities individually, so you do not know their IDs. Resolve them by name:

$unitMetric  = Get-MetricIdByName  $destFolder "Unit 1"
$unitMeasure = Get-MeasureIdByName $unitMetric "Steam Flow Hourly"

This is why consistent naming inside a template matters — a script resolving instance content by name depends on it.

Optional members are opt-in#

Template members marked optional are excluded from an instantiation unless you ask for them:

# what is optional on this template?
--list-optional --source-entity-type Metric --source-entity-id <id>

# include some, by ID or name — or all
--instantiate ... --include-optional "Steam Flow Weekly,Steam Flow Monthly"
--instantiate ... --include-optional all

The filter applies when instantiating to the model. A --derive carries every member, optional or not, into the new template, as the GUI does.

How dependencies are bound#

A template measure's event context, parent and calculation dependencies point at template entities. On instantiation the CLI binds them to the copies in the destination the way the GUI's dependency-mapping dialog does:

  1. An event scheme that this instantiation creates (a folder instantiation carrying its own event schemes) stays unmapped and binds to the new instance.
  2. An explicit entry in --mapping-file wins.
  3. Otherwise the candidate in the destination folder, then the nearest ancestor folder, then the only candidate.
  4. Several candidates with none in place is refused before anything is created, naming the candidates and pointing at --mapping-file.

Instantiating a single measure resolves its parent measure and calculation dependencies the same way, re-points the copy at the chosen entities (including Measure_n references in the calculation text), and refuses up front if any cannot be resolved. Deriving a folder into a template folder creates its event schemes with it.

--enable-auto-mapping and --allow-fuzzy-matching only pick from the candidates the GUI's dialog would offer — a same-named entity elsewhere in the model is not a legal target.

Note

Older builds bound the first same-named event. Before this, every instantiation after the first bound its event context to the first model event scheme with a matching name wherever it sat, and the next update doubled the row. If instances built with an earlier 7.2.0 build show measures whose event context belongs to another folder, that is why; re-attach the correct scheme with --attach-measure-event-scheme.

Template servers#

7.2.0 added the verbs to set federation up by script, which matters because these steps recur per site.

Register with Bootstrap#

--register-with-bootstrap [--platform-id <id>] [--bootstrap-port 4501] [--use-https]

Registers the connected instance with the Flow Bootstrap service on the platform's host, sending the instance's SQL connection details with the password encrypted. With one platform configured it is used; with several, --platform-id is required. Bootstrap listens on port 4501 by default.

The verb reports a refusal rather than claiming success. Bootstrap answers a rejected registration — bad credentials, an instance database it cannot reach, an instance with no identity property — with an error body, and the CLI prints it and exits 1. Earlier builds printed "Registered" and exited 0 in that case, and the instance then silently never ran.

Issue a server token#

--create-server-token --server-id <id> --name "Site A" --token-user "DOMAIN\svc" --token-password ... [--expiry 2027-12-31]

This asks the instance's running, deployed Flow Server for a token and stores the matching ServerKey row. --token-user is validated against the Windows account database of the machine the server runs on, not against Infohub users, and the password travels RSA-encrypted as it does from the GUI. The token is printed; a subscriber passes it as --template-server-key. Issue one per consumer rather than sharing, so access can be revoked per site.

--create-server-key only writes a ServerKey row with an invented value. Nothing can authenticate with it; use --create-server-token.

--create-template-server --template-server-name "Group Library" \
    --template-server-host <publisher host> --template-server-instance <publisher instance name> \
    [--template-server-port 4501] --template-server-key <token>

--template-server-host is the publisher's HTTP host, not its SQL host. The verb writes the full property set — address, instance, key, port, https, proxy, timeout — with the server key and any proxy password stored encrypted, so creating the link by script does not leave credentials in plain text in the database.

--refresh-template-servers (or --refresh-template-server <id>) verifies each link and loads its template model for that command only; nothing is persisted, and the verbs that consume templates load the model themselves.

Important

Server keys expire, and a lapsed key fails like a network problem. Record the expiry dates when you issue keys, and rotate ahead of them. Verify after any rotation by actually deriving or refreshing a template — a federated link that is not exercised for months can be non-functional long before anyone notices, and the symptom looks like connectivity rather than authentication.

Deriving and the two-step#

A site does not instantiate a remote template directly. It derives a local template from the remote one, then instantiates the local template. The derive is --derive --source-entity-type ... --source-entity-id ... --template-server-name <link> (or --template-server-id) into a template-side destination.

Both steps matter, and so does releasing the derived template locally before instantiating it. In script terms:

  1. Create the template server link.
  2. Derive the template.
  3. Verify the derived template — see below.
  4. Release it locally.
  5. Instantiate it.
  6. Assert the instance count.

Verify a derived template before releasing it#

A derive resolves a structure across a network boundary. Confirm the result rather than assuming it:

# after deriving, compare against what you expected
$metrics  = & $exe @conn --list-metrics  --metric-folder-id $tplFolder --view-type template --output-format json | ConvertFrom-Json
$measures = & $exe @conn --list-measures --metric-id $($metrics[0].MetricID) --output-format json | ConvertFrom-Json

if ($measures.Count -lt $expectedMeasureCount) {
    throw "derived template has $($measures.Count) measures, expected $expectedMeasureCount"
}
Important

Check the measure count in particular. A derived template that arrives with its metrics but missing some measures will release and instantiate happily, producing instances with holes that are only obvious when values never appear. Comparing the derived template against the published one before releasing it takes two minutes and prevents a confusing afternoon.

Charts are only partly covered#

Charts can be released and un-released as templates from the CLI in 7.2.0:

--release   --entity-type Chart --entity-id <id>
--unrelease --entity-type Chart --entity-id <id>

The remaining chart template operations — link, delink, instantiate, synchronise — are Flow Config activities. So a scripted build can create charts, configure them, release them as templates, and delete them (--delete-chart). Instantiating and updating chart instances stays manual. Plan build scripts around that boundary rather than into it.

Updating instances#

--update-from-template --entity-type metric --entity-id <instance id>
--update-instances-of  --entity-type metric --entity-id <template id>
--update-all-out-of-sync [--dry-run] [--include-children]

Updates compare each instance's recorded template version against the template's current version. An instance already at the template's version is reported as nothing to do, and re-running an update is harmless.

Remember the sequence when the template has changed: edit, release again, then update. An edit un-releases the template, and an update against an unreleased template is refused with exit 1 rather than reported as done; --list-out-of-sync and --update-all-out-of-sync leave such instances out of their scans and print one warning block naming each entity skipped and its template. When an update fails, the reason the import gave is printed next to the entity rather than a bare "Update failed".

--update-all-out-of-sync scans folders, metrics, event schemes and measures, local and template-server linked. Entities under a folder the run updates are left to that folder update (pass --include-children to update them individually as well); after the folder pass it rescans and takes the leftovers one by one, event schemes first, because a folder import writes its metrics before its event schemes. Earlier builds scanned folders only and never converged on template-server entities; both are fixed.

Scope updates deliberately. A staged rollout — one instance, verify, then an area, then the rest — is right for a fleet of any size, and it is easy to express in a script as a loop over an ordered list, or as a --batch-update-from-template --entity-ids-file per stage.

Delinked properties#

An instance can carry a calendar, consumer, connection or limit its template does not. --list-delinked --entity-type metric --entity-id <id> reports them, and --cleanup-delinked --entity-type metric --entity-id <id> --property-type timescheme --target-id <id> removes one. Removing a calendar also removes the measure's limits on that calendar (clearing any chart items that bound them), as the GUI does; it refuses while a consumer, a tag transform, another measure's sourced limit or a message segment still uses the calendar. A limit left on a calendar the measure no longer has broke every later template pass over the measure, which is why the cascade is there.

A federation build sequence#

# --- on the publisher ---
--register-with-bootstrap
--release --entity-type metric --entity-id <id>
--create-server-token --server-id <id> --name "Site A" --token-user "DOMAIN\svc" --token-password ...

# --- on each consumer ---
--create-template-server --template-server-name "Group Library" --template-server-host <host> --template-server-instance <name> --template-server-key <token>
--derive --source-entity-type Metric --source-entity-id <id> --template-server-name "Group Library" --destination-entity-type MetricFolder --destination-entity-id <tplFolder>
# verify, then:
--release --entity-type metric --entity-id <localDerivedId>
--instantiate --source-entity-type Metric --source-entity-id <localDerivedId> --destination-entity-type MetricFolder --destination-entity-id <dest> --naming-pattern "Unit {0}" --instance-count 3
# assert the count

Every step's exit code checked, and the two silent points — an instantiation that found its instances already present, and an incompletely derived template — explicitly asserted.

Diagnosing#

Symptom Cause
Instantiate refused "not a released template" Release the source — the message names the command; for a server source, release on the publisher
Instantiation exited 0 and created nothing Matching instances already exist in the destination
Instantiate refused on engine/platform More than one of either on the instance; pass --engine-id and --platform-id
Instantiate refused naming several event schemes Ambiguous event context; answer it with --mapping-file
Optional members missing from instances Expected — pass --include-optional
Instances all read the same tag Attribute token missing, or attribute set too high in the tree
Some instances read a wrong tag Attribute value format — leading zeros, case, spaces
Derived template missing measures Verify against the published template before releasing
Derived template will not instantiate Local derived template not released
Registration refused by Bootstrap Read the printed body — SQL login, unreachable instance database, or an instance with no identity
Cannot reach the template server Network path to the publisher's Bootstrap, or proxy; --template-server-host is the HTTP host, not the SQL host
Authentication fails Server key expired, revoked, issued for another instance, or a --create-server-key value rather than a token
Update refused "not released" Re-release the template, then update
Update did nothing Instances already at the template's version
Instances no longer take updates They were delinked
Calendar cleanup refused A consumer, tag transform, sourced limit or message segment still uses it
esc
↑↓ navigate · ↵ open · ⌘K anywhere