
Hello, and welcome to Another Salesforce Blog! Here I will be posting solutions to problems that I couldn’t find an answer to in hopes of helping those who find themselves stuck when using the Salesforce platform.
User Story
We have a new object in our organization. We must update our existing profiles to have appropriate permission sets for the object. Some profiles will have Read access, others will have Edit access. We want to automate this because we have dozens of profiles, and we have to make this change in a testing environment, a staging environment, and production.
Background
Unfortunately, updating profile object permission sets is a time consuming manual process. While you are able to edit multiple profiles with list views, you would have to set up the list view for each org that you want to make changes in. This is still a time consuming manual process, and likely to be error prone.
This tutorial assumes that you have the most recent version of Apache ANT installed, instructions for which can be found here. I have my ANT folder installed under my C:\
directory for ease of access.
If you are not familiar with ANT, you can refer to my previous post, Getting Started With ANT: Retrieving Metadata via ANT Migration Tool for Salesforce.
Solution
As in my previous post, we will start by retrieving the profiles using Apache Ant. We will need three files, build.properties
, build.xml
, and package.xml
. The package.xml
file will be contained within a folder called retrievepkg
. Additionally, we will have a second folder called deploypkg
, which we will populate with our changes. I have my files located in the folder C:\ant\UpdateCases
for ease of access.

Step One – build the files.
build.properties
Today, we will make our build.properties
file a little bit more suited to an enterprise org by creating parameters for two environments, Stage
and Production
. This will allow us to test our changes in a lower environment before applying them to the full organization. This is useful for automating lengthy deployments. If you are using a developer environment, you can specify sf.username
and sf.password
here.
Additionally, we will specify a retrieval package, retrievepkg
, and a deployment package, deploypkg
, which will allow us to easily copy our changes to multiple environments.
# build.properties
#
# Specify the login credentials for the desired Salesforce organization
sf.usernameStage = USERNAME
sf.passwordStage = PASSWORD
sf.usernameProd = USERNAME
sf.passwordProd = PASSWORD
#sf.pkgName = <Insert comma separated package names to be retrieved>
sf.pkgNameRetrieve = retrievepkg
sf.pkgNameDeploy = deploypkg
# Use 'https://login.salesforce.com' for production or developer edition (the default if not specified).
# Use 'https://test.salesforce.com for sandbox.
#sf.serverurl = https://test.salesforce.com
sf.testurl = https://test.salesforce.com
sf.loginurl = https://login.salesforce.com
sf.maxPoll = 20
# If your network requires an HTTP proxy, see http://ant.apache.org/manual/proxy.html for configuration.
#
package.xml
In order to retrieve the metadata for an object as it relates to a profile, we must retrieve the object as well as the profiles. We can specify this in our package.xml
file according to the Salesforce documentation. In order to retrieve a standard or custom object, we will name the component using the CustomObject
tag, and declare the specific object using the members
tag. In order to retrieve a standard or custom field, we would name the component using the CustomField
tag, and declare the field using the members
tag.
The example given in the Salesforce documentation is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>Case.EngineeringReqNumber__c</members>
<name>CustomField</name>
</types>
<types>
<members>Account</members>
<name>CustomObject</name>
</types>
<version>47.0</version>
</Package>
This example is easily modified. For this tutorial, we will be retrieving the Case
object metadata and the Profile
metadata to modify CRUDE permissions on the Case
object for each profile. Our package.xml
file will be:
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>Case</members>
<name>CustomObject</name>
</types>
<types>
<members>*</members>
<name>Profile</name>
</types>
<version>47.0</version>
</Package>
build.xml
Lastly, we will build our build.xml
file. Similar to the last tutorial, we will have a retrieve
action, but we will also have a deploy
action so that we can deploy our changes. For a step by step walkthrough of each line of code, please see my previous tutorial here. Note that we are specifying the login parameters as sf.usernameStage
and sf.passwordStage
so that we can test this in a lower environment first. If you are using a developer org, you can use sf.username
and sf.password
here.
<project name="Another Salesforce Blog" default="test" basedir="." xmlns:sf="antlib:com.salesforce">
<property file="build.properties"/>
<property environment="env"/>
<!-- Setting default value for username, password and session id properties to empty string
so unset values are treated as empty. Without this, ant expressions such as ${sf.username}
will be treated literally.
-->
<condition property="sf.usernameStage" value=""> <not> <isset property="sf.usernameStage"/> </not> </condition>
<condition property="sf.passwordStage" value=""> <not> <isset property="sf.passwordStage"/> </not> </condition>
<condition property="sf.sessionId" value=""> <not> <isset property="sf.sessionId"/> </not> </condition>
<taskdef resource="com/salesforce/antlib.xml" uri="antlib:com.salesforce">
<classpath>
<pathelement location="C:\ant\ant-salesforce.jar" />
</classpath>
</taskdef>
<!-- Retrieve Profiles -->
<target name="retrieveProfiles">
<mkdir dir="retrieveProfiles"/>
<!-- Retrieve the contents into another directory -->
<sf:retrieve username="${sf.usernameStage}"
password="${sf.passwordStage}"
sessionId="${sf.sessionId}"
serverurl="${sf.loginurl}"
maxPoll="${sf.maxPoll}"
retrieveTarget="retrieveProfiles"
unpackaged="retrievepkg/package.xml"/>
</target>
<!-- Deploy Profiles -->
<target name="deployProfiles">
<!-- Upload the contents of the specified package -->
<sf:deploy username="${sf.usernameStage}"
password="${sf.passwordStage}"
sessionId="${sf.sessionId}"
serverurl="${sf.loginurl}"
maxPoll="${sf.maxPoll}"
deployRoot="${sf.pkgNameDeploy}"
singlePackage="true"
rollbackOnError="true"/>
</target>
</project>
Step two – retrieve metadata.
Next, we will retrieve the metadata for our Profiles
and the Case
object.
We do this by opening a command prompt, navigating to the folder our build.xml
file is contained in, and calling our action in ANT using the command ant retrieveProfiles
.

We will now have a directory, retrieveProfiles
, that is populated with the metadata from our organization’s profiles and the Case
object, as well as a copy of our package.xml
:

It is important to note that if there are special characters in any of your organization’s profile names, they will need to be manually updated in your filenames before they can be deployed back into Salesforce.
Step three – modify profile metadata.
Next, we must modify the Profile
metadata to update the object permissions. We will not be modifying the Case
object metadata.
Best practice dictates that we keep a copy of our original data in case we mess anything up, so what we are going to do is copy the contents of our retrieveProfiles
folder into our deploypkg
folder. That way, our originals are in the retrieveProfiles
folder, and the files we are modifying are in the deploypkg
folder.
Now, our deploypkg
folder should look like this:

We need all three pieces in order to modify our CRUDE permissions for our profiles.
When we open up our profiles
folder, we see a list of .profile
files, which look like this when you open them up:

Most of them go on for about 800 lines of code (or more, depending on how many objects you pulled). In these files are all of the User Permissions in our organization, each of the Field permissions for each object, and the Object Permissions for each object specified.
The nice thing about deploying with metadata is that the only changes that will occur is what we specify, so we can delete everything we don’t want to change. The easiest way to do this is to search for objectPermissions
, delete everything before the section we want using Ctrl+Shift+Home
while deselecting the first three lines, and delete everything after using Ctrl+Shift+End
while deselecting the last line.
After removing the unnecessary metadata, our metadata is down to a much more readable 13 lines:

You’ll notice that the name of the profile is not specified in the metadata, just the file name, so if you have a number of profiles that require the same object settings, you can modify one profile and either Save As the other profiles or copy paste into the .profile
files. It is important, however, to note that the custom
tags denoting whether a profile is Standard or Custom to the org must be included, so I prefer to copy paste.
Say I want to make the Case
object read only for all profiles. I would update the allowCreate
, allowDelete
, allowEdit
, and modifyAllRecords
permissions to false
, and then update the allowRead
and viewAllRecords
permissions to true
:

<?xml version="1.0" encoding="UTF-8"?>
<Profile xmlns="http://soap.sforce.com/2006/04/metadata">
<custom>false</custom>
<objectPermissions>
<allowCreate>false</allowCreate>
<allowDelete>false</allowDelete>
<allowEdit>false</allowEdit>
<allowRead>true</allowRead>
<modifyAllRecords>false</modifyAllRecords>
<object>Case</object>
<viewAllRecords>true</viewAllRecords>
</objectPermissions>
</Profile>
I would copy the objectPermissions
tags into all of the profiles I wanted to modify and save each file, being careful to leave the custom
tags for Standard profiles set to false
, and for Custom profiles set to true
.
Step four – deploy profile metadata.
After all of our profiles are modified, we will deploy our updated metadata.
Open a command line window, navigate to the directory containing your build.xml
file, and issue the command ant deployProfiles
.

(Optional) Step five – roll back changes.
In case you need to roll back your changes, whether due to human error or due to locking out users during a deployment, we can once again copy the contents of our retrieveProfile
folder into our deploypkg
folder.
Again, open a command line window, navigate to the directory containing your build.xml
file, and issue the command ant deployProfiles
.

Post-mortem
There you have it. A way to automate updating Object Permissions for multiple Profiles. It takes a minute to set up, but once you’ve done it, you can reuse and easily modify your data for multiple deployments across multiple staging, testing, and production environments.
Thanks for reading, let me know if you have any comments or questions!
-Evelyn, Another Salesforce Blog

Make a one-time donation
Make a monthly donation
Make a yearly donation
Choose an amount
Or enter a custom amount
Help keep Another Salesforce Blog on the internet by donating today!
Your contribution is appreciated.
Your contribution is appreciated.
DonateDonate monthlyDonate yearly
3 responses to “Mass Updating Profile Object Permissions Via ANT Migration Tool”
[…] have already covered the basics of how to do this in my previous post, Mass Updating Profile Object Permissions Via ANT Migration Tool. Unfortunately, manually updating profiles and permission sets is a labor intensive and error prone […]
LikeLike
Thank you very much for this tutorial, it is really helpful for those like me that are starting to use ANT.
I have an small question, if we deploy only the permissions for the case object, and the profiles i the destination org have permissions for also (let’s say) Account, Opportunity and others, would this remove their permissions on the Account and Opportunity objects because we are not mentioning these two objects in the package as well? or Salesforce will keep all the existing permissions and only add this one of the Case object?
Thank you very much!, you’re exellent at explaining this things.
LikeLike
Hello! As long as the permissions are not specified (eg, ONLY the case permissions are deployed, no others), the permissions for other objects will not be affected. I hope this helps!
LikeLike