[SalesForce] Update Custom Metadata with Apex in the org

In the documentation it says

Apex code can create, read, and update (but not delete) custom metadata records

No where in the guide do I see apex being used to update custom metadata, only to create and read. I'd like to update the custom meta data in my org with apex. Are you only able to edit custom meta data if it's in another org and part of a managed packaged?

Best Answer

The Metadata.Operations class gives the ability to update/retrieve/insert metadata.

To answer your question about updating Custom Metadata, you'd be using the Metadata.Operations.enqueueDeployement();

I've provided the apex class you can create in your own org to reproduce.

1st steps: Create a Custom Metadata Type. If you'd like to use the code I provide verbatim, then follow these steps

  1. Create Custom Metadata Type, name it "Test Custom Metadata".
  2. Create a custom field named "Custom Field 1" of type Text.. api name would be Custom_Field_1__c
  3. Click on Manage Test Custom Metadata(create a custom metadata record), and enter in the values for label : "Before_Change", that will populate the name with Before_Change as well. Enter in any text for the Custom_Field_1__c, for me, I put 'Testing before changes'.(This will be updated once the code is run)
  4. Your custom metadata is all set up, and now it's time to create the apex class
  5. Create an Apex Class named MetadataDeploy
  6. Populate the following apex class with the code below.
public without sharing class MetadataDeploy implements Metadata.DeployCallback{

    public List<FieldWrapper> listField{get;set;}
    public class FieldWrapper
    {
        public String fieldName {get; set;}
        public String fieldAPIName {get; set;}
    }
   
    public static String updateCustomMetadata()
    {
        try {
        Test_Custom_Metadata__mdt tcm = getMetaDataRecords()[0]; //retrieving custom metadata records, using the first.
        Id customMetadataId = tcm.Id;
        system.debug('CustomMetadata Id ' + customMetadataId);
        Metadata.CustomMetadata customMetadata = new Metadata.CustomMetadata();
        customMetadata.fullName = 'Test_Custom_Metadata.Before_Change'; //custom metadata name
        customMetadata.label = 'Before_Change';
        // customMetadata.id = customMetadataId;
        Metadata.CustomMetadataValue customField1 = new Metadata.CustomMetadataValue(); //the values you're changing/updating
        customField1.field = 'Custom_Field_1__c'; //the custom field API Name that you're wanting to insert/update a value of
        customField1.value = 'FIeld Test 1 after';
        customMetadata.values.add(customField1);//add the changes to list of changes to be deployed
       
        // if(customMetadataId != null) //if a custom metadata record exists, add the id here
        // {
        //     Metadata.CustomMetadataValue customId = new Metadata.CustomMetadataValue();
        //     customId.field = 'Id';
        //     customId.value = customMetadataId;
        //     customMetadata.values.add(customId);
        // }
        
        Metadata.DeployContainer mdContainer = new Metadata.DeployContainer();
        mdContainer.addMetadata(customMetadata);
        
        MetadataDeploy callback = new MetadataDeploy();
        // Id jobId = Metadata.Operations.enqueueDeployment(mdContainer, null);
        
        Id jobId = Metadata.Operations.enqueueDeployment(mdContainer, callback);
        
        system.debug('jobId is ' + jobId);
        
        // Metadata.DeployContainer metaDeploy1 = new Metadata.DeployContainer();
        // metaDeploy1.addMetadata(approvalConfigToInsert);
        // Id AsynchResult1 = Metadata.Operations.enqueueDeployment(metadeploy1, null);
        // system.debug('jobId ' + mdContainer);
        return 'Success';
            
        } catch (Exception e) {
                return e.getMessage();
        }
        }
        public static list<Test_Custom_Metadata__mdt> getMetaDataRecords()
        {
            list<Test_Custom_Metadata__mdt> tcm = [Select Id, label FROM Test_Custom_Metadata__mdt];
            return tcm;
        }
       
    
        // find all sObjects available in the organization
        public void handleResult(Metadata.DeployResult result, Metadata.DeployCallbackContext context) {
            if(result.status == Metadata.DeployStatus.SUCCEEDED)
            {
                //success
                System.debug(' ===>'+ result);
            }else{
                //not success
                System.debug(' ===> fail '+ result);
            }
            
        }
}

Open up your developer console, and execute MetadataDeploy.updateCustomMetadata();

You can check the results in deployment status, or the logs where it says QueueableHandler.

Check the Custom Metadata Record and see if it updated to 'FIeld Test 1 after', if it did, it successfully deployed the change.

TLDR: You can update Custom Metadata by using the Metadata.Operations.enqueueDeployment(metadataContainer, callback). The metadataContainer is basically setting up the package to deploy, which would be Metadata.CustomMetadata, filling out the values that you want to change (Metadata.CustomMetadataValue), and placing it in the metadata container(Metadata.DeployContainer.add(customMetadata). The callback is a way for the results to be handled, so if it failed or succeeded, you can get a response back.