Cleaning salesforce profiles before deployment

That's quite common case when you face the following errors during deployment:

profiles/Admin.profile — Error: Unknown user permission: EditBillingInfo</span>
profiles/Admin.profile — Error: Unknown user permission: ManageSandboxes</span>
profiles/Admin.profile — Error: Unknown user permission: SocialInsightsLogoAdmin</span>
profiles/Admin.profile — Error: Unknown user permission: EditPublicReports
profiles/Admin.profile — Error: Unknown user permission: DataExport

Usually it means you trying to deploy the org which has no features which source org has. It might be a case when you deploy from Production to Sandbox or from some production org to developer edition. All what you can do with this error is to clean up profiles. Usually you do that by hand in any text editor. But I decided to build a small script in python which will do all the stuff automatically during deployment.

import os
from xml.etree import cElementTree as et

dir = '../src/profiles/'
nodes_to_remove = ('EditBillingInfo', 'ManageSandboxes', 'SocialInsightsLogoAdmin')

def clean_profile():
    et.register_namespace(, "http://soap.sforce.com/2006/04/metadata")
    tr = et.parse(xml_file)
    root = tr.getroot()
    for child in root:
        if 'userPermissions' in child.tag:
            for element in child:
                for node in nodes_to_remove:
                    if 'name' in element.tag and node in element.text:
                        root.remove(child)
                        break
    return root

if __name__ == '__main__':
    for filename in os.listdir(dir):
        with open(dir + filename, 'r+b') as xml_file:
            result = clean_profile()
            xml_str = et.tostring(result, encoding='utf-8′, method='xml')
            xml_file.seek(0)
            xml_file.truncate()
            xml_file.write(b'<?xml version="1.0" encoding="UTF-8"?>\n' + xml_str)

as you can see you just need to add properties which you want to remove into nodes_to_remove

Usually I put all scripts in scripts directory which is on the same level as src directory is, but if you want to locate the script in any other location you might need to fix dir variable for correct path to src/profiles

and here's a build.xml part which consumes this script

<target name="deploySandbox" description="Run cleaning scripts (for profiles) and upload source code to Sandbox" >
    <echo message="••••••••••••••••••••••• SANDBOX DEPLOY •••••••••••••••••••••••" />
    <echo message="Step 1: PROFILES CLEANING:" />

    <exec dir="scripts" executable="python3" failonerror="true">
        <arg line="profile_cleaner.py results.log" />
    </exec>

    <echo message="———————————————————————————————" />
    <echo message="Step 2:" />
    <echo message="Deploy with User Name: ${sf.username} To ${sf.serverurl}" />
    <!– Upload the contents of the "src" directory and run all test >
    <sf:deploy username="${sf.username}"
        password="${sf.password}"
        serverurl="${sf.serverurl}"
        deployRoot="src"
        runAllTests="true"
        maxPoll="3000"
        pollWaitMillis="150"
        testLevel="RunLocalTests">
    </sf:deploy>
    </target>
[ant][ant migration tool][automation][python][script][scripts]