Runbooks/Custom Workflow Steps

Custom Workflow Steps

Mesh workflows ship with steps for group membership, attributes, email and webhooks, plus a Run PowerShell script step for the common “run my script” case. That last step is delivered as a step package, the same kind of package you build here. When none of those fit, you build your own step with the Step SDK: a .NET class library that you sign and drop into a folder on the Admin API host. The console then shows your step in the designer palette, draws its form, stores its secrets and records its outputs exactly as it does for a shipped step.

This runbook takes you from an empty project to a custom step running in a workflow. Read it once end to end before starting, because the trust rules in Step 4 reject any package that is unsigned or from an unlisted publisher. The reason is shown in Settings → Workflows and in the event log.

If a script is all you need, you do not need the SDK. Use the Run PowerShell script step described in the Mesh workflows runbook, under Run a PowerShell script.

Before you start

How a step works

A step is one class. It tells IdentityMesh four things:

WhatHow
Its type keyA Type property such as notifyChat. It starts with a lowercase letter, then letters and digits, 2 to 40 characters in all. It must be unique across every installed step, including the shipped ones.
Its formA description of the step: a label, a description, a category and icon, the parameters the designer draws, and the outputs the step publishes.
How to check its settingsAn optional method that returns errors when a workflow author saves a step with settings that cannot work.
What it doesOne method that receives the run’s context and the step’s settings, does the work and returns a short summary and its outputs.

The designer builds the form from the description, so there is no console work. The parameter kinds are text, multiline text, number, toggle, choice, URL, secret, group, attribute name, attribute assignments, expression, template, recipients, attribute list and script.

The step runs inside the Admin API service, as the service account. It reaches IdentityMesh only through the context it is given: the run and object identity, the object’s attributes, secrets, the rules-language evaluator, the template renderer and a logger. There is no database access.

Step 1: Create the project

Start from the sample solution. It is the Run PowerShell script step under its own name, complete with its tests, small enough to read in one sitting.

  1. Build and test the kit as downloaded first, so you know the toolchain works before you change anything:

    dotnet build
    dotnet test
  2. Copy the kit folder and rename the sample project. Keep nuget.config and the packages folder beside your solution. They restore the SDK package:

    <PackageReference Include="IdentityMesh.Workflow.Sdk" Version="1.0.0" />
  3. Set the assembly name so it matches the pattern the Admin API scans for:

    <AssemblyName>IdentityMesh.Steps.YourStep</AssemblyName>

    The Admin API loads only files named IdentityMesh.Steps.*.dll. A DLL with any other name is never looked at, and the step simply never appears.

  4. Pick your own type key. The sample ships with the type key powershellSample and the assembly name IdentityMesh.Steps.Sample so that deploying it untouched never collides with the installed powershell step package. Replace both with names of your own.

  5. Do not change the package’s build settings. The package keeps IdentityMesh.Workflow.Sdk.dll out of your build output on purpose. The reason is in Step 4.

Step 2: Implement the step

Derive from WorkflowStepBase and implement the type key, the description and the execute method. Override Validate to check settings when a workflow author saves.

using System.Text.Json;
using IdentityMesh.Workflow.Sdk;

public sealed class NotifyChatStep : WorkflowStepBase
{
    public override string Type => "notifyChat";

    public override StepDescriptor Describe() => new(
        Type, "Post to chat", "Posts a message to a chat room.", "Integrate", "webhook",
        [
            new StepParameterDeclaration("room", "Room", StepParameterKind.Text, Required: true,
                Help: "The room name, for example it-alerts."),
            new StepParameterDeclaration("message", "Message", StepParameterKind.Template, Required: true),
            new StepParameterDeclaration("token", "API token", StepParameterKind.Secret, Required: true),
        ],
        [
            new StepOutputDeclaration("messageId", "Message id", "text"),
        ]);

    public override IReadOnlyList<StepValidationError> Validate(JsonElement config, IStepValidationContext ctx)
    {
        var cfg = Config<ChatConfig>(config);
        return cfg.Room.StartsWith('#')
            ? [new StepValidationError("room", "Enter the room name without the leading #.")]
            : [];
    }

    public override async Task<StepResult> ExecuteAsync(IStepExecutionContext ctx, JsonElement config, CancellationToken ct)
    {
        var cfg = Config<ChatConfig>(config);
        var token = await ctx.GetSecretAsync("token", ct)
            ?? throw Fail("The API token has not been set on this step.");

        var text = ctx.RenderTemplate(cfg.Message);      // {{displayName}}, {{steps.x.y}}
        var id = await ChatClient.PostAsync(cfg.Room, text, token, ct);   // your own code

        return new StepResult($"Posted to {cfg.Room}.", new Dictionary<string, string?> { ["messageId"] = id });
    }
}

Four helpers on the base class and context cover most steps:

Rules that are easy to get wrong:

IdentityMesh checks the declaration before it calls your Validate: required parameters (when visible), number ranges, choice options and patterns. A secret may not appear in the settings JSON, and unknown keys are rejected. You only need Validate for rules the declaration cannot express.

Step 3: Test it

The sample’s test project shows the pattern: hand-written stubs for the context, no service and no mocking library. Test the described form, each validation error, a successful run, and both failure kinds.

dotnet build
dotnet test

The output you deploy is the one DLL in your bin folder named IdentityMesh.Steps.YourStep.dll, plus any third-party libraries it needs. There must be no IdentityMesh.Workflow.Sdk.dll beside it. If there is, something changed the package’s build settings.

Step 4: Deploy

IdentityMesh loads a step package only if its publisher is on the Admin API’s trusted list. Sign first, trust second, copy third.

The Run PowerShell script step is a worked example of a deployed package. It was deployed this way: it sits in the Steps folder, it is signed, and it appears in the Step packages table. Look at its row in Settings → Workflows before you deploy your own, so you know what a healthy package looks like.

  1. Sign the DLL with your code-signing certificate.

  2. Open the Admin API’s configuration file:

    C:\Program Files\IdentityMesh\api\appsettings.json

    and add your certificate to the trusted publishers under Workflows:StepVerification. The common name is the certificate’s subject CN and the thumbprint is its SHA-1 thumbprint:

    "Workflows": {
      "StepVerification": {
        "RequireRevocationCheck": true,
        "AllowedPublishers": [
          { "SubjectCommonName": "Contoso Ltd", "Thumbprint": "<your certificate thumbprint>" }
        ]
      }
    }

    Keep any entry that is already there. It is the publisher of the PowerShell step package and the other shipped components. If the section already holds other settings, add the entry beside them.

  3. Copy your DLL, and only the third-party libraries it needs, into the steps folder:

    C:\Program Files\IdentityMesh\api\Steps\

    Copy individual files. Do not mirror your whole bin folder, which is how the SDK DLL ends up there.

  4. Restart the Admin API. Step packages are read once at start-up.

    sc stop IdentityMeshAdmin
    sc start IdentityMeshAdmin
  5. Confirm it loaded. In the console open Settings → Workflows. The Step packages table lists every DLL in the folder with its version, publisher and a status of Loaded or Rejected. A rejected package shows the reason under the status. The same reason is written as an error to the IdentityMesh event log (Event Viewer, Applications and Services Logs → IdentityMesh, source IdentityMesh Admin API). Open a workflow in the designer: a loaded step appears in the Custom section of the palette, with the package name and version under it.

Never put IdentityMesh.Workflow.Sdk.dll in the Steps folder. The SDK is the contract between your step and the Admin API, and the Admin API already has its own copy. A second copy beside your step is loaded separately, your step’s interface becomes a different type from the Admin API’s, and the package is rejected as having loaded the contract twice. If a package you deployed is rejected for that reason, delete the extra file and restart.

Development environments

On a development or test machine you can skip the signature check, so you can try an unsigned build without a certificate. It works only when both of these are true:

If either one is missing, packages are verified as usual. The setting is honoured only in the Development environment.

Warning: never use this in production. With the check skipped, the Admin API runs any DLL placed in the Steps folder, signed or not, with the service account’s rights. Production hosts must keep the service in its normal environment and sign every package.

Use the step in a workflow

  1. Open Workflows and create or edit a workflow.
  2. Drag your step from the Custom section of the palette onto the canvas. The properties panel shows the form you described.
  3. Fill in the fields. Secret fields are stored when you save and shown only as a mask with a Change button. Change the timeout if the default of 300 seconds does not suit.
  4. Use Validate to run your checks, then Save.
  5. Use Run now against a test object. The run detail page lists the step’s outputs.

Upgrades

The IdentityMesh installer does not remove files it did not install, so your step stays in the Steps folder across upgrades. Two things can still require action:

Replacement DLLs go in the same way: stop the service, copy the file over the old one, start the service.

Troubleshooting

A rejected package shows its reason in the Step packages table and in the event log. Fix the cause, then restart IdentityMeshAdmin.

SymptomCauseFix
Reason: “The package is not signed.”The DLL has no Authenticode signatureSign it.
Reason: “The package publisher is not trusted.” A sentence follows naming the signerThe certificate is not in AllowedPublishersAdd its subject CN and SHA-1 thumbprint under Workflows:StepVerification and restart.
Reason: “The revocation status of the package’s signing certificate could not be checked.”The host cannot reach the certificate authority’s revocation serviceAllow outbound access to the CA’s CRL or OCSP endpoint.
Reason: “The package signature is not valid.” A detail follows, such as a WinVerifyTrust codeThe DLL was changed after it was signed, or the certificate chain is not trustedSign the final file again, and check the chain on the host.
Reason: “The package signature could not be verified on this host.” A detail followsThe signature check itself failed to runRead the detail. Check the host’s certificate services and try again after a restart.
Reason: “No trusted step publishers are configured.” on every packageNo trusted publisher is configuredAdd your publisher under Workflows:StepVerification and restart. Also add or restore the IdentityMesh publisher entry beside your own. It is absent on this error, and the PowerShell package needs it.
Reason: “Step type ‘k’ is already provided by a built-in step.” or ”… by the package ‘f’.”Two packages, or a package and a built-in step, declare the same type keyGive your step its own type key and rebuild. A key already used by another step or package, such as powershell, is always refused.
Reason: “The package was built against workflow SDK x, which is newer than this IdentityMesh’s SDK y.”The step uses an SDK version newer than the Admin API’sUpdate IdentityMesh, or rebuild the package against SDK y.
Reason: “No workflow step was found in the package.” Concrete public types found are listedThe DLL contains no public class implementing the step contractCheck the class is public and not abstract.
Reason names a class and says “The step has no public parameterless constructor.”The step class cannot be created by IdentityMeshGive the class a public constructor with no parameters.
Reason names a class and says the step’s type key is not valid, or that it “declares the parameter ‘timeoutSeconds’, which IdentityMesh reserves.”The descriptor breaks a ruleUse a type key that starts with a lowercase letter and has 2 to 40 letters and digits, and remove the timeoutSeconds parameter. IdentityMesh adds the timeout itself.
Reason says the contract assembly is loaded twiceIdentityMesh.Workflow.Sdk.dll is beside the stepDelete it from the Steps folder, check the project’s package reference was not altered, and restart.
The DLL is not in the table at all, and there is no errorThe file name does not start with IdentityMesh.Steps.Set <AssemblyName> and rebuild.
The step is not in the palette after a copyThe service was not restartedRestart IdentityMeshAdmin. Packages are not reloaded while it runs.
Saving a workflow reports a message from your stepYour Validate returned an errorFix the setting the message names.
A run fails with the step’s message and no retryThe step threw FailRead the message. It is the one your code wrote.
A run keeps retrying, then fails after the fifth retryThe step times out, or throws Retry or an unexpected exceptionCheck the timeout setting and the event log.