Skip to content

Instantly share code, notes, and snippets.

@mattandneil
Last active November 24, 2020 22:37
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save mattandneil/94ff2f14b42c8d8e7d4e8008d591990c to your computer and use it in GitHub Desktop.
Save mattandneil/94ff2f14b42c8d8e7d4e8008d591990c to your computer and use it in GitHub Desktop.
Salesforce Org Snapshot - Ant Script
<macrodef name="snapshot" description="Snapshots all metadata in an organization - Revision 22">
<attribute name="username" />
<attribute name="password" />
<attribute name="serverurl" default="https://login.salesforce.com" />
<attribute name="outPath" default="temp/snapshot" description="Directory to write snapshot." />
<sequential>
<!-- prompt user to confirm -->
<input message="This snapshot overwrites the following directory. Are you sure?" validargs="@{outPath}" />
<!-- cache to avoid repeat logins -->
<local name="login.tmp" />
<tempfile property="login.tmp" prefix="login" suffix=".tmp" createfile="true" deleteonexit="true" />
<local name="describeMetadataResult.tmp" />
<tempfile property="describeMetadataResult.tmp" prefix="describeMetadataResult" suffix=".tmp" createfile="true" deleteonexit="true" />
<!-- determines org shape -->
<record name="${login.tmp}" action="start" />
<sf:describeMetadata serverurl="@{serverurl}" username="@{username}" password="@{password}" resultFilePath="${describeMetadataResult.tmp}" trace="true" />
<record name="${login.tmp}" action="stop" />
<!-- parse endpoint -->
<local name="loginUrl" />
<loadfile property="loginUrl" srcFile="${login.tmp}">
<filterchain><tokenfilter><filetokenizer/><replaceregex flags="gs" pattern=".*(https://[^/]+).*" replace="\1" /></tokenfilter></filterchain>
</loadfile>
<!-- parse session -->
<local name="sessionId" />
<loadfile property="sessionId" srcFile="${login.tmp}">
<filterchain><tokenfilter><filetokenizer/><replaceregex flags="gs" pattern=".*&lt;sessionId&gt;([^&lt;]+)&lt;/sessionId&gt;.*" replace="\1" /></tokenfilter></filterchain>
</loadfile>
<local name="metadataTypes.tmp" />
<tempfile property="metadataTypes.tmp" prefix="metadataTypes" suffix=".tmp" createfile="true" deleteonexit="true" />
<!-- clean metadata descriptions -->
<concat destFile="${metadataTypes.tmp}">
<fileset file="${describeMetadataResult.tmp}" />
<filterchain>
<linecontainsregexp><regexp pattern="ChildObjects|XMLName" /></linecontainsregexp>
<tokenfilter><replacestring from="," to="${line.separator}"/></tokenfilter>
<tokenfilter><replacestring from="ChildObjects: " to=""/></tokenfilter>
<tokenfilter><replacestring from="XMLName: " to=""/></tokenfilter>
<tokenfilter><replacestring from="*" to=""/></tokenfilter>
<tokenfilter><ignoreblank/></tokenfilter>
<sortfilter/>
</filterchain>
</concat>
<macrodef name="listMetadataForSnapshot">
<attribute name="loginUrl" />
<attribute name="sessionId" />
<attribute name="metadataType" />
<attribute name="folder" default="" />
<sequential>
<echo>Listing each @{metadataType} @{folder}</echo>
<local name="listMetadataResult.tmp" />
<tempfile property="listMetadataResult.tmp" prefix="@{metadataType}" suffix=".tmp" createfile="true" deleteonexit="true" />
<echo file="${listMetadataResult.tmp}" />
<sf:listMetadata serverurl="@{loginUrl}" sessionid="@{sessionId}" metadataType="@{metadataType}" resultFilePath="${listMetadataResult.tmp}" folder="@{folder}" />
<concat destFile="@{outPath}/package.xml" append="true">
<header filtering="false"><![CDATA[<types><name>@{metadataType}</name>]]></header>
<fileset file="${listMetadataResult.tmp}" />
<filterchain>
<linecontains><contains value="FullName/Id"/></linecontains>
<tokenfilter>
<replaceregex pattern="FullName/Id: ([^/]+)/([^/]+)/([^/]+)" replace="&lt;members&gt;\1/\2&lt;/members&gt;" />
<replaceregex pattern="FullName/Id: ([^/]+)\.([^/]+)/([^/]+)" replace="&lt;members&gt;\1.\2&lt;/members&gt;" />
<replaceregex pattern="FullName/Id: ([^/]+)/([^/]+)/" replace="&lt;members&gt;\1&lt;/members&gt;" />
<replaceregex pattern="FullName/Id: ([^/]+)/([^/]+)" replace="&lt;members&gt;\1&lt;/members&gt;" />
<replaceregex pattern="FullName/Id: ([^/]+)/" replace="&lt;members&gt;\1&lt;/members&gt;" />
</tokenfilter>
<tokenfilter>
<filetokenizer />
<scriptfilter language="javascript">with (new JavaImporter(java.net, java.io)) {
if ('@{folder}') {
self.setToken(self.getToken() +
project.getProperty('line.separator') +
'&lt;members&gt;@{folder}&lt;/members&gt;')
;}
}</scriptfilter>
</tokenfilter>
<sortfilter/>
</filterchain>
<footer filtering="false"><![CDATA[</types>]]></footer>
</concat>
</sequential>
</macrodef>
<macrodef name="listFolderForSnapshot">
<attribute name="loginUrl" />
<attribute name="sessionId" />
<attribute name="folderType" />
<attribute name="metadataType" />
<sequential>
<echo>Listing all @{folderType}s</echo>
<local name="listFolderResult.tmp" />
<tempfile property="listFolderResult.tmp" prefix="@{metadataType}" suffix=".tmp" createfile="true" deleteonexit="true" />
<echo file="${listFolderResult.tmp}" />
<sf:listMetadata serverurl="@{loginUrl}" sessionid="@{sessionId}" metadataType="@{folderType}" resultFilePath="${listFolderResult.tmp}" />
<loadfile srcFile="${listFolderResult.tmp}" property="file" />
<loadfile property="">
<fileset file="${listFolderResult.tmp}" />
<filterchain>
<linecontains><contains value="FullName/Id"/></linecontains>
<tokenfilter>
<replaceregex pattern="FullName/Id: " replace="" />
<replaceregex pattern="/.*" replace="" />
</tokenfilter>
<sortfilter/>
<scriptfilter language="javascript">
var macro = project.createTask('listMetadataForSnapshot');
macro.setDynamicAttribute('loginurl', '${loginUrl}');
macro.setDynamicAttribute('sessionid', '${sessionId}');
macro.setDynamicAttribute('metadatatype', '@{metadataType}');
macro.setDynamicAttribute('folder', self.getToken());
macro.execute(); //dynamic attributes are lowercase insistent
</scriptfilter>
<tokenfilter>
<replaceregex pattern="FullName/Id: " replace="" />
<replaceregex pattern="/.*" replace="" />
</tokenfilter>
</filterchain>
</loadfile>
</sequential>
</macrodef>
<!-- reset working directory -->
<delete dir="@{outPath}" />
<mkdir dir="@{outPath}" />
<!-- open package definition -->
<echo file="@{outPath}/package.xml"><![CDATA[<Package>]]></echo>
<!-- iterates over each metadata type -->
<loadfile property="" srcFile="${metadataTypes.tmp}">
<filterchain>
<!-- exclude folders and standard value sets -->
<linecontainsregexp negate="true">
<regexp pattern="StandardValueSet|Dashboard|Document|EmailTemplate|Report" />
</linecontainsregexp>
<scriptfilter language="javascript">
var macro = project.createTask('listMetadataForSnapshot');
macro.setDynamicAttribute('loginurl', '${loginUrl}');
macro.setDynamicAttribute('sessionid', '${sessionId}');
macro.setDynamicAttribute('metadatatype', self.getToken());
macro.execute(); //dynamic attributes are lowercase insistent
</scriptfilter>
</filterchain>
</loadfile>
<listFolderForSnapshot loginUrl="${loginUrl}" sessionId="${sessionId}" folderType="EmailFolder" metadataType="EmailTemplate" />
<listFolderForSnapshot loginUrl="${loginUrl}" sessionId="${sessionId}" folderType="ReportFolder" metadataType="Report" />
<listFolderForSnapshot loginUrl="${loginUrl}" sessionId="${sessionId}" folderType="DocumentFolder" metadataType="Document" />
<listFolderForSnapshot loginUrl="${loginUrl}" sessionId="${sessionId}" folderType="DashboardFolder" metadataType="Dashboard" />
<!-- shim standard value sets -->
<echo append="true" file="@{outPath}/package.xml"><![CDATA[
<types>
<name>StandardValueSet</name>
<members>AccountContactMultiRoles</members>
<members>AccountContactRole</members>
<members>AccountOwnership</members>
<members>AccountRating</members>
<members>AccountType</members>
<members>AddressCountryCode</members>
<members>AddressStateCode</members>
<members>AssetStatus</members>
<members>CampaignMemberStatus</members>
<members>CampaignStatus</members>
<members>CampaignType</members>
<members>CaseContactRole</members>
<members>CaseOrigin</members>
<members>CasePriority</members>
<members>CaseReason</members>
<members>CaseStatus</members>
<members>CaseType</members>
<members>ContactRole</members>
<members>ContractContactRole</members>
<members>ContractStatus</members>
<members>EntitlementType</members>
<members>EventSubject</members>
<members>EventType</members>
<members>FiscalYearPeriodName</members>
<members>FiscalYearPeriodPrefix</members>
<members>FiscalYearQuarterName</members>
<members>FiscalYearQuarterPrefix</members>
<members>IdeaCategory</members>
<members>IdeaMultiCategory</members>
<members>IdeaStatus</members>
<members>IdeaThemeStatus</members>
<members>Industry</members>
<members>InvoiceStatus</members>
<members>LeadSource</members>
<members>LeadStatus</members>
<members>OpportunityCompetitor</members>
<members>OpportunityStage</members>
<members>OpportunityType</members>
<members>OrderStatus</members>
<members>OrderType</members>
<members>PartnerRole</members>
<members>Product2Family</members>
<members>QuestionOrigin</members>
<members>QuickTextCategory</members>
<members>QuickTextChannel</members>
<members>QuoteStatus</members>
<members>RoleInTerritory2</members>
<members>SalesTeamRole</members>
<members>Salutation</members>
<members>ServiceContractApprovalStatus</members>
<members>SocialPostClassification</members>
<members>SocialPostEngagementLevel</members>
<members>SocialPostReviewedStatus</members>
<members>SolutionStatus</members>
<members>TaskPriority</members>
<members>TaskStatus</members>
<members>TaskSubject</members>
<members>TaskType</members>
<members>WorkOrderLineItemStatus</members>
<members>WorkOrderPriority</members>
<members>WorkOrderStatus</members>
</types>
]]></echo>
<!-- close package definition and retrieve -->
<echo append="true" file="@{outPath}/package.xml"><![CDATA[</Package>]]></echo>
<sf:retrieve serverUrl="${loginUrl}" sessionId="${sessionId}" unpackaged="@{outPath}/package.xml" retrieveTarget="@{outPath}" />
</sequential>
</macrodef>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment