Creating a Lead for every ClickDimensions Posted Form using Power Automate

Recently, my good friend Megan Walker did a guest post for ClickDimensions showing how to create a Lead for every ClickDimensions Posted Form and if you haven’t read it then you really should!

When I was reading this post one of the things I noticed was the requirement to use a Filter Array step and Compose step for each question in your Form in order to be able to use them when you created the Lead. This approach works perfectly, but it could be quite time consuming to create if your Form has lots of questions, so I wondered if there could be a way to simplify it a little bit.

Please note, for the purposes of this post I’m only going to be focusing on an alternative way to get the Posted Field data and use it to create your Lead, Megan has covered everything else in her post so please read it!

If you’d prefer to watch a video overview of this blog, click here.

The Solution

The first step is still to List your Posted Fields using the List Records action. If you’re using the Common Data Service (Current Environment) connector then you’ll be able to select the specific attributes you want returned. In this case we’re only interested in the Label (i.e. the Question) and the Value (i.e. the Answer). You should also set the Order By to the Label; this will ensure consistency in the returned results which is important for later steps in the Flow.

If you look at the Output for the List Records action, you’ll see that it gives a JSON array that looks something like:

We want to make matching pairs for the Questions (Labels) and Answers (Values) from each of the returned Posted Fields and combine them in an array. Fortunately for us, in Power Automate there is an Action called Select that allows you to “Select the specified properties from all elements of the ‘From’ array into a new array”. This is really just a fancy way of saying you can take the elements of the output above that you like and keep them, while disregarding the rest, and you can reshape them into pairs as required.

For the Select action, we take the data From the output of the List Records step, and then we create a map of the Label and the Value as a name pair

When this is done, the output from the Select action will look something like this:

This is much better!

As you can see, the Questions and Answers have been combined into array elements. You can also see that, as we set the Order By to the Label in the List Records Step above, the array elements are listed alphabetically. One important thing to note at this stage is that each element in an Array has an index number beginning from 0, so you can identify the array elements I’ve returned above as follows

The final step for creating the Lead is to add a Create a New Record action, and then we’re going to use an expression to pull the values from the output above into the fields we need to populate:

As you can see, for each field we are populating we have an expression with the following format:

@{outputs('Select')['body']?[0]?['Company Name']}

The key things to note in the construction of this expression are:

  1. for the Outputs expression, the name ‘Select’ must match the name that you’ve given to the Select step
  2. the Integer number [0] must match the element number for the array element as indicated above. For example, if we want to set the Telephone Number we will use [4]
  3. The final part of the expression [‘Company Name’] must match the text of the Question from the array.

If we wanted to get the Email Address instead of the Company Name then the expression would be:

@{outputs('Select')['body']?[5]?['Work Email Address']}

After we’ve completed all of the above our flow now only has three actions to retrieve Posted Fields and to create a Lead with the answers submitted:


Conclusions

The first thing to say is that none of this would have been possible without the valuable insights of Megan Walker and Rob Dawson. I keep saying this, but one of my favourite things about this community is the collaborative efforts we all make to help each other.

The second thing is that while this works, if the Questions on your form were to change, either by adding/removing questions or by changing the Label of the questions then this would probably affect the success of the Flow, so it might be worth putting some additional validation logic in to catch any potential issues like that.

The final thing is that Power Automate is so powerful and it’s amazing what you can achieve with some lateral thinking. This has been a fun little challenge for me, and hopefully you find it useful. Please reach out to me if you have any questions or comments.

Using Flic Buttons with CDS – Dynamically setting Option Set fields

In my previous post I demonstrated how to use a Flic button with CDS to create a Fire Evacuation record and notify Users that an office is being evacuated. The eagle-eyed amongst you may have noticed that the Office option set field value was hard-coded, meaning no matter where you were when you triggered the Flic button it would create an evacuation record against the specifically named office, so you’d have to have a Flic button for each office in your organisation, which might be a challenge if you regularly visit multiple offices in your organisation.

In this blog I’m going to show you how we can use the location data from the Flic buttons and dynamically set the option set field for the office location.

The Scenario

In this scenario, my organisation has 4 offices spread throughout the UK:

  • Glasgow
  • Edinburgh
  • Birmingham
  • London

I may be required to visit any of these offices throughout the course of my working week, and to reduce confusion for me I’d like to ensure I only have to carry one Flic button that I can use to trigger the process to record an Evacuation and to notify others within my organisation, and have the Flow recognise which Office I am in and ensure the record is populated accordingly. Furthermore, if I am too far from any of the offices when I click the button, I’d like the Flow to notify me of this if I accidentally try to trigger the process to record the evacuations.

The Setup

For this flow we’ll be using the same Evacuation entity I configured in the previous blog. I’m also going to be using a Bing Maps – Get Route action, so you need to ensure you’ve signed up for a Bing Maps API key. The final preparation step is to get the Office Option Set values (as highlighted below) for use in the Flow.

The Solution

This Flow is a bit longer than the previous one, so I’ve included an image below of the whole flow, and I’ll go through each step in turn to explain what I’m using them for.

1. Trigger – Same as in the previous blog, this is triggered using a “Click” event from a Flic button

2. Compose – as per the previous blog, this is an expression to take the DateTime value from the Click Time value and format it to make it more “friendly”, using the expression below

@{formatDateTime(triggerBody()?['clicked_at'], 'dd MMMM yyyy')}

3. Initialize Offices Array – this is just an Array variable we’re going to use later in the Flow

4. Compose Office Info – In this Compose action I’m inputting an array that includes the Office location, the Office address, and the Option Set Value that corresponds to the office

5. Parse Office Info – I’m using a Parse JSON action to parse the array I created in the previous step, using the following schema

{
    "type": "array",
    "items": {
        "type": "object",
        "properties": {
            "Office": {
                "type": "string"
            },
            "Address": {
                "type": "string"
            },
            "OptionSetValue": {
                "type": "integer"
            }
        },
        "required": [
            "Office",
            "Address",
            "OptionSetValue"
        ]
    }
}

6. For Each Office loop – For each office from the array we created in Step 4, we carry out the following actions

6A. Get Distance to Office – For this action we’re using the Bing Maps Get Route action to calculate the distance from where we were when we clicked the Flic button (using the Latitude and Longitude that are included as parameters from the Flic trigger) to the Office (using the address we put into the array in step 4)

6B. Compose Office Distance – for this step we’re going to take the Distance output from Step 6A, and the Office name and Option Set value that we parsed from the array we created in Step 4 and put them into a new array element

6C. Append to Offices Array – we take the output from the Compose Office Distance step above and append it to the Offices Array we created in Step3

7. Initialize SuccessfulActions – we initialize a new integer variable called SuccessfulActions that we’ll use in Step 9 and Step 10 below, ensuring the default value is set to 0

8. Parse Offices – next we use another Parse JSON action to parse the Offices Array variable that we’ve been populating in step 6, using the schema below:

{
    "type": "array",
    "items": {
        "type": "object",
        "properties": {
            "Office": {
                "type": "string"
            },
            "Distance": {
                "type": "number"
            },
            "OptionSetValue": {
                "type": "integer"
            }
        },
        "required": [
            "Office",
            "OptionSetValue",
            "Distance"
        ]
    }
}

9. For Each Office Distance loop – in this loop we’re going to be checking if the Flic has been triggered within a set distance of any of the offices, and if so we’ll be creating a new evacuation record

9A. Check if the Flic has been triggered close to the Office – we use a Condition action to check if the Distance from the Office is less than a set amount. In my example I’ve set it to check that the distance is less than 1 mile, but you can set accordingly

9B. Create an Evacuation Record – if the condition above is satisfied then we use a CDS Create Record action to create a new Evacuation record, and we set the Title using the Office value from the Offices Array and the formatted DateTime from Step 2. We then set the Office Option Set using the OptionSetValue from the array, and set the Start Time using the Click Time from the Flic Trigger

9B. Increment Successful Actions – We then increment the SuccessfulActions variable we initialised in Step 7 by 1, as this will be used in Step 10 below

10. Check if the Flic has been triggered too far from any office – we use a Condition action to check if the SuccessfulActions variable equal 0. If so, we send a mobile notification to the triggering user to to let them know they’re too far from the office to trigger an evacuation record

Conclusions

This Flow hopefully demonstrates a viable way to use array variables to dynamically set fields on CDS records; I’ve had particular issues with Option Set fields in the past so I’m happy to have found a potential solution to this problem. Ideally I’d be able to retrieve the Option Set values dynamically, but I’m not aware of any way to retrieve them from CDS. If you are reading this and know of a better way please let me know!

If you’d prefer to watch how to do this, check out the video by clicking here

Using Flic Buttons with CDS – Managing Fire Evacuations

I was asked to create a tech demo in work recently that could show off some of the capabilities of Power Apps and Power Automate, and I figured this would be a perfect opportunity to use the Flic button I got from Matt Beard of Data8 when I attended the User Group Summit in Amsterdam earlier this year .

The Scenario

Anyone who’s worked in an office will undoubtedly have experienced the fun of a fire drill (usually on a day where it is cold and miserable). I thought it would be a great demonstration of the Power Platform if we could create a system to record when a fire evacuation has occurred in one of our offices so we could notify the other offices, and to send notifications when the evacuation was over.

The Setup

The first thing to do was create a new Evacuations entity in CDS where I wanted to record the following:

  1. Evacuation Start Date/Time
  2. Evacuation End Date/Time
  3. Office being evacuated
  4. Any comments/notes about the evacuation

It would be really simple to manually record the evacuations in here, but where’s the fun in that? I had the Flic buttons at my disposal, and I was going to use them!

Using a Flic Button

For those of you who are not aware, Flic buttons have three trigger events:

  1. Click
  2. Double Click
  3. Hold

Each of these triggers can be mapped to a specific action, but the exciting part for us #PowerAddicts is that you can use them to trigger a Flow.

The Flows

For the purposes of this tech demo, I wanted to have two flows:

  1. Create an Evacuation Record
  2. Close the Evacuation Record

Create an Evacuation Record

I created an Instant Flow that is triggered “When a Flic is Pressed”:

As you can see from the screenshot above, there are two options on this trigger. First you select the Flic Button you’ll be associating with the Flow, then you select one of the available Events; I’m going to be using a “Click” event for this Flow.

When you use a Flic trigger, it returns the Click Time in the following format:

2019-11-12T19:19:43Z

This isn’t particularly presentable, so I’ve added a Compose step to format the Date using the following expression:

formatDateTime(triggerBody()?['clicked_at'], 'dd MMMM yyyy')

This compose step formats the Click Time above as “12 November 2019“, and I’m going to use this when I create the record in CDS

The last step in the Flow is to use the Send an Email (V2) action to send an email to everyone in my organisation to let them know our office has been evacuated

And that’s it, we have a simple Flow configured run on clicking a Flic button to create a record in CDS when an evacuation is happening and to notify all employees in our organisation. The next step is to create a Flow to close the evacuation record when everyone returns to the office

Close the Evacuation Record

For this Flow, I’m using the Same “When a Flic is Pressed” trigger as above, but this time I’m going to use the Double Click event. For this Flow I want to retrieve the Active Evacuation records for this office and set them to Inactive, recording the time that the Evacuation was ended.

If you want to find out an easy way to generate OData queries to use in the List Records step, then I’d recommend reading Sara Lagerquist’s recent blog on this topic

Once the Evacuation records are closed, I use the Send an Email (V2) action again to send an update email to all employees to tell them that the Evacuation of the office is now over.

Conclusions

This is a relatively simple use case for using Flic buttons but it shows the power of combining IoT devices with the Power Platform and it reinforces my belief that “no UI is the best UI”. Through two actions we have a complete record in our database, and it’s required no input from the User other than a Click and a Double Click

In part 2 of this blog, I’ll show how I created a canvas app for reviewing and monitoring Evacuations, and how to use the location data so the button knows which office has been evacuated.

Display SharePoint Documents related to Parent on Child Record in Dynamics 365

Recently my friend Sara Lagerquist asked if there was a way to show the documents stored on SharePoint for a parent entity record on a child entity record (e.g. seeing Documents related to an Account on an Opportunity) without having to copy the documents between SharePoint sites.

This sounded like an interesting challenge so I decided to see if I could find a solution.

The Scenario

In my organisation, we have a custom entity called Engagement which is a child of the Account entity that we use as part of our project management processes. It’s not uncommon whilst on an Engagement to want to refer to a contract or other reference file which may be contained on the SharePoint site related to the Account, so finding it can involve a lot of clicking around between records, or navigating back and forth between D365 and SharePoint. The relationship between the entities and the SharePoint sites can be seen below:

In practice, this means that if I was on an Engagement record and there was a reference document in Document Library 1 on the Client Site that I wished to view, I’d have to navigate back to the Account, then look for the related Documents on the Account to see the information I want, then go back to the Engagement. It would be much simpler if I was able just to view this directly from the Engagement.

The Solution

The solution to this situation was to create a simple Flow:

This Flow is made up of two actions:

  1. Find all related Document Locations for the Parent record
  2. For each Document Location, create a copy and Set Regarding to the Child Record

The key thing to note when creating the new Document Locations is that you must ensure you copy the Relative URL, Parent Site or Location and Parent Site or Location Type from the Document Location you’re copying, and then set the Regarding Type to the entity type of the Child Entity, and set the Regarding to the record you’ve triggered the Flow from.

Once you’ve completed these steps, you’re good to go

The Output

Before Flow

As you can see from the screenshot below, on my Account record I have a Document Location called Reference Documents that has two files in it

On my Engagement record I have 3 documents in the Document Locations related to the Engagement record. If I want to access the Client Contract file from the Account I need to navigate to the Account record.

After Flow

After I run my Flow I now have a new Document Location on the Engagement record and I can see the documents from the parent Account record.

Note that the Path for the Engagement related documents is different from the Path for the Account related documents; the Document Location in D365 acts as a signpost to the actual location on SharePoint. This means that the Document only exists in one location, so any changes to it from either the Account or the Engagement will be visible from both records.

Conclusion

This is a simple way to improve the experience for your Users, allowing them to find the information they need quickly and easily, and reducing the amount they might need to click around your system. Let me know in the comments if you think this will be useful for you!

Filtered Lookup Field based on Linked Entity using North52

If you’ve ever had a requirement to filter lookup fields then you’ll no doubt be aware that this is possible in Dynamics 365, but that there are some limitations to the functionality.

Microsoft have done a great job of enabling out of the box filtering for simple scenarios using the “Related Records Filtering” options or by limiting the records returned using specific view(s)

To read more about the options available out of the box I’d recommend referring to Carl de Souza‘s blog post – https://carldesouza.com/filtering-lookup-fields-in-dynamics-365/

For the more developmentally minded amongst us there is also the option to use the addCustomFilter JavaScript function, more information on which can be found on the Microsoft Docs site – https://docs.microsoft.com/en-us/powerapps/developer/model-driven-apps/clientapi/reference/controls/addcustomfilter

For those who are comfortable with JavaScript I’d recommend reading Aileen Gusni‘s posts about this for some tips and tricks – http://missdynamicscrm.blogspot.com/2016/09/utilize-custom-action-to-help-filtering-lookup-view.html

The Scenario

In my scenario we have a Peer Review entity to record the outputs of peer reviews carried out for activities related to an Account. The Peer Review entity has several Reviewer Roles which are lookups to the User entity. The lookups need to be filtered to only show Users who are in the Account Team. I’ve mapped the relationships between the entities below:

I tried to get this to work with the OOTB options, but found that I couldn’t quite get them to work for this scenario. I also looked at the JavaScript options but again ran into issues, primarily because I need the filtering criteria to be dynamic on each Peer Review record depending on the selected Account, whereas the JavaScript was a bit prescriptive for me. (Note: I’m not a coder, so someone cleverer than me could probably get it to do what they needed).

However, in exploring the JavaScript i stumbled upon a potential solution. You can use an “in” operator in a condition in your FetchXML to specify the list of values to be returned, like so:

<filter type='and'> 
        <condition attribute='YOUR_FIELD_HERE' operator='in'>
          <value>{YOUR_GUID_HERE1}</value>
          <value>{YOUR_GUID_HERE2}</value>
          <value>{YOUR_GUID_HERE3}</value>
        </condition>
</filter>

If I could figure out a way to make this list of values dynamic then that would solve my problem!

The Solution

To solve this issue I turned to my trusty old friend North52. I’ve written previously about using looping functions and I’ll be doing something similar here.

The first step is to get the FetchXML to get the Users from the Team, which I’ve done using advanced find to output:

<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="true">
    <entity name="systemuser">
        <attribute name="systemuserid" />
        <link-entity name="teammembership" from="systemuserid" to="systemuserid" visible="false" intersect="true">
            <link-entity name="team" from="teamid" to="teamid" alias="ab">
                <filter type="and">
                    <condition attribute="teamid" operator="eq" value="{0}" />
                </filter>
            </link-entity>
        </link-entity>
    </entity>
</fetch>

As you can see above the value in the teamid condition is set to {0}, and we’ll set this dynamically in the ClientSide – Perform Action formula, which is below:

Smartflow(

  ForEachRecord(

    FindRecordsFD('TeamMembers', true, SetParams([ryan_peerreview.ryan_accountid.ryan_accountteam.teamid.?])), 

    Case(RecordIndex(),

      When(0), Then(SetVar('teammembers', StringFormat('<filter type="and"><condition attribute="systemuserid" operator="in"> <value>{0}</value>', CurrentRecord('systemuserid')))),

      When(RecordTotal()-1), Then(SetVarConcat('teammembers', StringFormat('<value>{0}</value></condition></filter>', CurrentRecord('systemuserid')))),

      Default(SetVarConcat('teammembers', StringFormat('<value>{0}</value>', CurrentRecord('systemuserid'))))

    )

  ),

  AddPreFilterLookup('ryan_primaryreviewerid', 
    'q1z', 
    GetVar('teammembers'), 
    'systemuser')

)

I’ll explain the key elements of this formula below:

SmartFlow: SmartFlow allows you to run multiple actions in one Formula

ForEachRecord: ForEachRecord is a looping function, and it iterates through the output of the FetchXML query we created above using the FindRecordsFD function and carries out the actions specified. As mentioned above, I set the value of the TeamID to be {0}, and now I use the SetParams function to define the value that will be put in here.

As we’re using ForEachRecord to loop through the records returned by the FetchXML, I will use the Case function to create a variable for the filter that I will be putting on the lookup field using the SetVar/SetVarConcat functions

The Case function works by splitting the Filter FetchXML into 3 parts:

  1. The Opening section, which includes the open tags for the Filter and Condition, and the first value returned from the FindRecordsFD function
  2. The Looping section, for all the values between the first and last values returned from the FindRecordsFD function
  3. The Closing section, which includes the closing tags for the Filter and Condition, and the last value returned from the FindRecordsFD function

To make this work with the Case function, we use the RecordIndex function, which contains an integer with the current index number of the loop, so the Case function can be described in plain English as:

WHEN we are on the first loop, THEN create a variable with the opening section of the Filter FetchXML;
WHEN we are on the last loop, THEN concatenate the variable with the closing section of the Filter FetchXML;
OTHERWISE if we are not on the First or Last loops, THEN concatenate the variable with another value

When we have created the Filter FetchXML we use the AddPreFilterLookup function to add the filter to the selected field.

Once we’ve done all of this, the field will show only the people who are in the Team related to the Account on the Peer Review record:

Conclusions

I think this is a good method of dynamically altering the available options in a lookup field, and I can envision a number of useful scenarios for this functionality, but please leave a comment below or reach out to me on social media with your thoughts.

Update a User’s Business Unit and retain their Security Roles

If you have ever had to change the Business Unit of a User then you will know that the change causes their security roles to be dropped, so you need to ensure the security roles are reassigned. This is mildly frustrating when you have one or two Users to update, but what happens when you have to update all Users in your organisation?

I recently encountered a scenario where the organisation needed to completely remodel their Business Unit structure and move the Users to the new Business Units; the prospect of doing this manually filled me with dread.

I’ve done a bit of research and seen methods that others have used (see CRM Tip of the Day 1134 for a good example), but I’m a massive North52 nerd so I felt sure there would be a way I could achieve this dynamically using this tool.

The Scenario

In the scenario, I had ~800 Users in 4 existing Business Units, while the new Business Unit structure was comprised of 7 Business Units. This was further complicated because the Users could have anywhere between 3 and 14 Security Roles (don’t ask…). We needed to be able to assign them to the new BU and ensure their Security Roles were reassigned when their BU was changed.

The Setup

The first step in setting up was to add an Option Set field to the User entity to hold the name of the Business Unit we would be moving the User to.

The next step was to create a FetchXML query to get the Security Roles assigned to a User, which would be called from within the North52 formula. The FetchXML expression I used is:

 <fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="true">
  <entity name="role">
    <attribute name="name" />
    <attribute name="businessunitid" />
    <attribute name="roleid" />
    <order attribute="name" descending="false" />
    <link-entity name="systemuserroles" from="roleid" to="roleid" visible="false" intersect="true">
      <link-entity name="systemuser" from="systemuserid" to="systemuserid" alias="ad">
        <filter type="and">
          <condition attribute="systemuserid" operator="eq" value="@systemuserid@" />
        </filter>
      </link-entity>
    </link-entity>
  </entity>
</fetch>

 

The important thing to note from the FetchXML expression is that the value in the condition searching for the systemuserid is @systemuserid@. North52 uses the @…@ tags to find the value in the field with the schema name that is enclosed in the tags, so it will substitute the GUID of the User on each record this is triggered on.

The Formula

The North52 Formula is actually relatively straightforward:

 SmartFlow(

  SetVar('BusUnitID', 

    Case([systemuser.hr_primarypractice], 

      When( 150000000), Then (FindValueQuickId('businessunit','Actuarial & Benefits')),

      When( 100000000), Then (FindValueQuickId('businessunit','Business Support Unit')),

      When( 100000001), Then (FindValueQuickId('businessunit','Commercial Group')),

      When( 100000002), Then (FindValueQuickId('businessunit','Insights & Analytics')),

      When( 150000001), Then (FindValueQuickId('businessunit','Investment')),

      When( 100000003), Then (FindValueQuickId('businessunit','Life & Financial Services')),

      When( 150000003), Then (FindValueQuickId('businessunit','Third Party Administration')),

      Default(FindValueQuickId('businessunit','Business Support Unit'))

    )
  ),

  ForEachRecord(

    FindRecordsFD('FindUserSecurityRoles','true'), 

    SetVar('Role' + recordIndex(), FindValue('role','roleid',currentrecord('roleid'),'name','?','true')),

    SetVar('Roles', RecordTotal())
  ),




  UpdateRecord('systemuser',
    [systemuser.systemuserid],
    SetAttributeLookup('businessunitid', 'businessunit', GetVar('BusUnitID'))

  ),

  DoLoop(GetVar('Roles'), 

    AssociateEntities('systemuser',[systemuser.systemuserid],
      'role',
      FindValue('role',
        SetFindAnd('businessunitid','name'),
        SetFindAnd(GetVar('BusUnitID'),GetVar('Role' + DoLoopIndex())),
        'roleid'),
      'systemuserroles_association')

  )


)
 

I’ll try to explain some of the key elements of this N52 Formula below:

Case: The Case function is used to check the value in the new Business Unit Option Set field, and it sets a variable to hold the ID of the new Business Unit (‘BusUnitID’) so we can use it later in the formula

ForEachRecord: ForEachRecord is a looping function, and it iterates through the output of the FetchXML query we created above using the FindRecordsFD function and carries out the actions specified. In this instance I have two actions:

  1. Create a Variable to hold the Name of the Security Role (‘Role’). The RecordIndex() function outputs an integer with the number of the current loop, so I’ve used it to create iterative Variables (i.e. each loop of the ForEachRecord function will create a new Variable (Role1, Role2, Role3, etc.)
  2. Create a Variable (‘Roles’)to hold the Total Number of loops we’re running, which will be used later in the Formula

DoLoop: DoLoop is another looping function that allows us build an iterative function to complete actions. We use the variable that we created in the ForEachRecord function step to hold the total number of loops (‘Roles’) to specify the number of iterations of the DoLoop function to carry out.

For each role we will carry out an AssociateEntities function to associate the User to the Security Role. To find the right Security Role(s) to associate we do a FindValue function and use SetFindAnd to enable us to specify multiple input parameters that must be met. In this case we want to find Security Roles using the following criteria:

  1. The businessunitid of the new Business Unit we’ve updated on the User Record, using the ‘BusUnitID’ variable we set at the start of the formula
  2. The Security Role name, which we retrieve by getting the Variable using the DoLoopIndex() function, which outputs an integer with the current loop number, identical to the RecordIndex() function we used to set the Variable name in the ForEachRecords function.

Conclusion

This relatively straightforward formula allowed me to dynamically update all of my Users to their new Business Units and to ensure their security roles were applied properly. It saved me a huge amount of time over a manual approach, and hopefully has demonstrated some of the capabilities of the North52 solution.

I am sure I will be able to reuse the functionality of setting and getting a dynamic number of variables using ForEachRecord and DoLoop functions in this way, but I’d love to hear from others if they can think of any other scenarios in which this could be applied, so please feel free to reach out!