Global variables/constants/configuration for your Function Libraries and BPMs

Has anyone come up with a clever way to store global variables or constants or configuration values that would allow scripting changes to them between prod/test environments, but still avoid hard-coding them all over the place in your BPMs and functions? Something like you’d do with global consts or configuration files if you were doing a standalone program.

My current example: calling a webhook across a few different Epicor Functions, and I want the URL for the webhook to be something that isn’t hard-coded into the function libraries.

Some Grok / Claude generated ideas are doing things like adding UD columns to Company and storing things there, or creating one Epicor Library function - for my example something like MyLibrary.GetConfig_WebhookURL() - but I’m still not wanting the value hard-coded in that method, even if it is only one place to update when copying production database over to test.

But I figured surely some smart folks on here already have tried some nifty things and might be able to point me in a good direction, even if there isn’t anything official from Epicor for this?

We have a couple sets of User Codes for storing things like that. They can be easily changed between environments and aren’t hardcoded onto tables like a UD field is.

7 Likes

Instance Management is something we’ve talked about on many occasions here. At one point it was suggested that it would be useful to have instance information stored outside of all instances. At start up, the AppServer would get the settings for that instance (passing the AppServerURL) and cache them for future calls. These settings could include all of the usual suspects:

  • Company Name
  • Color scheme
  • External services turned off or moved to another endpoint
  • Set Production Flag on or off
  • etc.
1 Like

So you have like a “CONFIG” UserCodeType and then you put the Key / Value pairs into like the CODE and Description fields?

Yep, that’s what we do. We have a couple of them for more specific apps that need multiple settings. We group those under a user code for the app. IE “SF” is for our Salesforce integration.

1 Like

We use a UD table to define variables based on the keys. We have an EFx that we use to grab that data. Part of our refresh script updates those fields to make the code work in Test.

EDIT: The only part that’s a problem is sometimes you have to define it for every company you need it for. Not a problem for most, but something you have to watch out for.

3 Likes

If you use UDCodes or a UDTable, is Production the Master then? New or updated keys are updated there before moving to another instance?

I’m on a campaign to have smaller dev instances that aren’t copies of live data. That’s why I was looking to pull it in from a central store. :person_shrugging:

I have a script to update them after copying to another environment. If it’s something new I add the user codes to the dev environment then create in prod when the custom is ready to go live.

**I’m talking about doing it with user codes. We don’t use UD codes/tables for this.

1 Like

You use production data for test then? I know everybody does, but I think @Doug.C might have some PII/PHI/HIPAA requirements.

No. We have test environments we connect the dev instances to. That’s why we update the user codes.

1 Like

Right. Your script is the “external” source. Like it.

I still would prefer something better similar to what you’re talking about here.

1 Like

And don’t forget it is possible to use the is production widget condition. Definately user codes can be levered for this as others have mentioned, and you can swap the values as part of your live to other environment process. You could even do a mix of both and store the test an live values on user codes with the condition if you did not want to update data… The world is your oyster…
Sponge Bob GIF

1 Like

That thought actually did occur to me, to use conditional for prod vs test, but I still like the idea of not hard-coding things like a webhook URL, so the combination idea you bring up still would be great @Hally. I maybe over-emphasized the production vs test environment variables part of this - there also are certainly use cases where the value might be the same in prod and test, but I still want to be able to change it without modifying code (function or BPM). Thanks all! Probably going to take a stab at the UDCodes idea unless someone else has a brilliant idea.

Grok AI mentioned something about the Ice.BO.ConfigValue business object/service, and Claude AI said something about Ice.Tables.SysConfig, but I think they might have both been hallucinating.

sysconfig does exist… But I would be steering away from them as they don’t have a UI and Epicor play with them storing things like help URLs etc.

If you were after configuration items that you didn’t want users changing then the other alternative would be to put them on company and lock them up nice and tight. perhaps with their own panel or panel card for custom settings.

I would avoid adding UD fields to the company table as this is one of the most updated areas during upgrades (e.g CSF changes) - I would suggest UD Codes or (if absolutely necesssary) UD tables to hold the data that you need to pull in

1 Like

We do the same with User Codes. We added a few extra nvarchar x(1000) max ud fields to user codes as well so we can store larger data.

1 Like

Do you guys treat 3rd party service credentials any differently or stuff them into User Codes, etc. as well? For example you have a Function library making API calls to an external system and so you store those keys in User Codes so they aren’t hardcoded…

I’ve always just put them into User Codes with the other environmental variables, but I imagine there is a more secure way to do this.

1 Like

<Insert @Mark_Wonsil idea here>

We store those in a user code right now. We don’t have any usernames or passwords - that I can think of. We have some API keys though.

The popular method is to use a credential vault like:

Azure Key Vault
Keeper Secrets Manager
HashiCorp Vault

The trick is how do you grant access to the secret based on the user context and not the context of appServer? If I give access to the appServer id, then anyone can write a function that can call an external service. The same is true for UDCodes or hardcoding credentials.

The current solution is to use a Backend For Frontend (BFF). This endpoint sits between the user and the service and it authenticates the user, gets the credential on behalf of the user, and then the BFF calls the service. The keys are never available at the client nor stored at the server.

2 Likes