Reading Environment (ENV) Variables From The Server Scope In Lucee CFML 5.3.7.47
This is a pro-tip that I originally picked up from Julian Halliwell a few years ago. However, I sometimes talk to people who don't realize that this is possible. So, I wanted to try and amplify Julian's post. In Lucee CFML, you can read environment (ENV) variables directly out of the server
scope. They are just automatically there - no dipping into the Java layer or dealing with the java.lang.System
class. Lucee CFML brings these values to the surface for easy consumption.
Specifically, all of the ENV variables are located at:
server.system.environment
Much like the process.env
object in Node.js, this is a Struct in which every value is a String. Let's see this in action. In the following ColdFusion code, I'm wrapping access to the aforementioned Struct in a User-Defined Function (UDF) - env()
- so that I can add a bit of fallback logic for non-existent / non-populated environment variable values:
<cfscript>
echoLn( "FOO:", env( "FOO" ) );
echoLn( "BAR:", env( "BAR", "!! Fallback for Bar !!" ) );
echoLn( "JAVA_HOME:", env( "JAVA_HOME" ) );
echoLn( "JAVA_VERSION:", env( "JAVA_VERSION" ) );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
/**
* I return the given environment variable value; or, the fallback if the variable is
* either UNDEFINED or EMPTY.
*/
public string function env(
required string name,
string fallbackValue = ""
) {
// In Lucee CFML, we can access the environment variables directly from the
// SERVER SCOPE.
var value = ( server.system.environment[ name ] ?: "" );
// For the sake of the demo, we're treating an EMPTY value and a NON-EXISTENT
// value as the same thing, using the given value only if it is populated.
return( value.len() ? value : fallbackValue );
}
/**
* I echo each argument and then add a line-break (BR tag).
*/
public void function echoLn() {
echo( arrayToList( arguments, " " ) & "<br />" );
}
</cfscript>
Now, if we run this ColdFusion code, we get the following output:
FOO:
BAR: !! Fallback for Bar !!
JAVA_HOME: /usr/local/openjdk-8
JAVA_VERSION: 8u272
As you can see, our non-existent FOO
and BAR
variables are reported using their fallback values. And, our valid JAVA_HOME
and JAVA_VERSION
environment variables are successfully pulled out of the server
scope. Easy peasy lemon squeezy!
Want to use code from this post? Check out the license.
Reader Comments
Loosely related, we tag each of our servers so that we can fork logic based on the development environment the server resides in. This allows us to use TEST resources whenever we're in the test environment and PROD resources when in prod.
How this works...in the
server.json
underjvm.args
we add"-Dges.config.serverEnvironment=test"
for our test server configs. Then in our applications, we access that variable using the application scopeapplication.serverEnvironment
. It's a very useful utility :)@Chris,
Great minds think alike - we use a variable called
serverType
for that same kind of thing. I think it can be either "live", "preview", or "local" (if memory serves me off-hand). Probably in a perfect world, we wouldn't these kind of checks because every possible value would be an ENV input. But, sometimes, it's just so much easier to check to see which environment you're running in 😆FWIW, the server.system struct (and environment and properties under that) are available also in CF2018 and above.
I appreciate that Ben's focus now is Lucee, and I don't want to jump in to every post and be "that guy". Still, since some folks using CF could find this, I wanted them to know they can use it there also. :-)
@Charlie,
No, Charlie, that is great to know. I have to say, I actually did quite a bit of Googling for things like, "ColdFusion environment variables" and "ColdFusion ENV" and "ColdFusion reading ENV values", and nothing came up for Adobe ColdFusion. So, thank you very much for adding clarity - I had tried to do my due diligence (without actually spinning up a server), and fell short.
@Charlie,
Ha ha, given that you just said it supported
server.system
, I just did a Google search with that query "quoted", and your post came up 😂https://www.carehart.org/blog/client/index.cfm/2020/2/28/cf2018_env_vars_in_server_scope
Small world!
@All,
I had wanted to write this post on
env()
access as a per-cursor to a post on managing - and rotating - secret ENV values across ColdFusion systems:www.bennadel.com/blog/4142-managing-shared-secret-token-rotation-across-systems-in-lucee-cfml-5-3-7-47.htm
So here is a question. If I add or change an ENV var, how do I force CF to re-read them w/o restarting CF? Sort of like you would do with a ?reload for the app.
@Derek,
That's an interesting question - I haven't run across that in my work since we actually deploy new containers as part of changes like that. As such, I've not personally had to reload an ENV value.
Your question is founded on the idea that the readable scope isn't automatically updated when an ENV is changed. I haven't tested that directly; but, I would mostly assume that to be true. But, even if that did update, you'd likely still have to reload the app itself since you never know where those ENV have been cached.
I wonder if the
java.lang.System
reads are update live. I supposed if they are, you might just have to go that route instead.Sorry I don't have anything more definitive - I'll see if I can find some time to play with this idea.