Maciej Ptak
written byMaciej Ptak
posted on August 6, 2022
DevOps Engineer/Consultant. Specialized in Salesforce and CICD tooling around it. Focused on removing the bottlenecks and streamlining the Salesforce delivery process since 2017.

Salesforce Functions in CICD

Hi there!

Salesforce Functions CICD

A few words about Salesforce functions

Recently Salesforce enabled the option to create a compute environment related 1:1 with Salesforce org or Scratch org.

The main objective is to delegate some functions to another place than org and a cool thing is you can write code either in Java, JavaScript or TypeScript.

I won't elaborate on the benefits or use cases here as there are already plenty of other articles where it is explained, like here in this official document

But in short, it is a Function as a Service, similar to AWS Lambda if you are familiar with this (but not that cool as Lambda though)

In this post, I will focus more on how to deploy functions rather than how to write them and use them within the apex. But another article addressing this topic may be created in the future so don't forget to subscribe to our blog - https://beyondthecloud.dev/blog

So let's say you had on hands experience with this, locally is working just fine but you want to include that as part of your CICD and use it in sandboxes and Production in a standardised way.
Here I am, I have been there and there are some caveats which I will disclose so you have a smooth implementation.

Salesforce Functions implementation

Salesforce Functions Prerequisites

  1. Make sure you have licences for functions in scratch orgs and sandboxes (assuming you already got this in Production - free trial https://functions.salesforce.com/signups/)
    1. In your org, from Setup, enter Functions in the Quick Find box and select Functions. In sandboxes toggle on "Enable Test Space". In Production, you will have 2 switches, for Sandboxes and Prod: "Enable Production Space"
    2. Scratch org feature for functions in config file config/project-scratch-def.json is added before creating an actual scratch org. Under the features list just add "Functions".
    3. Click Match Production Licenses in sandboxes, you can find it in Setup > Company Information
  2. Get the right permissions
    1. Once licenses are there, you can assign appropriate permission to your user and integration user (used for CICD).
    2. Perm set will be called functions but you can Edit it and add Compute Access in the System permissions section. There is also separate permission Compute Production Access. Otherwise, just add those permissions to any other permission set or profile.

So at this point, you should be all set and ready to create some compute environments. It should be easy, right? Right??
To some extent, yes.

Salesforce Functions Deployment

Regardless you are going to use Functions in Scratch Org / Sandbox / Production you should always start with points #1 and #2
Work in a git repository with DX project.
You cannot deploy only specific functions/part of functions. You will always deploy the whole functions folder.

In my case, a Production environment is set to be a DevHub.

  1. Authorize to DevHub org with Salesforce Functions enabled
    1. sf login org --alias DEV_HUB_ORG_NAME --set-default-dev-hub
  2. Connect Your CLI Environment to Salesforce Functions (login with dev_hub_org credentials)
    1. sf login functions
  3. Create a Compute Environment to deploy the functions and connect it to your designated org:
    1. sf env create compute --connected-org=TARGET_ORG_NAME --alias=COMPUTE_TARGET_ENV_ALIAS
    2. sf env list
  4. If you don't have some functions in your local repository, create one
    1. sf generate function -n myfunction -l javascript
  5. Commit changes
    1. There should be no changes that are pending to be committed in a whole repo, not only the functions folder.
    2. git status should return "nothing to commit, working tree clean"
    3. Otherwise Functions deployment will fail
  6. Deploy the functions
    1. sf deploy functions --connected-org=TARGET_ORG_NAME
  7. View Compute Environment Info and Logs
    1. sf env display -e COMPUTE_TARGET_ENV_ALIAS
    2. sf env log tail -e COMPUTE_TARGET_ENV_ALIAS

Integrating Salesforce Functions with CICD

Issues I have encountered

We will reuse the commands listed above but with little modifications.

  • WARNING: There is/was a bug that when you log in to org and functions for the first time, you need to do it with the standard UI way (without jwt) to accept terms & conditions. It is a one-time action. If you have API only user you need to remove that permission for a moment.
  • Use a connected app to authenticate with Dev Hub and functions
    • sf login org jwt --username DEV_HUB_USERNAME --keyfile pathToServer.key --clientid CONNECTED_APP_CLIENT_ID --set-default-dev-hub --alias DEV_HUB_ORG_NAME
    • sf login functions jwt --username DEV_HUB_USERNAME --keyfile pathToServer.key --clientid CONNECTED_APP_CLIENT_ID --set-default-dev-hub --alias DEV_HUB_ORG_NAME
  • Create compute environment using the CICD already.
    • It may already be fixed but I got an issue when I created compute environment in my local Visual Studio Code using the same user and same authentication method as CICD. However, this was failing due to some unknown exception. When I deleted compute environment and created one within the CICD process (in Jenkins pipeline directly) it worked.
  • Checkout to branch, not the detached head
    • When using Jenkins I came across an issue where I couldn't deploy function as I wasn't on a git branch, but a detached head. This is specific to the orchestrator you are using so that may not be an issue for you. The fix was to use this extension for the checkout method:
      extensions: [[$class: 'LocalBranch',
      localBranch: "${branchToCheckout}"]
  • It is recommended to first deploy functions and then deploy code. In my CICD I am doing a local merge, deploying the files that were modified, and when deployment is successful I am pushing that local merge to remote. Functions require a local branch without a detached head and that was my limitation. I moved the deployment of the functions after the deployment of the main code.
  • There is a limit to Compute Environments you can create. It wasn't documented but our license turned out to have a max of 25 compute environments. We hit a limit as the Scratch Org limit was 100. A workaround was to check in the pipeline if the functions folder was modified or needed at all to check the deployment. And we always delete compute environment and scratch org separately at the end of the job. When looking at the list of computing environments I saw some orphaned compute environments which is why I delete them separately.

    Salesforce Functions Implementation steps

    So with these modified commands, in CICD you will end up with the following steps depending on the target environment:

  • Scratch Org: #1, #2, #3, #4, #6
  • Sandboxes and Production: #1, #2, #5, #6

If you have some questions feel free to ask in the comment section below. 🙂

Was it helpful? Check out our other articles here.


References

Buy Me A Coffee