Is there any way to get the environments connection string from BPM/Epicor function?

I am on public cloud, and have 3 environments; production, pilot, and an additional third environment. I am trying to figure out a way to retrieve the environment connection string(ie. https://xxx.epicorsaas.com/SaaSxxx) from inside a BPM and Epicor Function. The closest I’ve found is the ‘this.GetEnvironmentType()’ which tells me I’m in production or not production but won’t tell me which non-production environment I’m in. I have code that I want to behave differently depending on the environment its run in and we often get our third and pilot environments refreshed with a copy of our production. I’d like to put conditional logic on the connection string so I don’t have to keep opening it up each time there’s a refresh.

it should be a part of the Session object in functions and BPMs.

try using this.Session.AppServerUrl

If your 3rd environment has a specific company name, you could test for that.

We always change the company name so that if something is printed from it wit would show like:
!!! TEST - Acme Industries !!!

1 Like

Thanks Joseph, that works for BPMs. Any advice however on trying to get this info from an Epicor function as it doesn’t have a session?

And Calvin, while that would be nice, since we overwrite the environments, it will be the same as live unless someone explicitly changes it after each overwrite. We also have multiple companies so it really needs to be on the environment level.

1 Like

Changing the Company Name was part of our copying procedure.

And the more I think about it, I think there was a way to determine the environment, from the server name. I’ll dig around a bit.

Fond this line of code from @jgiese.wci

if(Db.ConnectionString.Contains(“MyProductionDB”)){
    //is production
    }

Where `MyProductionDB’ is the name of the DB in SSRS. I’m assuming your non-production environments don’t share the DB name with your production environment’s DB.

We use the following in our bpms

string[] server = Session.AppServerURL.Split(’/’);

switch (server[3].ToUpper())
{
    case "PRODUCTION":
        Database = string.Empty;
        break;
    case "TRAINING":
        Database = "Train";
        break;
    case "TEST":
        Database = "Test";
        break;
    case "PILOT":
        Database = "Pilot";
        break;
    default:
        Database = string.Empty;
        break;
}

Thats interesting, I did not notice that the Session object does not get the app server url set in a function.

If you only need the server instance, you can use System.Environment.MachineName or System.Net.Dns.GetHostName() for that. Not sure how to get the app server instance name but maybe the server name will be enough for what you want?

Session property in EFx has the same properties like in the current BPM (defined by ISession interface). And AppServerURL is there.

However, it is caller responsibility to provide this information. When you use UI (classic or web-based) it provides such data. So, when you use EFx from BPM, then property should be populated. If not then it is a bug.

But if you call same function with REST it is your responsibility to provide a header with this information. Don’t remember which exactly.

Thanks for that info Sergey. the thing I’m trying to do is create an EFx library to essentially house logic to determine the environment and then call that Efx function from other functions that are doing work to determine things like the base urls for rest calls. At no point does it hit an actual BPM so the session.AppServerUrl is blank. Is there any other way of determining which environment you’re in solely in EFx functions?

I tried Joseph_Martins suggestion of System.Environment.MachineName or System.Net.Dns.GetHostName(), but as we are public cloud I find that this value varies occasionally across various machines. I believe it probably has something to do with the cloud datacenter provisioning or something.

That makes sense, I was testing the function call with Postman / Rest and no session / context headers being sent in.

We have a special condition that checks an environment (Product/Non-Product). But it cannot determine kinds of non-production environments. FW does not provide such API.

Probably you could ask SaaS team to set up some environment variable and use it from C# code/expression. But I don’t know whether thay make such changes or not.

In the Client Functions library, there is a GetProductionServerInstances service…

Not sure how the Instance Description is set though…

I have used User Codes before to store various environmental variables that BPMs and Customizations would look at… 3rd party database connection strings, what the “from” address shows on email alerts, BarTender-Commander watch folder unc paths… all kinds of whacky stuff.

Not terribly elegant but it worked very well operationally. After overwriting Test/Dev I just needed to spend 2 minutes updating the User Codes – before setting that up a Test overwrite would take hours to hunt down all the environmental variables scattered throughout BPMs and Customizations.

1 Like

I have a GetList BAQ BPM that I run from a BAQ Export Process on a nightly schedule that sends emails. I ran into a snag because the Session.AppServerUrl is different when running from the scheduled process or running the BAQ manually. This might be because the client is a cloud customer, not sure, but I want my solution to live anywhere. This brought me to the following solution:

//Environment Detection
string env = ""; 
foreach(string prop in Db.ConnectionString.Split(';')){
  string[] p = prop.Split('=');
  
  if(p[0] == "Initial Catalog"){
    switch(p[1]){
      case "SaaS###_######": env = "prod"; break; 
      case "SaaS###Pilot_######": env = "pilot"; break; 
    }
  }
}
var mailer = this.GetMailer(async: true);
var message = new Ice.Mail.SmtpMail();
message.SetTo("email***@yourdomain.com");
message.SetSubject("Process Set Test"); 
message.SetBody(env + "\n" + Db.ConnectionString);
mailer.Send(message);

You can handle the “p[1]” value with a regex or whatever, but in my tests, I found that value to be consistent regardless of where it runs from. You can use this example to send yourself the connection string and see what the actual value is for the environment and then replace the case value.

Hope this helps

Why not something like

bool isProd = Db.ConnectionString.Contains("SaaS###_######");

This might not work for SaaS MT users since they do not have access to the Db context.

If you’re just looking for Production or not and you or the Epicor Cloud Team is maintaining the Production Flag in the Admin Console, AND you’re on 10.2 or higher then BPM’s have a built-in condition for checking this flag:

For people at 10.1 and below, one can search for certain text in the Company Name and do a regex like you did above.

MT…
seinfeld-jerry-seinfeld1

3 Likes

BTW, at some point, maybe 2021.1 but certainly 2021.2, there are also GetInstancePurpose and SetInstancePurpose methods in the Client Functions service in addition to the IsProduction setting.

I’m not sure what the intended use for this by Epicor but it appears to just be a boolean and it has the same property as IsProduction? :thinking: If it were a string, it would be easy to set the purpose of non-production instances and solve this problem - even for SaaS MT!!!

Maybe the Open-API Schema is wrong here… :man_shrugging:

image

image

1 Like

ClientFunctions be like
image

1 Like