Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save infocynic/c6886b8a3220f4fcfc337e85dd9d7750 to your computer and use it in GitHub Desktop.
Save infocynic/c6886b8a3220f4fcfc337e85dd9d7750 to your computer and use it in GitHub Desktop.
Subflow to find users with a custom permission
<?xml version="1.0" encoding="UTF-8"?>
<Flow xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>58.0</apiVersion>
<assignments>
<description>Add the current PSet Id from the Record (ParentId) to the Text list</description>
<name>Add_PSet_Id_to_List</name>
<label>Add PSet Id to List</label>
<locationX>264</locationX>
<locationY>458</locationY>
<assignmentItems>
<assignToReference>pSetIdCollection</assignToReference>
<operator>Add</operator>
<value>
<elementReference>Loop_PSet_IDs.ParentId</elementReference>
</value>
</assignmentItems>
<connector>
<targetReference>Loop_PSet_IDs</targetReference>
</connector>
</assignments>
<assignments>
<description>The Remove first here gets us a &quot;Set&quot; where the IDs are actually unique.</description>
<name>Add_UserId_to_List</name>
<label>Add UserId to List</label>
<locationX>264</locationX>
<locationY>866</locationY>
<assignmentItems>
<assignToReference>out_UserIds</assignToReference>
<operator>RemoveAll</operator>
<value>
<elementReference>Convert_PSA_records_to_UserId_list.AssigneeId</elementReference>
</value>
</assignmentItems>
<assignmentItems>
<assignToReference>out_UserIds</assignToReference>
<operator>Add</operator>
<value>
<elementReference>Convert_PSA_records_to_UserId_list.AssigneeId</elementReference>
</value>
</assignmentItems>
<connector>
<targetReference>Convert_PSA_records_to_UserId_list</targetReference>
</connector>
</assignments>
<description>Gets the User IDs (only) for all users with a given custom permission, returned as a text collection suitable for use with IN clauses to retrieve whatever other information is needed, if any. Note that due to Flow limitations, Inactive users may be returned this way, callers should take that into account and apply filters when they query the User records.</description>
<environments>Default</environments>
<interviewLabel>SUB Get Users with Custom Permission {!$Flow.CurrentDateTime}</interviewLabel>
<label>SUB Get UserIds with Custom Permission</label>
<loops>
<description>Loop the PSA records and extract just the UserId (AssigneeId) from the record to a text collection</description>
<name>Convert_PSA_records_to_UserId_list</name>
<label>Convert PSA records to UserId list</label>
<locationX>176</locationX>
<locationY>758</locationY>
<collectionReference>Get_Permission_Set_Assignments</collectionReference>
<iterationOrder>Asc</iterationOrder>
<nextValueConnector>
<targetReference>Add_UserId_to_List</targetReference>
</nextValueConnector>
</loops>
<loops>
<description>Loop the records found by the SetupEntityAccess query to create a text collection for use with an IN operator.</description>
<name>Loop_PSet_IDs</name>
<label>Loop PSet IDs</label>
<locationX>176</locationX>
<locationY>350</locationY>
<collectionReference>Get_Permission_Set_Ids</collectionReference>
<iterationOrder>Asc</iterationOrder>
<nextValueConnector>
<targetReference>Add_PSet_Id_to_List</targetReference>
</nextValueConnector>
<noMoreValuesConnector>
<targetReference>Get_Permission_Set_Assignments</targetReference>
</noMoreValuesConnector>
</loops>
<processMetadataValues>
<name>BuilderType</name>
<value>
<stringValue>LightningFlowBuilder</stringValue>
</value>
</processMetadataValues>
<processMetadataValues>
<name>CanvasMode</name>
<value>
<stringValue>AUTO_LAYOUT_CANVAS</stringValue>
</value>
</processMetadataValues>
<processMetadataValues>
<name>OriginBuilderType</name>
<value>
<stringValue>LightningFlowBuilder</stringValue>
</value>
</processMetadataValues>
<processType>AutoLaunchedFlow</processType>
<recordLookups>
<description>Even profiles and PSGs will show up this way because they&apos;re still PermissionSets under the hood</description>
<name>Get_Permission_Set_Assignments</name>
<label>Get Permission Set Assignments</label>
<locationX>176</locationX>
<locationY>650</locationY>
<assignNullValuesIfNoRecordsFound>false</assignNullValuesIfNoRecordsFound>
<connector>
<targetReference>Convert_PSA_records_to_UserId_list</targetReference>
</connector>
<filterLogic>and</filterLogic>
<filters>
<field>PermissionSetId</field>
<operator>In</operator>
<value>
<elementReference>pSetIdCollection</elementReference>
</value>
</filters>
<getFirstRecordOnly>false</getFirstRecordOnly>
<object>PermissionSetAssignment</object>
<storeOutputAutomatically>true</storeOutputAutomatically>
</recordLookups>
<recordLookups>
<description>SetupEntityAccess will tell us where a SetupEntityId is used.</description>
<name>Get_Permission_Set_Ids</name>
<label>Get Permission Set Ids</label>
<locationX>176</locationX>
<locationY>242</locationY>
<assignNullValuesIfNoRecordsFound>false</assignNullValuesIfNoRecordsFound>
<connector>
<targetReference>Loop_PSet_IDs</targetReference>
</connector>
<filterLogic>and</filterLogic>
<filters>
<field>SetupEntityId</field>
<operator>EqualTo</operator>
<value>
<elementReference>Get_Setup_Entity_Id.Id</elementReference>
</value>
</filters>
<getFirstRecordOnly>false</getFirstRecordOnly>
<object>SetupEntityAccess</object>
<storeOutputAutomatically>true</storeOutputAutomatically>
</recordLookups>
<recordLookups>
<description>Custom permissions are a setup entity -- we need the ID that goes with the name for the next query because Flow doesn&apos;t give us any way to write subqueries.</description>
<name>Get_Setup_Entity_Id</name>
<label>Get Setup Entity Id</label>
<locationX>176</locationX>
<locationY>134</locationY>
<assignNullValuesIfNoRecordsFound>false</assignNullValuesIfNoRecordsFound>
<connector>
<targetReference>Get_Permission_Set_Ids</targetReference>
</connector>
<filterLogic>and</filterLogic>
<filters>
<field>DeveloperName</field>
<operator>EqualTo</operator>
<value>
<elementReference>in_CustomPermissionApiName</elementReference>
</value>
</filters>
<filters>
<field>NamespacePrefix</field>
<operator>EqualTo</operator>
<value>
<elementReference>in_NamepacePrefix</elementReference>
</value>
</filters>
<getFirstRecordOnly>true</getFirstRecordOnly>
<object>CustomPermission</object>
<storeOutputAutomatically>true</storeOutputAutomatically>
</recordLookups>
<runInMode>SystemModeWithoutSharing</runInMode>
<start>
<locationX>50</locationX>
<locationY>0</locationY>
<connector>
<targetReference>Get_Setup_Entity_Id</targetReference>
</connector>
</start>
<status>Active</status>
<variables>
<name>in_CustomPermissionApiName</name>
<dataType>String</dataType>
<isCollection>false</isCollection>
<isInput>true</isInput>
<isOutput>false</isOutput>
</variables>
<variables>
<description>Optional namespace prefix to apply when finding the Custom Permission.</description>
<name>in_NamepacePrefix</name>
<dataType>String</dataType>
<isCollection>false</isCollection>
<isInput>true</isInput>
<isOutput>false</isOutput>
<value>
<stringValue></stringValue>
</value>
</variables>
<variables>
<description>List of UserIds to be output by this flow representing the final collection of users who have the custom permission assigned</description>
<name>out_UserIds</name>
<dataType>String</dataType>
<isCollection>true</isCollection>
<isInput>false</isInput>
<isOutput>true</isOutput>
</variables>
<variables>
<description>Collection of PermissionSetIds as text -- the IN operator can&apos;t use a list of SObjects yet (as of Summer 23).</description>
<name>pSetIdCollection</name>
<dataType>String</dataType>
<isCollection>true</isCollection>
<isInput>false</isInput>
<isOutput>false</isOutput>
</variables>
</Flow>
@infocynic
Copy link
Author

It's bulk-safe, but it hasn't really been tested at high volumes. I considered making a version that took a list of possible custom permissions, but felt that was a weird question to be asking "give me any users with X, Y, or Z" ... just make a new permission instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment