Start tracking your progress
Trailhead Home
Trailhead Home

Build Automated Metadata Updates for Multi-Org Deployment

Learning Objectives

After completing this unit, you’ll be able to:
  • Use Apex Metadata API to retrieve metadata from an org.
  • Use Apex Metadata API to update metadata and deploy into an org.

Solve a Common Multi-Org Installation Issue

OK, time to solve a common problem that you’ve probably faced if you work in a multi-org environment. Let’s say you have 10 orgs, one for each of the countries in which you do business. You use managed packages to update the orgs. You’ve created a custom field for Accounts, and you want it to show up on the page layouts in all your orgs.

Ideally, you want to make these updates without creating repetitive work in the Setup UI for your admins. Let’s see how we can automate the process by using Apex Metadata API.

We walk through Apex code that retrieves the existing metadata for the Account page layout and updates it with the new field. Then we create an Apex post install script that deploys the metadata update. Instead of having to add our field to the Account page layout in each of our orgs, we simply upgrade our managed package in our orgs. Apex does all the work to add the new field behind the scenes.

Retrieve Metadata and Add a Custom Field to a Layout

Here’s some example code that retrieves the current metadata for the Account page layout. Then it uses the metadata to locate the Account Information section of the page layout. Finally, it creates a metadata layout item for the custom field and adds it to the list of metadata layout items in this section.

  1. Open the Developer Console by clicking the Setup gear, then Developer Console.
  2. In the Developer Console, click File | New | Apex Class.
  3. Name the class UpdatePageLayout and click OK.
  4. Get rid of any existing code, and copy and paste the following example code in its place.
public class UpdatePageLayout {
    // Add custom field to page layout
    
    public Metadata.Layout buildLayout() {
        
        // Retrieve Account layout and section 
        List<Metadata.Metadata> layouts = 
            Metadata.Operations.retrieve(Metadata.MetadataType.Layout, 
            new List<String> {'Account-Account Layout'});
        Metadata.Layout layoutMd = (Metadata.Layout) layouts.get(0);
        Metadata.LayoutSection layoutSectionToEdit = null;
        List<Metadata.LayoutSection> layoutSections = layoutMd.layoutSections;
        for (Metadata.LayoutSection section : layoutSections) {
            
            if (section.label == 'Account Information') {
                layoutSectionToEdit = section;
                break;
            }
        }
        
        // Add the field under Account info section in the left column
        List<Metadata.LayoutColumn> layoutColumns = layoutSectionToEdit.layoutColumns;     
        List<Metadata.LayoutItem> layoutItems = layoutColumns.get(0).layoutItems;
        
        // Create a new layout item for the custom field
        Metadata.LayoutItem item = new Metadata.LayoutItem();
        item.behavior = Metadata.UiBehavior.Edit;
        item.field = 'AMAPI__Apex_MD_API_sample_field__c';
        layoutItems.add(item);
        
        return layoutMd;
    }
}

This example uses classes and methods from Apex Metadata API, which can be found in the Metadata namespace. The Metadata.Operations.retrieve method retrieves metadata from the org synchronously. We provide a list of metadata component names to retrieve. In this case, we’re retrieving metadata for the Account page layout. The retrieve method returns a list of matching component data, represented by component classes that derive from Metadata.Metadata.

Note

Note

To use this class, replace 'AMAPI__Apex_MD_API_sample_field__c' with your namespace and field name.

For more information on classes in the Metadata namespace, see the Apex Developer Guide.

Provide a Callback Class

Now that we’ve created the layout field metadata, we get ready to deploy the metadata to our orgs. Because the deployment is asynchronous, we provide a callback so that we are notified when the queued deployment completes. The callback class must implement the Metadata.DeployCallback interface.

Note

Note

Because the callback is called asynchronously after deployment, there can be a brief period where the deployment has completed, but the callback hasn’t been called yet.

From the Developer Console, create an Apex class with the name PostInstallCallback and the following code.

public class PostInstallCallback implements Metadata.DeployCallback {
  
    public void handleResult(Metadata.DeployResult result,
        Metadata.DeployCallbackContext context) {
        
        if (result.status == Metadata.DeployStatus.Succeeded) {
            // Deployment was successful, take appropriate action.
            System.debug('Deployment Succeeded!');
        } else {
            // Deployment wasn’t successful, take appropriate action.
	    System.debug('Deployment Failed!');
        }
    }
}

Create a Deployment Container

Apex Metadata API provides the Metadata.Operations.enqueueDeployment method to deploy metadata to the current org. When this method is called, the deployment request is queued for asynchronous processing.

Note

Note

When deploying metadata, you can create and update components, but you can’t delete them.

Create an Apex class with the name DeployMetadata using the following code. This class creates a deployment container and adds the new layout metadata to it. It instantiates our callback class, which is called when the deployment completes. It then calls the Metadata.Operations.enqueueDeployment method to deploy our new metadata to the org.

public class DeployMetadata {
 
    // Create metadata container 
    public Metadata.DeployContainer constructDeploymentRequest() {
        
        Metadata.DeployContainer container = new Metadata.DeployContainer();
        
        // Add components to container         
        Metadata.Layout layoutMetadata = new UpdatePageLayout().buildLayout();
        container.addMetadata(layoutMetadata);
        return container;
    }
    
    // Deploy metadata
    public void deploy(Metadata.DeployContainer container) {
        // Create callback. 
        PostInstallCallback callback = new PostInstallCallback();
        
        // Deploy the container with the new components. 
        Id asyncResultId = Metadata.Operations.enqueueDeployment(container, callback);
    }
}

Deploy the Metadata Asynchronously

Now that we’ve written the Apex classes we need, we can deploy our custom metadata. We initiate the deployment from a post install script, which is an Apex class that implements the InstallHandler interface. If a package contains a post install script, the script is run automatically after the package is installed or upgraded. From the Developer Console, create the PostInstallScript Apex class.

public class PostInstallScript implements InstallHandler {
    
    // Deploy post-install metadata  
    public void onInstall(InstallContext context) {
        DeployMetadata deployUtil = new DeployMetadata();
        Metadata.DeployContainer container = deployUtil.constructDeploymentRequest();
        deployUtil.deploy(container);
    }
}
Note

Note

You can retrieve and deploy metadata in post install scripts. However, in uninstall scripts, you can retrieve, but not deploy, metadata from Apex code.

That’s it! When you upgrade your package in each of your orgs, this script adds your new field to all the page layouts. You (and your admins) don’t have to make a single change in the Setup UI. In the next unit, we look at a different scenario. What happens when you’ve created a custom metadata type that has to be configured and deployed into multiple orgs?

But first, give it a try. Complete the following challenge to test your knowledge.

Resources

retargeting