When playing around with a new custom connection I was creating in Power Automate, I came across the Code (Preview) tab. The developer part of my brain thought “Oh, that’s neat, I can now include code, I like writing code. But I’m not sure why I would need or use that.” And that was that.
That was, until, I came across this Twitter post by John Liu. In it he mentions two other custom code connector solutions by Alex Shlega and Hiro which sparked him to create a parse CSV connector. I recommend checking out those other two as well. One thing I always appreciate about John (hopefully he’s ok with my referring to him as if we know each other) is that his posts and ideas always spark those “ooh, I wonder if …” thoughts which my brain latches onto.
This is when it clicked.
Code in custom connectors can be used 1) for simplifying complex Power Automate flow logic by (in some cases) replacing the need for multiple actions, and 2) to fill in missing Power Automate functionality. So this got me thinking, what expressions/actions are missing that I am need of? After one last pass through the Microsoft documentation and examples, their regular expression example grabbed my attention. Ooh yeah, I’ve needed that, let’s built that!
Building the Regular Expression action using a custom code connector.
Here’s a look at what we’re ultimately going to build. This action will allow us to supply the text to match, a regular expression pattern, some basic options, and receive the matches.

And here’s what we need to do it. The two main components of custom code connectors are: the action definition (what it can do) and the code (how it will do it).
Action Definition
The action can be defined using the custom connector GUI wizard or using the YAML swagger definition editor. If this is your first time building a connector, while a little intimidating, I would recommend that you use the GUI so that you become familiar with all of the components of the action definition.

But for simplicty of this post, I’ve included the swagger definition below. Simply use the “Swagger Editor” toggle to display the YAML editor and paste in the swagger definition.
To switch back to the wizard under the Defintion tab, use the “Swagger Editor” toggle again.
swagger: '2.0'
info: {title: SteelCut Bytes Actions, description: SteelCut Bytes Power Automate Actions,
version: '1.0'}
host: flow.steelcutbytes.com
basePath: /
schemes: [https]
consumes: []
produces: []
paths:
/RegularExpression:
post:
responses:
default:
description: default
schema:
type: object
properties:
input: {type: string, description: The supplied string to search for
a match., title: Text to Match}
pattern: {type: string, description: The supplied .NET regular expression
pattern to match., title: Regular Expression}
matches:
type: array
items:
type: object
properties:
Groups: {type: object}
Success: {type: boolean}
Name: {type: string}
Captures:
type: array
items: {type: object}
Index: {type: integer}
Length: {type: integer}
Value: {type: string}
description: An array of the Match objects found by the search. If
no matches are found, the method returns an empty array.
title: Matches
isMatch: {type: boolean, description: Indicates whether the .NET regular
expression finds a match in the input string., title: IsMatch}
summary: Regular Expression
description: Searches the specified input string for all occurrences of a specified
.NET regular expression, using the specified matching options.
operationId: RegularExpression
parameters:
- name: value
in: body
required: true
schema:
type: object
properties:
input: {type: string, description: The string to search for a match, title: Text
to Match}
pattern:
type: string
description: The .NET regular expression pattern to match
title: Regular Expression
options:
title: Regex Options
description: Value that specifies options for matching
type: string
enum: [None, IgnoreCase, Singleline, Multiline]
default: IgnoreCase
required: [options, pattern, input]
definitions: {}
parameters: {}
responses: {}
securityDefinitions: {}
security: []
tags: []
Code
The code creates a .NET Regular Expression, matching the text against the pattern, returning the matches.
Simply paste the code below under the Code (Preview) tab in the connector GUI.
public class Script : ScriptBase
{
public override async Task<HttpResponseMessage> ExecuteAsync()
{
switch (this.Context.OperationId)
{
case "RegularExpression":
return await this.HandleRegexOperation().ConfigureAwait(false);
}
var response = new HttpResponseMessage(HttpStatusCode.BadRequest);
response.Content = CreateJsonContent($"Unknown operation ID '{this.Context.OperationId}'");
return response;
}
private async Task<HttpResponseMessage> HandleRegexOperation()
{
var contentAsString = await this.Context.Request.Content.ReadAsStringAsync().ConfigureAwait(false);
var contentAsJson = JObject.Parse(contentAsString);
var textToMatch = (string)contentAsJson["input"];
var regexInput = (string)contentAsJson["pattern"];
var regexOptions = (string)contentAsJson["options"];
var regex = new Regex(regexInput,
(regexOptions == "IgnoreCase") ? (RegexOptions.IgnoreCase | RegexOptions.Compiled) :
(regexOptions == "Singleline") ? (RegexOptions.Singleline | RegexOptions.Compiled) :
(regexOptions == "Multiline") ? (RegexOptions.Multiline | RegexOptions.Compiled) :
(RegexOptions.None | RegexOptions.Compiled),
TimeSpan.FromSeconds(1)
);
var jSerializer = JsonSerializer.Create(new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
});
var result = new JObject
{
["input"] = textToMatch,
["pattern"] = regex.ToString(),
["isMatch"] = regex.IsMatch(textToMatch),
["matches"] = JArray.FromObject(regex.Matches(textToMatch), jSerializer),
};
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = CreateJsonContent(result.ToString());
return response;
}
}
Caveats: code must be C#, finish in 5 secs or less*, and be no larger than 1MB.
*In my tests, I’ve found that if the code takes longer than 5 seconds to run, the action and flow will still run. But for best performance, you should still stick to this requirement by keeping your code as lean and optimized as possible. I’m not sure, but I feel like at some point Microsoft could prevent your action and flow from running successfully if it violates this time threshold. Just keep this in mind.
Output
Here’s the output of the Regular Expression custom code connection action.

As you can see from the output, it returns the array of Matches, along with the IsMatch true/false value. This allows the Matches output to be used in an expression like this:
@first(outputs('Regular_Expression')?['body/matches'])?['Groups/token/Value']
Now you can use Regular Expressions in your flows. How about that!
Currently, there are no official out-of-the-box actions or expressions in Power Automate that provide support for regular expressions, although it’s been requested. And recently, I found out that you can also do this via Excel Office scripts, but I’ve never been truly comfortable with Excel, a side effect of having to write VBA code early on in my career. (I know, I know, the Office Script code is actually Javascript which is better, but still …)
What other creative ways can we leverage custom code connectors? I’ll leave that as an exercise for the reader … and future blog posts.
Happy coding!
Follow My Blog
Get new content delivered directly to your inbox.
I’ve some concerns about the “host” value (“flow.steelcutbytes.com”) in the “General information” tab when setting up the connector.
Does that mean that every time the connector is used, there is an API call to your domain?
What happens to the data? And what if the domains gets offline ?
LikeLike
The host value really does nothing. You can change it to anything you want. Normally with a connector you are hitting some web API and need the host to know what to talk to. With the custom code connector, it simply runs the code you put in the connector. The host is simply a part of the connector definition. Hopefully that helps. 🙂
LikeLiked by 1 person
Can you clarify the usage of the “host: flow.steelcutbytes.com” line in the swagger definition please ?
LikeLike
The host value really does nothing. You can change it to anything you want. Normally with a connector you are hitting some web API and need the host to know what to talk to. With the custom code connector, it simply runs the code you put in the connector. The host is simply a part of the connector definition. Hopefully that helps. 🙂
LikeLiked by 1 person
What happened to my previous comments (were “awaiting moderation” and now gone
LikeLike
Not sure what happened. I think I fixed it.
LikeLike
Sorry for multiposting, pending commends reappeared after multiple F5’s and login out and back again. You may delete extra post at your convenience
LikeLike
Another question.
When testing with input = “aaaBBB” and pattern = “(a*)(B*)(C*)” the “matches” array of the returned body contains *two* elements.
I don’t understand the purpose of the second
Here’s the return JSON : https://pastebin.com/fiNCHczf
LikeLike
Amazing. just what i needed. Going to implement and see if i get this working
LikeLike