When a user is deactivated from Salesforce, there is cleanup work that is needed after the fact if you want to keep your org relatively free of “clutter.”
In most cases, leaving inactive users associated to public groups, queues, permission sets, etc. is fairly harmless. As I mentioned before, it’s just clutter in your org. However, if your org has managed packages which have paid user licenses, when a user is deactivated, unless you physically remove the user from the managed license, that deactivated user will still count as an active licensed user of that AppExchange tool. That could be unnecessary $ out the door! Who needs that?!
It’s really too bad that there isn’t an automated out of the box Salesforce platform solution for this. However, with the power of clicks, not code, you can automate the removal of inactive users from managed packages licenses and their related permission sets using process builder and visual workflow. Love process automation!
Here are a few lessons learned from implementing this use case:
- Automate the removal of a deactivated user from an user license package and permission set assignment objects.
- Use custom metadata type to store Salesforce IDs rather than hardcoding them in your flow. It is a best practice to reference IDs outside your flow, especially since IDs are different in each environment, when created in that environment for the first time. By storing the IDs in a custom label, custom permission or custom metadata type, we can modify the ID outside of flow.
- Use process builder to invoke flow.
- Strengthen your flow from faulting by adding decision elements.
- Provide descriptions, where provided, in Salesforce. This may be tedious step, I know, but your future self will thank you when you are trying to remember what you configured or assist other/future admins when troubleshooting or enhancing what was built. This includes providing the purpose of a process builder, flow, variable, etc.
Note: This use case was written for the SKUID managed license and permission sets. It can be tweaked to handle other managed packages. You can delete the items related to the permission set if your managed license does not include permission sets.
Business Use Case: Addison Dogster is a system administrator at Universal Container. Addison noticed when performing a user audit review, that although user are deactivated in Salesforce, the users are still assigned to the SKUID license and permission set. Although the removal of a user from SKUID is on the checklist activity, sometimes, it may be overlooked due to human error.
Solution: Addison Dogster, being the Awesome Admin that she is, is able to solve this using process builder, visual workflow and custom metadata type. She can do this with clicks, no code!
Before we move further, while the storing of Salesforce IDs can be handled via a custom label or custom setting, we prefer to use custom metadata type in this case because flow can reference custom metadata type but also because the data records, in this case, the IDs, are stored as metadata. This means the data records associated with a custom metadata type can be deployed in a changeset up the regions or environments and will be there after a sandbox is refreshed or created from Production, unlike its sibling, custom setting, whose data records are data, not metadata. Those records will not come down automatically with a refresh or sandbox creation and cannot be deployed in changesets. That would require a data loader or manual add to the region/environment.
Quick Steps:
1.Let’s create the custom metadata type (Develop | Custom Metadata Type in Salesforce Classic or Custom Code | Custom Metadata Types in Lightning Experience).
We’re going to call this custom metadata type something generic, such as Permission Set ID so we can use this custom metadata type to store other perm set IDs for use in the future.
Best practice tip: Don’t forget to provide a description so you and other/future admins know what this custom metadata type is used for.
A. We need to create custom fields that will store the Salesforce IDs for the three Skuid permission sets.
Best practice tip: Don’t forget to provide a description so you and other/future admins know what this custom metadata type field is used for.
The end result looks like this…
B. Create the custom metadata type data record by clicking on the Manage Permission Set Ids button. Then, create the “new” button on the next page.
Create a record called “PermSetIDs.” In the three defined permission set fields, navigate to the permission set record for the Skuid Admin, Skuid Page Builder and Skuid Page Viewer permission sets. Take the ID in the URL (as shown in the screenshot of the Skuid Admin permission set).
Then, past the IDs in the respective permission set fields and click the “Save” button.
The end result looks like this…
2. Now, let’s create the visual workflow (Create | Workflows & Approvals | Flows in Salesforce Classic or Process Automation | Flows in Lightning Experience). This flow will lookup the custom metadata type to pull the permission set IDs into the flow, determine whether the user is associated to a managed package, then determine if a user is associated to a Skuid perm sets. If yes, remove the perm sets and then remove the managed user licenses from the deactivated user.
A. We will create 4 variables upfront, which will store the user ID, the permission set ID for the Skuid Admin, Skuid Page Builder and Skuid Page Viewer.
Best practice tip: Don’t forget to provide a description so you and other/future admins know what these variables are used for.
B. Create 2 SObject Collection Variables, to hold the collection of the user package licenses and the collection of permission sets.
Best practice tip: Don’t forget to provide a description so you and other/future admins know what these sobject collection variables are used for.
C. Perform a Fast Lookup flow element that will lookup the UserPackageLicense object where the UserId is the user ID passed from process builder. Once it finds UserPackageLicense records, it will store it in the sObject collection variable sCollectionofUserPackageLicenses.
Check the box “Assign null to the variable if no records are found.”
Then specify to save the PackageLicenseId as the record field in the variable.
The end result should look like this…
Best practice tip: Don’t forget to provide a description so you and other/future admins know what this flow element is used for.
D. Now, we need to use a Decision flow element to determine whether records were found in the Fast Lookup to continue on with the process.
We want to decide whether the sobject collection sCollectionofUserPackageLicenses has records so it is set to {!sCollectionofUserPackageLicenses} is null {!GlobalConstant.False}, which means that the sobject collection is not null.
Best practice tip: Don’t forget to provide a description so you and other/future admins know what this flow element is used for.
E. We will perform a Record Lookup flow element where we will lookup the custom metadata type we created in Step 1 to grab the Skuid permission set IDs and store them in the three flow variables earlier.
We will look up the custom metadata type object Permission_Set_IDs_mdt where the MasterLabel is PermSetIDs (custom metadata type data record).
Take the three Skuid permission sets and assign the values to the variables, {!varSkuidAdminPermSetID}, {!varSkuidPageBuilderPermSetID} and {!varSkuidPageViewerPermSetID}.
Ensure you check the box “Assign null values to the variable(s) if no records are found.
The end results look like this…
Best practice tip: Don’t forget to provide a description so you and other/future admins know what this flow element is used for.
F. In our next step, we will add a Fast Lookup flow element of the PermissionSetAssignment object where the AssigneeId is the user’s ID and the PermissionSetIds equal one of the three variables for the Skuid permission sets.
If records are found, store them in the sobject collection variable sCollectionOfPermSets.
Don’t forget to check the box “Assign null to the variable if no records found.”
Take the record’s PermissionSetId field and store it in the sobject collection.
Best practice tip: Don’t forget to provide a description so you and other/future admins know what this flow element is used for.
G. Next, we need a Decision flow element to determine whether the user is associated to a Skuid permission set. Here, we want to look at the sobject collection variable {!sCollectionOfPermSets} is not null or “is null {!$GlobalConstant.False}.”
Best practice tip: Don’t forget to provide a description so you and other/future admins know what this flow element is used for.
H. We now need a Fast Delete flow element to delete the records found in the sobject collection sCollectionOfPermSets.
Best practice tip: Don’t forget to provide a description so you and other/future admins know what this flow element is used for.
I. We need another Fast Delete flow element to delete the records found in the sobject collection sCollectionofUserPackageLicense.
Best practice tip: Don’t forget to provide a description so you and other/future admins know what this flow element is used for.
J. Draw the other connectors between the flow elements.
K. Add the subflow Send Flow Fault Email. For instructions on how to create this, go to Step 2 of blog post: Maximize Maintainability With Process Builder and Componentized Visual Workflow.
L. Set your flow starting point. And connect the flow elements and the fault connectors to match the below…
Can’t seem to get the fault connector to appear? Create a temporary flow element (circled below), draw your first connector to that temporary flow element. Then, draw another connector to the send element. This connector has the word “FAULT” in it.
Once that is completed, delete the temporary flow element you created. This is the end result.
M. Click on the Save button and provide the following information
Best Practice Tip: Don’t forget to provide a description so you and other/future admins know what this visual workflow is for.
Best practice tip: Don’t forget to provide a description so you and other/future admins know what this flow is used for.
N. Click the “Close” button.
O. On the flows screen, activate the flow.
3. Finally, we will create a process using process builder that will run when a user is deactivated and will invoke the flow created in Step 2. Create a process builder (Create | Workflows & Approvals | Process Builder in Salesforce Classic or Process Automation | Process Builder in Lightning Experience).
A. Set the process builder properties. We will call this process “Deactivate Users”, provide it a description and select this process to start when “A record changes.”
Best practice tips: Don’t forget to provide a description so you and other/future admins know what this process is used for.
B. We need to select “User” as the object and we want our process to run when a record is created or edited, click the “Save” button.
C. We need to specify the criteria node that needs to be met to then execute the flow. Let’s call out criteria “User is deactivated” which will execute when “conditions are met.”
We want this to run when the user’s active field has changed and the active field is set to false. Save the criteria node.
D. Next, we want to set the immediate action to invoke the flow we created in Step 2.
Action Type: Flows
Action Name: InvokeFlow
Flow: Delete User Package Licenses (this was the flow we activated in Step 2)
Set Flow Variables: We want to send the User’s ID in the variable varUserID.
The end result looks like this…
E. Click on the “Activate” button to activate the process.
Congrats, you made it to the end! You’ve implemented a process to automatically remove managed package licenses and applicable permission sets when a user is deactivated.
Now, before you deploy the changes to Production, don’t forget to test your configuration changes.
- Assign a user to a managed package license.
- Confirm that the user is assigned to the managed package license.
- Go to the user record, deactivate the user and save.
- Confirm the user’s active field is unchecked.
- Navigate to the Managed Package Licenses page.
- Confirm that the user is no longer listed as having a managed package license.
- Assign a user to a managed package license that has permission sets (i.e. Skuid).
- Confirm that the user is assigned to the managed package license.
- Assign the user to one or more Skuid permission set(s).
- Go to the user record, deactivate the user and save.
- Confirm the user’s active field is unchecked.
- Go to the user’s permission set related list.
- Confirm the user is no longer associated to the Skuid permission set(s).
- Navigate to the Managed Package Licenses page.
- Confirm that the user is no longer listed as having a managed package license.
Deployment Notes/Tips:
- The new custom metadata type, custom metadata type record, process builder and visual workflow can be deployed to Production in a change set (or can be deployed using a tool such as Dreamfactory’s Snapshot).
- You will find the custom metadata type information under the Custom Metadata Type in the Component Type dropdown. However, locate the data records for the custom metadata types by looking for the custom metadata type names in the Component Types dropdown.
- You will find the process builder and visual workflow components in a change set under the Flow Definition component type.
- Activate the process builder and visual workflow as they are deployed as inactive into Production.
Hi i am not able to select Is changed operator in Process builder…Please advice
LikeLike
What do you see in the Operator picklist then?
LikeLike
Great way of cleaning up license assignments after users are deactivated. Although currently we do this via UserTrigger, its a great design if you want to achieve this via config.
LikeLike
Thanks
LikeLike
Hi Jenwlee,
Thanks for the detailed explanation.
How to test this process is working fine or not in Sandbox instances,Manage package licences are not available in sandbox. Because we cant create this directly in production alone.
LikeLike
I had tested out this process first in a personal dev org so I know the license piece works. You would need to reach out to the vendor to see if it is possible to enable the manage license function in a sandbox. Not sure if they can do it or not.
LikeLike
Hi Jen, this is such a great outline and idea! Thank you for putting so much detail into it. I’m wondering if you can help me with a step I’m stuck on. Step E…states “We will look up the custom metadata type object Permission_Set_IDs_mdt where the MasterLabel is PermSetIDs (custom metadata type data record).”
I can choose Permission_Set_IDs_mdt as well as MasterLabel however I’m not able to choose PermSetIDs in the Value column. I don’t know if I missed a step before hand or if I’m looking in the wrong place? It’s not a Variable that was created in the initial steps and I don’t see it listed under Outcomes, Record Lookups, Fast Lookups or Global Constant. Flows are still relatively new to me so I’m sure I’m missing something easy. If you could assist me I would be grateful…would love to test this in our org. Thank you in advance!!!
LikeLike
Check Step 1B. This is where you create the custom metadata type record that you are referencing in Step E. Hope that helps.
LikeLike