mimimport

package
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 9, 2026 License: AGPL-3.0 Imports: 8 Imported by: 0

Documentation

Overview

Package mimimport converts Microsoft Identity Manager (MIM/FIM) workflow definitions into Atlas-executable BPMN 2.0 XML.

MIM does not emit BPMN. Its workflows are serialised as XOML — the markup of the Windows Workflow Foundation (WF) — and are normally extracted with the FIMAutomation cmdlet Export-FIMConfig, which wraps each WorkflowDefinition's XOML as an attribute inside a resource-graph XML. This package accepts either raw XOML or such a wrapper (it locates and unescapes the embedded XOML) and produces a single <definitions> document the Atlas compiler can deploy. XOML is not reliably well-formed — MIM writes a workflow root's xmlns declarations without quotes around the value — so input that does not parse is repaired once and the repair is reported.

Losslessness

The translation follows one rule: nothing is silently dropped. Where a WF construct has a faithful BPMN counterpart it is emitted natively (see the mapping in Convert); where it does not, the activity is still emitted — as a typed or plain task placeholder — and its original markup is preserved in an <atlas:mimSource> extension element together with a <documentation> note. A Report lists every produced node with a status of native, preserved or manual-review, so the lossy points are explicit rather than hidden, and its Warnings carry what belongs to no single node — today, that the input only parsed after a repair.

A preserved activity keeps its namespace binding: the fragment carries the prefixes it needs and declares them, so it parses on its own, and the <atlas:mimSource> element names the .NET type and assembly the namespace identifies — which is what tells a MIMWAL activity apart from a stock MIM one of the same local name (see namespace.go).

Flow-node ids come from the activity's x:Name where it has one, so a re-import of a workflow that gained a step does not renumber the steps it already had.

Preserved markup is written as escaped character data, never in a CDATA section: CDATA suppresses entity resolution, which would turn the quotation marks inside a MIM expression into literal &#34; and change the expression while appearing to preserve it. A consumer unescapes the element text once and holds the activity's markup as MIM wrote it.

Conditionality is part of that structure even where MIM does not express it as control flow: an activity of the MIMWAL library runs only when its ActivityExecutionCondition holds, and a ConditionedActivityGroup's child runs on the passes where its WhenCondition holds, so such an activity is wrapped in an exclusive split with a bypass; one carrying an Iteration becomes a sequential multi-instance activity, and the group itself the repeat-until loop it is. An IfElseBranchActivity usually carries its condition as a WF property element rather than an attribute, which is read as the condition it is rather than as a step in the flow. No expression is translated — see emitGuard and miPlaceholder for why — but each is documented on the model and flagged in the Report.

The serialised .NET collections a MIMWAL activity hangs off itself — the queries it runs and the assignments it makes, thousands of characters of Hashtable markup in the source — are decoded once and rendered three ways: as a small table on the activity's documentation, as <atlas:mimCollection> extension elements on the node, and as one item per row in the Report. All three are by position: the structure of such a table is mechanical, the meaning of its columns is not (see tables.go).

The decomposition is machine-readable, and stays out of the graph

A row of an UpdatesTable is a write and a row of a QueriesTable is a read, so it is tempting to import each as its own task. This package does not, and the reason is worth stating: which target system a row writes to is not in the XOML at all — it is in MIM's sync rules and attribute flows — and MIM applies the whole table as one request. Splitting the rows into flow nodes would put a structure into the diagram that the source does not contain, and would carry the untranslated per-activity guard onto every one of them, so a model that looks precise would be exactly as unexecutable as before. Nothing here decides that; the migrator does, knowing the sync rules.

What the importer can do without inventing anything is hand over the decomposition: <atlas:mimCollection property="UpdatesTable" kind="table"> with an <atlas:mimRow> per row and an <atlas:mimCell column="…"> per cell, verbatim. A cell says where it sat, never what it does. That is addressable by a tool, checkable against the preserved source, and asserts nothing that was not read out of the markup. The whole argument, including the option this rejects, is in ADR-0292.

The Report is a migration worksheet

The Report counts *work*, not BPMN elements. A MIMWAL activity carrying five assignments and a named query is one preserved node and six pieces of work, each of which has to be re-expressed against a real target system, so it contributes six items and the manual-review count says six. A worksheet that counted nodes would report "1 preserved" for a step nobody can migrate in an afternoon, which is the number a plan would then be made with.

The conversion reproduces the workflow's *structure and intent*, not MIM's runtime semantics: the authentication/authorization/action request model, the FEEL bodies of individual activities, and diagram layout are intentionally out of scope for a first pass and are flagged for manual review where relevant.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Note

type Note struct {
	NodeID   string // BPMN id of the produced element
	Activity string // XOML activity local name it came from
	Kind     string // BPMN element kind (userTask, serviceTask, exclusiveGateway, …)
	Status   Status
	Detail   string // what a reviewer should know (why preserved / what to check)
}

Note is one item of a conversion Report: a produced node, or one piece of work inside it — a decoded row of a MIMWAL collection is its own item, because it is its own read or write to re-express.

type Report

type Report struct {
	ProcessID string
	Notes     []Note
	// Warnings are document-level observations that belong to no single node —
	// that the input had to be repaired before it would parse, or that it carried
	// workflows this Result does not cover.
	Warnings []string
	// Source is the WorkflowDefinition resource the workflow came from, when the
	// input was an export rather than raw XOML. Its fields are what MIM knows
	// about a workflow and the XOML does not say.
	Source SourceInfo
}

Report is the migration worksheet of a conversion: every produced node and, inside a node, every decoded row of the MIMWAL collections it carries. The counts are therefore counts of *work*, not of BPMN elements — which is the number a migration is planned with.

func (Report) Count

func (r Report) Count(s Status) int

Count returns how many items carry the given status.

func (Report) String

func (r Report) String() string

String renders the report as a stable, human-readable summary (one line per item) suitable for stderr or a CLI log.

type Result

type Result struct {
	BPMN   []byte
	Report Report
}

Result is the output of a conversion: the BPMN document and its report.

func Convert

func Convert(r io.Reader, name string) (Result, error)

Convert reads a MIM/FIM XOML workflow (or an Export-FIMConfig wrapper that embeds one) and returns Atlas-deployable BPMN 2.0.

Control flow is mapped natively:

SequentialWorkflow / Sequence  → a chain of flow nodes
IfElseActivity (+ branches)    → an exclusive gateway split/join
ParallelActivity (+ branches)  → a parallel gateway split/join
WhileActivity                  → an exclusive-gateway loop

Leaf activities are mapped by intent:

Approval*        → userTask       (native)
Notification/Email → serviceTask  (native, type mim-notification)
FunctionEvaluator → serviceTask   (preserved, type mim-function)
PowerShell*      → serviceTask    (preserved, type mim-powershell)
*Unique*         → serviceTask    (preserved, type mim-uniquevalue)
Create/Update/Delete/Group/Resource → serviceTask (preserved, type mim-resource)
anything else    → task           (manual-review) with the XOML preserved

A leaf carrying a MIMWAL ActivityExecutionCondition is additionally wrapped in an exclusive split/merge, because MIMWAL expresses conditionality per activity rather than as control flow — see emitGuard. One carrying a MIMWAL Iteration becomes a sequential multi-instance activity — see emitMultiInstance. The serialised .NET collections a MIMWAL activity hangs off itself are rendered as a table on its documentation — see mimTables.

name, when non-empty, overrides the process name derived from the workflow.

func ConvertAll added in v0.6.0

func ConvertAll(r io.Reader, name string) ([]Result, error)

ConvertAll is Convert for an input that may carry more than one workflow: an Export-FIMConfig export holds one WorkflowDefinition per workflow, and a export of a whole MIM installation holds all of them. It returns one Result per workflow, in document order, and never an empty slice without an error.

name overrides the process name only when the input carries a single workflow. With several, each takes the name of its own WorkflowDefinition resource, because one name cannot stand for all of them.

type SourceInfo added in v0.6.0

type SourceInfo struct {
	DisplayName       string `json:"displayName,omitempty"`
	Description       string `json:"description,omitempty"`
	RequestPhase      string `json:"requestPhase,omitempty"`
	RunOnPolicyUpdate string `json:"runOnPolicyUpdate,omitempty"`
	ObjectID          string `json:"objectId,omitempty"`
}

SourceInfo is the WorkflowDefinition resource an imported workflow came from. Every field is empty when the input was raw XOML rather than an export.

type Status

type Status string

Status classifies how faithfully one produced BPMN node reflects its XOML source, so the lossy points of a conversion are explicit.

const (
	// StatusNative marks a node whose BPMN meaning matches the WF construct:
	// control-flow gateways, approvals (user task), notifications.
	StatusNative Status = "native"
	// StatusPreserved marks a node mapped to a typed task shell whose inner logic
	// (a FEEL body, a PowerShell script, a resource operation) was not translated
	// but is kept verbatim in <atlas:mimSource> for a developer to wire up.
	StatusPreserved Status = "preserved"
	// StatusManualReview marks a node that needs a human: an unrecognised activity
	// kept as a plain-task placeholder, or a branch/loop whose condition could not
	// be translated to FEEL and was replaced with a safe placeholder.
	StatusManualReview Status = "manual-review"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL