
Skill
workflow-json-generation-rules
generate workflow.json files for Logic Apps
Description
Rules for generating workflow.json files for Logic Apps Standard. Covers connector selection, splitOn preference, file trigger semantics, runAfter structure, and mandatory reference lookup before writing.
SKILL.md
Skill: Workflow JSON Generation Rules
Purpose: Authoritative rules for generating
workflow.jsonfiles. Follow exactly.
1. Mandatory Reference Lookup
BEFORE writing ANY workflow.json:
- Read the skill
source-to-logic-apps-mappingto get the Logic Apps equivalent connector, service provider ID, and operation names for each source component. - Use those operation names to call
migration_searchReferenceWorkflowsandmigration_readReferenceWorkflowto get exactoperationIdandserviceProviderConfigurationfor each trigger/action. - If the first search returns no relevant results, RETRY 2-4 more times with different word combinations.
- Also call
migration_readReferenceDocwithaction="search"thenaction="read"to verify connector capabilities. - Do NOT invent
operationIdvalues,serviceProviderConfigurationstructures, or connection formats — ALWAYS copy from references.
2. Workflow Definition Structure
Each workflow.json must contain a definition key with:
$schema— MUST be exactly"https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#"contentVersion— use"1.0.0.0"triggers— at least one triggeractions— withrunAfter(object mapping predecessor action names to status arrays) andtypefor each action
Action Types
Switch— must havecasesIf/Condition— must haveactions(true branch) and optionallyelse.actions(false branch)Foreach/Until— nestactionsinsideServiceProvider— withserviceProviderConfigurationApiConnection— only when no built-in ServiceProvider equivalent existsInvokeFunction— for .NET local functionsWorkflow/InvokeWorkflow— for calling child workflows
Preference: ServiceProvider over ApiConnection
Prefer ServiceProvider type with serviceProviderConfiguration over ApiConnection (managed connector) whenever a built-in equivalent exists. Built-in connectors run in-process with lower latency and no connection overhead.
Parameterization (keep it simple)
- For Logic Apps Standard, prefer
parameters.jsonfor cross-environment values. - In
workflow.json, reference values with@parameters('name'). - In
parameters.json,@appsetting('name')is the only valid expression type. - In
connections.json, only@parameters(...)and@appsetting(...)are valid. - For more details, fetch Microsoft Learn docs about Standard parameters and app settings (
create-parameters-workflows,edit-app-settings-host-settings).
3. SplitOn over ForEach
When a trigger returns an array (batch of messages), ALWAYS use splitOn on the trigger instead of wrapping actions in a For_each loop:
"triggers": {
"myTrigger": {
"splitOn": "@triggerOutputs()?['body']",
...
}
}
SplitOn debatches the array so each item fires a separate workflow run. Only use For_each if splitOn is not supported by that trigger type.
4. File System Trigger Semantics
- Do NOT add delete/remove/cleanup actions that remove the trigger input file by default.
- The runtime does not re-trigger on the same unchanged file, so deleting is unnecessary unless the user explicitly requests archival/deletion behavior.
5. Trigger Output Verification (MANDATORY)
⚠️ NEVER assume a trigger returns file/message content directly. ALWAYS verify what the trigger actually returns.
Many triggers return metadata (file path, message ID, blob URI) rather than the actual content. You MUST check the trigger's return type from the reference workflow before building downstream actions.
5.1 Procedure
- After selecting a trigger via reference lookup (§1), read the reference workflow AND the reference docs to see what
triggerOutputs()ortriggerBody()actually contains. - Call
migration_readReferenceDocwithaction="search"for the trigger's connector name to find documentation on its output schema and return type. - Call
migration_readReferenceDocwithaction="read"on the top result to confirm the exact trigger output fields. - If the trigger returns a file path (e.g. File System
whenFilesAreAddedreturnspathandname), you MUST add a separate read content action (e.g.getFileContent) before any parsing/processing. - If the trigger returns message content directly (e.g. HTTP Request trigger, Service Bus
receiveQueueMessage), you can usetriggerBody()directly.
5.2 Common Trigger Return Types
| Trigger | Returns | Content Available Via |
|---|---|---|
File System whenFilesAreAdded | File metadata (path, name, size) | Add getFileContent action with the path |
File System whenFilesAreAddedOrModified | File metadata (path, name, size) | Add getFileContent action with the path |
Azure Blob whenABlobIsAddedOrModified | Blob metadata (path, URI) | Add readBlob action with the path |
| HTTP Request | Full request body | triggerBody() directly |
Service Bus receiveQueueMessage | Message content + properties | triggerBody()?['contentData'] directly |
Service Bus peekLockQueueMessagesV2 | Array of message metadata | Add completeMessage after processing |
FTP/SFTP whenFileIsAdded | File metadata | Add getFileContent action with the path |
| Recurrence / Timer | No body | N/A — use actions to fetch data |
5.3 Rule
- If trigger returns metadata/path: Add a content-reading action BEFORE any XmlParse, Parse JSON, Compose, or processing action.
- If trigger returns content directly: Use
triggerBody()ortriggerOutputs()?['body']in downstream actions. - If unsure: Check BOTH the reference workflow AND the reference docs. If the reference workflow has a read-content action after the trigger, or the docs show the trigger returns metadata rather than content, you need a content-reading action too.
6. 1:1 Flow to Workflow Rule
- Every flow (including sub-processes with significant logic) MUST be a separate workflow.
- Use the
Workflowaction type to invoke child workflows from parent workflows. - Do NOT convert flows to local functions.
7. Plan Adherence
When fixing any errors in workflow.json:
- Do NOT deviate from the planned design.
- Do NOT add/remove actions, change triggers, switch connectors, or alter schemas.
- Before any fix, re-read the planning results to verify compliance.
- If a fix requires changing the design, STOP and report to the user.
8. Scenario-Specific Action Overrides
These overrides are deterministic — apply them directly without asking the user.
8.1 XML Field Extraction
When extracting fields from an XML message body:
- FIRST: Use
XmlParsebuilt-in action with the schema fromArtifacts/Schemas/. This validates and extracts XML fields in one step. Access fields via:body('Parse_XML')?['Root']?['fieldName'] - ONLY IF no schema exists and cannot be generated: fall back to
xpath()expressions.
Do NOT use xpath() when an XSD schema is available — XmlParse is a level-1 built-in action and MUST be preferred over level-2 expressions per the Component Priority Ladder.
8.2 XML Schema Validation
When validating incoming XML against a schema:
- Use
XmlValidationbuilt-in action — do NOT write custom validation logic or skip validation.
8.3 XML Transformation
When transforming XML documents:
- Use
Xslt(Transform XML) built-in action with maps fromArtifacts/Maps/— do NOT use Compose + string manipulation.
8.4 Output XML Construction
When constructing XML output documents:
- FIRST: Use
XmlComposebuilt-in action (XML Operations) to compose XML from structured data. This assembles XML output from structured fields. XmlComposecan reference schemas from either the localArtifacts/Schemas/folder or an Integration Account — select the source via theSourceparameter (LogicApporIntegrationAccount). If the flow uses the Integration Account model, useIntegrationAccountas the source.- If the source uses .NET code to build XML (e.g.
XmlDocument,XElement) with complex business logic, use a .NET local function. - If the output is a simple static template with field substitution,
Composeis acceptable. - Do NOT approximate XML construction with
Compose+concat()whenXmlComposeor a local function is more appropriate.
8.5 EDI Decode Output Handling
⚠️ CRITICAL: The EDIFACT
EdifactDecodeand X12X12Decodebuilt-in actions return JSON, not XML. If a downstream action or function expects XML (e.g.XmlDocument.LoadXml()), you MUST add anXmlComposeaction after the decode action to convert the JSON back to XML using the appropriate message schema.
Pattern:
Decode_EDIFACT (EdifactDecode) → JSON output
→ Compose_EDIFACT_XML (XmlCompose, Source: IntegrationAccount, Schema: message schema) → XML output
→ downstream action expecting XML
Do NOT pass the JSON decode output directly to XML-expecting code. Do NOT try to parse the JSON as XML — it will fail with "Data at the root level is invalid".
8.6 JSON Parsing
When parsing JSON payloads:
- Use
Parse JSONbuilt-in action — do NOT usejson()expression for structured access.
9. Pre-Finalize Validation Checklist
Before storing workflow definitions, cross-check EVERY action against this table. If ANY row in the "DON'T" column matches your output, fix it before proceeding.
| Scenario | DO (correct) | DON'T (wrong — fix before storing) |
|---|---|---|
| Trigger output assumption | Verify trigger return type from ref | Assume trigger returns file/message content directly |
| File/Blob/FTP trigger | Add getFileContent/readBlob action after trigger | Use triggerBody() for content (it only has metadata) |
| XML field extraction | XmlParse action + schema | xpath() expression when schema exists |
| XML validation | XmlValidation action | Skip validation or custom code |
| XML transformation | Xslt action + map file | Compose + string concat |
| XML output assembly | XmlCompose action (Source: LogicApp or IntegrationAccount) | Compose + concat() for XML |
| EDI decode → XML needed | Add XmlCompose after EdifactDecode/X12Decode | Pass JSON decode output directly to XML-expecting code |
| Complex XML with .NET logic | .NET local function | Compose + concat() approximation |
| JSON parsing | Parse JSON action | json() expression for structured access |
| Array trigger debatching | splitOn on trigger | For_each loop wrapping all actions |
| File trigger cleanup | Do nothing (no delete) | Delete/archive trigger input file |
| sub-process (via process-call) | Separate workflow + Workflow action | Merge into parent or local function |
| Custom source code | .NET local function | Expressions or inline approximation |
More skills from the logicapps-migration-agent repository
View all 12 skillsanalyse-source-design
analyze TIBCO flow architecture
Jul 12ArchitectureAzureCode AnalysisDiagramscloud-deployment-and-testing
deploy and test Azure Logic Apps
Jul 12AzureBicepDeploymentReporting +1connections-json-generation-rules
generate connections.json files for Logic Apps
Jul 12AutomationAzureConfigurationWorkflow Automationconversion-task-plan-rules
generate ordered conversion task plans
Jul 12AzureMigrationOperationsPlanningdependency-and-decompilation-analysis
analyze MuleSoft dependencies and decompiled Java code
Jul 12AzureCode AnalysisJavaMaven +1detect-logical-groups
group MuleSoft integration artifacts
Jul 12ArchitectureAzureMigration
More from Azure (Microsoft)
View publisherazure-arg-external-evaluation-policy-author
author and test Azure Resource Graph policies
azure-policy
Jul 12AzureComplianceGovernancePolicyazure-blueprints-migration
migrate Azure Blueprints to Template Specs
azure-blueprints
Jul 12AzureDeploymentInfrastructure as CodeMigrationapiview-feedback-resolution
resolve APIView feedback on Azure SDKs
azure-sdk-tools
Jul 12API DevelopmentAzureCode ReviewDocumentationazsdk-common-live-and-recorded-tests
deploy resources and run Azure SDK tests
azure-sdk-tools
Jul 12AzureDeploymentSDKTestingazsdk-common-prepare-release-plan
manage Azure SDK release plan work items
azure-sdk-tools
Jul 12AzureGitHubProject ManagementSDKazsdk-common-sdk-release
release Azure SDK packages
azure-sdk-tools
Jul 12AzureCI/CDDeploymentSDK