[SalesForce] Creating/updating NamedCredential from Apex

Question

Is it possible to create or update a NamedCredential from within Apex?

Investigation

I looked into available options, and it seems the SOAP API only supports the describeSObjects(), query(), and retrieve() operations. The Tooling API doesn't seem to support the object at all. But the Metadata API does!

Let's play around with some scripts in Execute Anonymous and see what we can do with the Apex Wrapper Salesforce Metadata API.

Working with the Metadata API

Helper Methods

Here are some bare-bones helper methods pulled out of the MetadataServiceExamples class:

static MetadataService.MetadataPort createService()
{
    MetadataService.MetadataPort service = new MetadataService.MetadataPort();
    service.SessionHeader = new MetadataService.SessionHeader_element();
    service.SessionHeader.sessionId = UserInfo.getSessionId();
    return service;
}
static List<MetadataService.SaveResult> createMetadata(MetadataService.Metadata input)
{
    return createService().createMetadata(new List<MetadataService.Metadata> { input });
}
static List<MetadataService.SaveResult> updateMetadata(MetadataService.Metadata input)
{
    return createService().updateMetadata(new List<MetadataService.Metadata> { input });
}
static List<MetadataService.DeleteResult> deleteMetadata(String type, String fullName)
{
    return createService().deleteMetadata(type, new List<String> { fullName });
}

NamedCredential

I tried to run a NamedCredential through these methods and was not able to get createMetadata or updateMetadata to work. The errors are included below. To my surprise, however, deleteMetadata runs successfully if the other methods are left out. I don't see any mention in the documentation of which operations are supported…

Script

MetadataService.NamedCredential credential = new MetadataService.NamedCredential();
credential.fullName = 'Demo_Credential';
credential.label = 'Demo Credential';
credential.endpoint = 'https://www.DEMO2.com';

system.debug(createMetadata(credential));
system.debug(updateMetadata(credential));
system.debug(deleteMetadata('NamedCredential', credential.fullName));

Error on createMetadata

Line: 11534, Column: 1
System.CalloutException: Web service callout failed: WebService returned a SOAP Fault: '' is not a valid value for the enum 'ExternalPrincipalType' faultcode=soapenv:Client faultactor=

Error on updateMetadata

Line: 11364, Column: 1
System.CalloutException: Web service callout failed: WebService returned a SOAP Fault: '' is not a valid value for the enum 'ExternalPrincipalType' faultcode=soapenv:Client faultactor=

ApexPage

Just to make sure my installation works correctly, I slightly tweaked some examples from the MetadataServiceExamples class. The createMetadata(), updateMetadata(), and deleteMetadata() calls each work fine for the ApexPage type.

Script

MetadataService.ApexPage apexPage = new MetadataService.ApexPage();
apexPage.apiVersion = 25;
apexPage.fullName = 'test';
apexPage.label = 'Test Page';
apexPage.content = EncodingUtil.base64Encode(Blob.valueOf('<apex:page/>'));

system.debug(createMetadata(apexPage));
system.debug(updateMetadata(apexPage));
system.debug(deleteMetadata('ApexPage', apexPage.fullName));

Abbreviated Logs

(SaveResult:[errors=null, fullName=test, success=true])
(SaveResult:[errors=null, fullName=test, success=true])
(DeleteResult:[errors=null, fullName=test, success=true])

Best Answer

You need to add two required "fields" to your request: principalType and protocol (you can see the entire list of fields here)

Script

MetadataService.NamedCredential credential = new MetadataService.NamedCredential();
credential.fullName = 'Demo_Credential';
credential.label = 'Demo Credential';
credential.endpoint = 'https://www.DEMO2.com';
credential.principalType = 'NamedUser';
credential.protocol = 'NoAuthentication';

system.debug(createMetadata(credential));
system.debug(updateMetadata(credential));

Abbreviated logs

(SaveResult:[errors=null, fullName=Demo_Credential, success=true)
(SaveResult:[errors=null, fullName=Demo_Credential, success=true)

Related Topic