How I Solved This with Jennifer Lee and Tom Hoffman.

How I Solved This: Dynamically Share Records for Multiple Objects

By

Welcome to another “How I Solved This.” In this series, we do a deep dive into a specific business problem and share how one #AwesomeAdmin chose to solve it. Once you learn how they solved their specific problem, you’ll be inspired to try their solution yourself! Watch how Tom Hoffman, Practice Director with Spark Orange, was able to use the power of Flow to dynamically share records for multiple objects, and then read all the details in the post below.


Key business problem

As an admin for a Salesforce instance with a private sharing model, you rely on the full complement of profiles, permissions, roles, and sharing rules to facilitate record access to meet your business requirements. However, you’ve been asked to share and provide access to individual users defined on a record by record basis that does not fit neatly inside a role, group, or hierarchy. Your sharing model looks like what’s below, so you have to find a solution that allows for dynamic sharing specific to each record.

Organization-wide defaults showing many of the objects have Private access.

Background

When an object’s default sharing settings are Private, only the record owner and those directly above them in the role hierarchy have access to a record. Additional access can be granted to users with the View All permission at the profile/permission set level or via sharing rules to roles and/or public groups.

While these tools certainly cover a wide range of sharing requirements, there are times when, as admins, we need the ability to share data with users linked to a record via a lookup value in much the same fashion that sharing sets allow admins to grant community users access to records where their User.ContactId value is indicated via a Contact lookup field.

Admins not familiar with ‘share’ objects may mistakenly believe Apex managed sharing is the only approach to this type of dynamic record access. A quick look at the Object Reference for Salesforce and Lightning Platform reveals that for all objects where sharing is available, there is also a corresponding share object; for example, Account, AccountShare; Case, CaseShare; Contact, ContactShare; and so on, including for custom objects not in a master-detail relationship. These are the magical ‘share tables’ that are often discussed when designing for security and sharing.

Pro tip: Bookmark the Object Reference for Salesforce and Lightning Platform site. I use it several times a day when I’m designing and planning; it’s immensely helpful.

Each share object has a ‘RowCause’ field that indicates the reason a sharing entry exists. It’s important to note that, as admins, we can only write to a share object when inserting, updating, or deleting a record whose RowCause = Manual. Armed with this information, we can now implement a scalable, dynamic approach to record sharing that allows us to use a single auto-launched flow to grant access to any object where sharing is allowed.

Available share objects.

RowCause information.

How I solved it

Foundation — Creating a single object record sharing flow

As a starting point, it’s helpful to understand the mechanics of using Flow for dynamic record sharing on a single object. We’ll do this using a record-triggered flow (after), triggered upon create or update.

1. The first step is to create a lookup field to the user object on the object you’re looking to share.

It’s important to understand any relationships involved as part of this process; for example, an object that is the detail side of a master-detail relationship cannot be shared since sharing is determined by the parent object. This also applies to standard objects that do not have their own share table like opportunity contact roles. Access to the related opportunity provides access to the OCR records.

For our example, I’m adding a lookup on the account object called ‘Custom Lookup’ that will allow us to define the user with whom we want to share this account record.

The Custom Lookup field attributes.

The Account relationship.

2. Create your record-triggered flow.

In my example, I’ll use the Account object since my lookup is there.

  • On the ‘Configure Start’ screen, select the object where your user lookup is located.
  • Set the Trigger the Flow When to ‘A record is created or deleted’.
  • Note: I do not specify any criteria as I use a Decision element to route my record-triggered flows (different discussion for another blog!).

Flow Start element.

3. Build the rest of your flow (details below).

The flow.

Do we need to delete an old share first?

  • The ‘Prior Value Updated or Cleared’ Decision determines if we need to delete the manual record share associated with a prior user/value in our custom lookup field. If the prior value was blank or not changed, it moves on to the next Decision element; if the prior value was cleared or changed, it heads down the ‘Yes it Was’ path.
  • This Decision checks if the value was cleared (1 and 2) OR if the current value is different from the prior value after confirming the prior value was not null (2 and 3 and 4).
  • The ‘Yes it Was’ path sends the flow into a ‘Get Old Share’ step, where using the $Record.ID & $RecordPrior.Custom_Lookup__c value to find a share where the RowCause = ‘Manual. If one is found, we delete it, then we continue on with our flow returning to our ‘New User Populated’ Decision.

Prior Value Updated or Cleared? Decision element.

Do we need to create a new manual share?

  • The ‘New User Populated’ Decision determines if we need to create a new record share by looking at the current $Record.Custom_Lookup__c value compared to its $RecordPrior values.
  • This Decision checks if the value was empty before and now populated (1 and 2) OR if the current value is different from the prior value after confirming the prior value was not null (2 and 3 and 4).
  • The ‘Yes New User’ branch sends the flow to our create path; otherwise, we let the default outcome end the flow.

New User Populated? Decision Flow element.

Creating our new manual share

  • We always check to see if a record exists before creating one, not just with share tables. In general, it’s the right way to approach automating record creation. We do this in our flow through the ‘Check for Existing Access’ Get Records step where we look for a share record for this $Record.Id & $Record.Custom_Lookup__c combination where RowCause = ‘Manual’.
  • Our next Decision determines if a record was found; if not, we create it.
  • If one was found, we check if the correct access is on the existing share record; if not, we create a new one. Example: A user has Read Only access via manual sharing but our flow wants to grant them Read/Write.
  • Fun Fact: When inserting a share record for a user that already has one, it will update the existing record, so there is no need to have a separate update step!

A portion of the flow; Get Records, two Decisions, and Create Records.

Now that we’ve established our foundation, using a user lookup field to dynamically share records via Flow, it’s time to scale this into a multi-object tool that can be continuously extended!

Creating a multi-object record sharing flow

Now that we know the movements involved in manual record sharing with Flow, we simply need to make a few tweaks to scale it appropriately. 

1. Create lookup fields.

Create custom lookups to the User object on all the objects where you would like to use dynamic record sharing with Flow.

2. Create an auto-launched flow.

Create a new flow with the type ‘Auto-Launched.’

Total Pro Move: From the flow above, select Save As, then a New Flow at the top of the following screen, and under ‘Show Advanced’ you can save it as an Auto-Launched Flow. You’ll need to update all our $Record and $RecordPrior references, but it’s helpful!

3. Add your variables.

Create four text variables to support our flow:

  • CurrentUser — type text, single value, available for input
  • PriorUser — type text, single value, available for input
  • ObjectName — type text, single value, available for input
  • RecordId — type text, single value, available for input

4. Build the flow.

Build the flow from the foundation step above for each object you created a lookup field for in Step 1. You can copy and paste the branch—just note that you’ll need to update the Step Labels, API Names, and object references to make them work correctly (I realize you don’t HAVE to update the labels and API names… but come on, just do it!).

5. Add a Decision element directly after the start.

Determining the object defined in the ObjectName variable and sending it to the current object-specific Flow branch created in Step 4.

In my example below, I have three objects: Accounts, Opportunities, and an ExampleCustomObject. The initial Decision checks the ObjectName variable and then routes it to the correct branch.

Which Object? Decision Flow element.

Flow with multiple objects sharing flow.

6. Call your sharing flow from a record-triggered flow.

If you have an existing record-triggered flow on an object, add to that; otherwise, create a new record-triggered flow for each object you would like to share. Since we want our record-triggered flows to be scalable, I skip any initial entry criteria and use Decision elements to determine if a certain path should be followed. 

Record-triggered flow calling a subflow.

In the example below, my criteria is checking for the following conditions:

  1. Record is new and lookup is populated.
  2. Prior Value is blank and now is populated.
  3. Prior Value is not blank and current value is blank.
  4. Prior & Current value are not blank but are different.

The configured Decision.

We then call our subflow, setting our input variables as shown below.

The subflow.

Business results

Leaning on those #AwesomeAdmin skills, you’ve created a multi-object flow that is handling dynamic sharing across several objects through a user experience-friendly lookup field. Your team can quickly grant access to other users while updating their records and not having to bother with manual sharing. Users and management are thrilled that expanding the tool to additional objects is a quickly achievable task, allowing them to focus on their roles rather than trying to grant their team members access to data.

Do try this at home

Save the Resources

The Object Reference guide mentioned at the beginning of this post is something all admins, developers, consultants, etc. should have bookmarked. I’ll often get questions from coworkers that I know can be answered with a browse of this tool, so a sly smile and a “I bet you have the tools to figure this one out” is a pretty standard reply around the office.

Sub It

The ability to create subflows from record-triggered flows was delivered in Winter ’22 (sorry, Process Builder… we are officially breaking up now). You should attempt to build your version using Record-Triggered Flows calling the Multi-Object Sharing Flow as a SubFlow.

Resources

Want to see more good stuff? Subscribe to our channel!

SUBSCRIBE TODAY
How I Solved It with Jennifer Lee and Dee Ervin

Search Unsearchable Field Data Types | How I Solved It

Welcome to another “How I Solved It.” In this series, we do a deep dive into a specific business problem and share how one Awesome Admin chose to solve it. Once you learn how they solved their specific problem, you’ll be inspired to try their solution yourself! Watch how Dee Ervin searched unsearchable field data […]

READ MORE