Regular Expression Support in Power Automate, via Custom Code Connector

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.

Custom Connector GUI wizard

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.

Regular Expression Action Output

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.

Author: Joey

I develop modern solutions leveraging the Microsoft cloud technologies: Power Platform and SharePoint Online, with a focus on process automation. I got my start writing code professionally beginning with Microsoft VB6, but really fell in love with code (and the art of it) with Microsoft .NET Framework version 1.0. I'm also a fan of Superman, so don't be surprised if references creep in. Up, Up, and Away!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: