While watching this video by the venerable Jace Benson, I was inspired by an idea presented around the 4-minute mark, which he picked up from Maik Skoddow's article, here: a more effective way to manage instance-specific System Properties. So inspired was I, that I decided to not only adopt his suggestion, but to write a little tool to handle it for me as well! I call this tool "Local Properties". 'Local', here meaning "instance-specific"; properties which should have one value in one instance (such as your dev environment), and a different value in a different instance (such as production).
As many developers will know all too well, this is a problem that we face often: How do you use one property value in one instance and another value in another instance, without risking poisoning the data in the higher instance just by modifying the value in a lower instance (and perhaps not even realizing that it's been captured in your update set)?
In this article, we're going to discuss the idea that Jace presented to solve this problem in a robust and elegant way, as well as a free tool that I've built to handle this for you!
The idea presented in Jace's video is to use a specific naming convention for "local" System Properties wherein you prefix your instance-specific (“local”) system properties with the exact name of the instance to which they belong.
For example, if you have a system property like “my_rest_endpoint
”, you would actually create a separate version of that property for each of your ServiceNow instances. Say you have three instances: Dev (acme-dev
), Test (acme-test
), and Prod (acme
). In this case, you would create three separate versions of this system property like so:
acme-dev.my_rest_endpoint
acme-test.my_rest_endpoint
acme.my_rest_endpoint
This sounds simple enough -- but it still requires your code to "know" which instance it's in, in order to retrieve the correct property. Luckily, there's another system property in every ServiceNow instance called instance_name
which we can use to make our code fancy and dynamic; and that's what I leveraged in order to build a little Script Include to handle this for us automatically!
All you have to do to use this functionality is copy the below code into a new Script Include in your instance. The Script Include should have the name LocalProperty
, and should be accessible from all scopes.
View Script Include code (expand)
/** | |
* Local (instance-specific) System Property Script Include | |
* More info available at https://localprop.snc.guru | |
* | |
* A ServiceNow JavaScript ES5 class, LocalProperty, for getting and setting instance-specific | |
* system properties. | |
* See method documentation for more information. | |
* @param {string} [instanceNameOverride=] - Override the name of the instance that's currently | |
* executing this code. | |
* For example, if you have a system property such as "my_prod_instance.some_property" which | |
* you need to specifically retrieve while executing code in your development environment | |
* for some reason, you would call this constructor like so: <br /> | |
* | |
* var localPropUtil = new LocalProperty('my_prod_instance'); | |
* //... | |
* | |
* This is most commonly useful for testing, but this argument should generally NOT be specified. | |
* | |
* @constructor | |
*/ | |
function LocalProperty(instanceNameOverride) { | |
//If the instance name override is not provided, use the instance name system property. | |
var instanceNamePropVal = gs.getProperty('instance_name', ''); | |
//Set the local-scope instance_name property for later use, depending on whether the override argument has been specified. | |
this.instance_name = (typeof instanceNameOverride === 'undefined') ? | |
(instanceNamePropVal) : (instanceNameOverride); | |
//If the instance name is empty, throw an error. | |
//This should never happen, and would indicate that something has gone horribly wrong. | |
if (!this.instance_name) { | |
throw new Error( | |
'Whoa there! The "instance_name" system property was not found in the sys_properties ' + | |
'table. Please set the instance_name property. This is very important, not just ' + | |
'for this specific error!' | |
); | |
} | |
/* Methods below */ | |
/** | |
* Get the value of an instance-specific ("local") system property. | |
* Local system properties should follow the pattern <instance_name>.<property_name>. For | |
* example, if your instance is named "my_instance" and you want to retrieve the value of a | |
* property named "my_http_endpoint", you would need to have a system property with the | |
* name "my_instance.my_http_endpoint", and call this method with the propVal argument | |
* "my_http_endpoint". | |
* | |
* @param {string} propName - The name suffix of the system property to retrieve. | |
* This should be the portion of the system property name that comes after the instance name. | |
* For example, if your instance name is "dev1234", and you want to retrieve the value of a | |
* system property named "dev1234.my_http_endpoint", you would pass "my_http_endpoint" as | |
* the propName argument and not include the instance name in this argument. | |
* If there is no system property with the name <instance_name>.<propName>, then the default value you specify in the defaultVal argument is returned. If you do not specify a default value, then an empty string is returned instead. | |
* The instance name is retrieved automatically from the instance_name system property, but can be overridden when calling the constructor for this class. | |
* | |
* @param {string} [defaultVal=''] - The default value to return if the system property | |
* is not found. | |
* | |
* @returns {string} - The value of the system property with the name | |
* <instance_name>.<propName>, or the default value if the system property is not found. | |
* | |
* @example Get the value of a local system property "my_instance.my_http_endpoint": | |
* var propName = 'my_http_endpoint'; | |
* var localProp = new LocalProperty(); | |
* var propVal = localProp.getProperty(propName); | |
* | |
* @example Get the value of a local system property "my_instance.my_http_endpoint", but with a default value of "http://localhost:8080": | |
* var propName = 'my_http_endpoint'; | |
* var defaultVal = 'http://localhost:8080'; | |
* var localProp = new LocalProperty(); | |
* var propVal = localProp.getProperty(propName, defaultVal); | |
* | |
* @example Get the value of a local system property "my_other_instance.my_http_endpoint", but with a default value of "http://localhost:8080", and override the instance name (in this case, to "my_other_instance") - which will cause the system property "my_other_instance.my_http_endpoint" to be retrieved no matter which instance this code is executed in: | |
* var propName = 'my_http_endpoint'; | |
* var defaultVal = 'http://localhost:8080'; | |
* var instanceNameOverride = 'my_other_instance'; | |
* var localProp = new LocalProperty(instanceNameOverride); | |
* var propVal = localProp.getProperty(propName, defaultVal); | |
*/ | |
this.getProperty = function(propName, defaultVal) { | |
var propVal; | |
var fullPropName = this.instance_name + '.' + propName; | |
//Set the default value ('') of the defaultVal argument if it is not provided. | |
defaultVal = (typeof defaultVal === 'undefined') ? ('') : (defaultVal); | |
//If the propName argument is not provided, throw an error. | |
if (!propName) { | |
throw new Error( | |
'Whoa there! The "propName" argument was not provided when attempting to get a ' + | |
'property with the LocalProperty class. You must provide a value for the propName ' + | |
'argument.' | |
); | |
} | |
//Get the value of the system property with the naming pattern <instance_name>.<propName>. | |
propVal = gs.getProperty( | |
fullPropName, | |
defaultVal | |
); | |
gs.debug( | |
'Got the value "' + propVal + '" from system property with name "' + | |
fullPropName + '".' | |
); | |
return propVal; | |
}; | |
/** | |
* Set the value of an instance-specific ("local") system property to the specified new value. | |
* Local system properties should follow the pattern <instance_name>.<property_name>. For | |
* example, if your instance is named "my_instance" and you want to set the value of a | |
* property named "my_http_endpoint", you would need to have a system property with the | |
* name "my_instance.my_http_endpoint", and call this method with the propVal argument | |
* "my_http_endpoint". | |
* | |
* Note: Care should be taken when setting system properties (sys_properties), as it causes | |
* a system-wide cache flush. Each flush can cause system degradation while the caches | |
* rebuild. If a value must be updated often, it should not be stored as a system property. | |
* In general, you should only place values in the sys_properties table that do not | |
* frequently change. | |
* | |
* Note: Just as with using the OOB gs.setProperty() method, if the specified system property does not exist, then it will be created. If it is created, it will always be created as a STRING type -- regardless of what data-type is passed into it. Therefore, | |
* | |
* @param {string} propName - The name suffix of the system property to set. | |
* This should be the portion of the system property name that comes after the instance name. | |
* For example, if your instance name is "dev1234", and you want to set the value of a | |
* system property named "dev1234.my_http_endpoint", you would pass "my_http_endpoint" as | |
* the propName argument and not include the instance name in this argument. | |
* If there is no system property with the name <instance_name>.<propName>, then the default | |
* value you specify in the defaultVal argument is returned. If you do not specify a default | |
* value, then an empty string is returned instead. | |
* The instance name is retrieved automatically from the instance_name system property, but | |
* can be overridden when calling the constructor for this class. | |
* | |
* @param {string} [newVal=''] - The new value to which the specified system property should | |
* be set. | |
* If this is not specified, then the system property will be set to a blank string. | |
* If the system property does not exist, then it will be created. | |
* | |
* @param {string} [description=] - A description of the property. | |
* This can normally be left blank unless you're intending to create a new system property, | |
* but is not mandatory in either case. | |
* | |
* @returns {string} the new value to which the specified system property has been set. | |
* | |
* @example Set the value of a local system property "my_instance.my_http_endpoint" to the new value http://localhost:8080 (if the property doesn't exist, it will be created): | |
* var propName = 'my_http_endpoint'; | |
* var newPropVal = 'http://localhost:8080'; | |
* var localProp = new LocalProperty(); | |
* | |
* localProp.setProperty(propName, newPropVal); | |
* | |
* @example Set the value of a local system property "my_other_instance.my_http_endpoint" to "http://localhost:8080", but override the instance name (in this case, to "my_other_instance") - which will cause the system property "my_other_instance.my_http_endpoint" to be set no matter which instance this code is executed in: | |
* var propName = 'my_http_endpoint'; | |
* var newPropVal = 'http://localhost:8080'; | |
* var instanceNameOverride = 'my_other_instance'; | |
* var localProp = new LocalProperty(instanceNameOverride); | |
* localProp.setProperty(propName, defaultVal); | |
*/ | |
this.setProperty = function(propName, newVal, description) { | |
var fullPropName; | |
if (!propName) { | |
throw new Error( | |
'Whoa there! The "propName" argument was not provided when attempting to set a ' + | |
'property with the LocalProperty class. You must provide a value for the ' + | |
'propName argument.' | |
); | |
} | |
fullPropName = this.instance_name + '.' + propName; | |
if (typeof newVal === 'undefined') { | |
gs.warn( | |
'The "newVal" argument was not provided when attempting to set the value ' + | |
'of the property "' + fullPropName + '" with the LocalProperty class. The ' + | |
'property will be set to an empty string. \n' + | |
'It is best to explicitly pass an empty string to this method if that is the ' + | |
'desired outcome.' | |
); | |
newVal = ''; | |
} | |
gs.debug( | |
'Setting the system property "' + fullPropName + '" to the new value "' + | |
newVal + '".' | |
); | |
if (description) { | |
gs.setProperty( | |
fullPropName, | |
newVal, | |
description | |
); | |
return newVal; | |
} | |
gs.setProperty( | |
fullPropName, | |
newVal | |
); | |
return newVal; | |
}; | |
} |
This above Script Include exposes two 'methods' (functions): .getProperty()
and .setProperty()
. These behave effectively identically to ServiceNow's own gs.getProperty()
and gs.setProperty()
APIs, except that it will prepend the system property name you specify with the instance name!
The Script Include above makes heavy use of JSDoc to document the methods - arguments that they accept, what they return, and example usage - but if you're having trouble picturing what I'm talking about, here are some examples of how you might interact with these methods in your own code:
Get the value of a local system property
Retrieve the value of the local system property named “my_instance.my_http_endpoint
(where my_instance
is the name of whatever instance the code is executing in, such as acme-dev
)”.
var propName = 'my_http_endpoint';
var localProp = new LocalProperty();
var propVal = localProp.getProperty(propName);
Get local system property value (with default value if it doesn't exist)
Retrieve the value of the “my_instance.my_http_endpoint” system property, but specify a default value of “http://localhost:8080
” to be returned if the property doesn't exist.
var propName = 'my_http_endpoint';
var defaultVal = 'http://localhost:8080';
var localProp = new LocalProperty();
var propVal = localProp.getProperty(propName, defaultVal);
Get the value for a given system property, in a different instance ('my_other_instance')
Retrieve the value of the "my_http_endpoint" system property, as if you were in another instance (in this case, the "my_other_instance" instance).
var propName = 'my_http_endpoint';
var defaultVal = 'http://localhost:8080';
var instanceNameOverride = 'my_other_instance';
var localProp = new LocalProperty(instanceNameOverride);
var propVal = localProp.getProperty(propName, defaultVal);
Set the value of a local system property
var propName = 'my_http_endpoint';
var newPropVal = 'http://localhost:8080';
var localProp = new LocalProperty();
localProp.setProperty(propName, newPropVal);
Set the value of the local system property for a specific instance
Set the value of a given system property for a specific instance; not necessarily the instance in which the code is running.
var propName = 'my_http_endpoint';
var newPropVal = 'http://localhost:8080';
var instanceNameOverride = 'my_other_instance';
var localProp = new LocalProperty(instanceNameOverride);
localProp.setProperty(propName, defaultVal);
That's all there is to it!
After adding this Script Include to your instance, you should be able to get and set local (instance-specific) System Properties using the pattern new LocalProperty().getProperty('my_prop', 'default value here');
or new LocalProperty().setProperty('my_prop', 'new_val');
.
Thanks for reading! If you find my content useful, please consider checking out my latest book: The ServiceNow Development Handbook, check out some of my other articles below, subscribe to this blog and if you like, connect with me on LinkedIn.
If you like Jace's content as much as I do, consider checking out his website and YouTube channel!
-
March 2025
- Mar 24, 2025 Calculate Distance Between Two Locations in ServiceNow (without an API call!) Mar 24, 2025
- Mar 11, 2025 5 Ways to Check your ServiceNow Instance for DANGEROUS CODE in Less Than 5 minutes Mar 11, 2025
-
March 2024
- Mar 28, 2024 How to Identify Duplicate Records by Multiple Fields in ServiceNow Mar 28, 2024
- Mar 7, 2024 How to Merge Personal & Company ServiceNow Accounts Mar 7, 2024
-
February 2024
- Feb 12, 2024 5 Lessons About Programming From Richard Feynman Feb 12, 2024
-
July 2023
- Jul 5, 2023 Managing Instance-Specific System Properties for Dev/Test/Prod in ServiceNow Jul 5, 2023
-
April 2023
- Apr 28, 2023 Your ACLs and Business Rules are Broken (Here's How to Fix Them) Apr 28, 2023
-
December 2022
- Dec 13, 2022 ServiceNow Developers: BE THE GUIDE! Dec 13, 2022
-
October 2022
- Oct 19, 2022 A Faster, More Efficient Client-side GlideRecord (Free tool!) Oct 19, 2022
- Oct 9, 2022 Animated Loading Message & Collapsible Details on ServiceNow Form or Field (Client-side) Oct 9, 2022
-
August 2022
- Aug 23, 2022 Using .addJoinQuery() & How to Query Records with Attachments in ServiceNow Aug 23, 2022
- Aug 18, 2022 Free, Simple URL Shortener for ServiceNow Nerds (snc.guru) Aug 18, 2022
- Aug 16, 2022 How to Get and Parse ServiceNow Journal Entries as Strings/HTML Aug 16, 2022
- Aug 14, 2022 New tool: Get Latest Version of ServiceNow Docs Page Aug 14, 2022
-
March 2022
- Mar 4, 2022 How to Set or Change ServiceNow Application's Repository URL, Credentials, or SSH Key Mar 4, 2022
-
February 2022
- Feb 7, 2022 How to return a CSV file from a Scripted REST API (SRAPI) in ServiceNow Feb 7, 2022
-
May 2021
- May 3, 2021 Adding a Guided Setup to Your ServiceNow Application May 3, 2021
-
April 2021
- Apr 27, 2021 Use Automated Tests to Validate "Guided Setup" Completion & Functionality. Apr 27, 2021
-
February 2021
- Feb 11, 2021 "Processors", SRAPIs, and How to Run a Script and Redirect a User From a URL in ServiceNow Feb 11, 2021
-
November 2020
- Nov 17, 2020 SN Guys is now part of Jahnel Group! Nov 17, 2020
-
September 2020
- Sep 14, 2020 Better ServiceNow Notifications (& Another FREE Tool!) Sep 14, 2020
-
July 2020
- Jul 31, 2020 Debugging Client & Catalog Client Scripts in ServiceNow Jul 31, 2020
-
January 2020
- Jan 20, 2020 Getting Help from the ServiceNow Community Jan 20, 2020
-
December 2019
- Dec 18, 2019 Can ServiceNow Script Includes Use the "current" Variable? Dec 18, 2019
-
November 2019
- Nov 18, 2019 Handling 'text/plain' and Other Unsupported Content Types in ServiceNow Scripted REST APIs Nov 18, 2019
-
April 2019
- Apr 21, 2019 Understanding Attachments in ServiceNow Apr 21, 2019
- Apr 10, 2019 Using Custom Search Engines in Chrome to Quickly Navigate ServiceNow Apr 10, 2019
- Apr 4, 2019 Set Catalog Variables from URL Params (Free tool) Apr 4, 2019
- Apr 1, 2019 Outlook for Android Breaks Email Approvals (+Solution) Apr 1, 2019
-
March 2019
- Mar 11, 2019 GlideFilter is Broken - Free Tool: “BetterGlideFilter” Mar 11, 2019
-
February 2019
- Feb 27, 2019 Making Update Sets Smarter - Free Tool Feb 27, 2019
-
November 2018
- Nov 29, 2018 How to Learn ServiceNow Nov 29, 2018
- Nov 6, 2018 ServiceNow & ITSM as a Career? Nov 6, 2018
-
October 2018
- Oct 19, 2018 Asynchronous onSubmit Catalog/Client Scripts in ServiceNow Oct 19, 2018
- Oct 11, 2018 How to do Massive, Slow Database Operations Efficiently With Event-Driven Recursion Oct 11, 2018
-
September 2018
- Sep 18, 2018 Broken Queries & Query Business Rules in ServiceNow Sep 18, 2018
- Sep 7, 2018 JournalRedactor - Easily Redact or Delete Journal Entries in ServiceNow! Sep 7, 2018
-
July 2018
- Jul 23, 2018 Admin Duty Separation with a Single Account Jul 23, 2018
-
June 2018
- Jun 19, 2018 Improving Performance on Older Instances with Table Rotation Jun 19, 2018
- Jun 4, 2018 New Free Tool: Login Link Generator Jun 4, 2018
-
May 2018
- May 29, 2018 Learning ServiceNow: Second Edition! May 29, 2018
-
April 2018
- Apr 17, 2018 Upgrading From Express to Enterprise: What's Missing Apr 17, 2018
- Apr 12, 2018 If a Genie Gave Me Three Wishes, I'd Use Them All to "Fix" Scope Apr 12, 2018
-
March 2018
- Mar 19, 2018 Service Catalog "Try in Portal" button Mar 19, 2018
- Mar 15, 2018 Video: Custom Output Transition Conditions From a Single Workflow (Script) Activity Mar 15, 2018
-
February 2018
- Feb 11, 2018 We have a new book! Feb 11, 2018
-
November 2017
- Nov 6, 2017 Requiring Attachments (& Other Miracles) in Service Portal Nov 6, 2017
-
September 2017
- Sep 12, 2017 Handling TimeZones in ServiceNow (TimeZoneUtil) Sep 12, 2017
-
July 2017
- Jul 27, 2017 How to Enable DOM Manipulation in ServiceNow Service Portal Catalog Client Scripts Jul 27, 2017
-
June 2017
- Jun 25, 2017 What's New in ServiceNow: Jakarta (Pt. 1) Jun 25, 2017
- Jun 4, 2017 Powerful Scripted Text Search in ServiceNow Jun 4, 2017
-
May 2017
- May 9, 2017 Work at Lightspeed: ServiceNow's Plan for World Domination May 9, 2017
-
April 2017
- Apr 9, 2017 Avoiding Pass-By-Reference Using getValue() & setValue() Apr 9, 2017
- Apr 4, 2017 "Learning ServiceNow" is Now Available for Purchase! Apr 4, 2017
-
March 2017
- Mar 12, 2017 reCAPTCHA in ServiceNow CMS/Service Portal Mar 12, 2017
-
December 2016
- Dec 20, 2016 Pro Tip: Use updateMultiple() for Maximum Efficiency! Dec 20, 2016
- Dec 2, 2016 We're Writing a Book! Dec 2, 2016
-
November 2016
- Nov 10, 2016 Chrome Extension: Load in ServiceNow Frame Nov 10, 2016
-
September 2016
- Sep 7, 2016 Force-Include Any Record Into an Update Set Sep 7, 2016
- Sep 1, 2016 GlideRecord Pagination - Page through your GlideRecord query Sep 1, 2016
-
July 2016
- Jul 17, 2016 Granting Temporary Roles/Groups in ServiceNow Jul 17, 2016
- Jul 15, 2016 Scripted REST APIs & Retrieving RITM Variables via SRAPI Jul 15, 2016
-
May 2016
- May 17, 2016 What's New in Helsinki? May 17, 2016
-
April 2016
- Apr 27, 2016 Customizing UI16 Through CSS and System Properties Apr 27, 2016
- Apr 5, 2016 ServiceNow Versions: Express Vs. Enterprise Apr 5, 2016
-
March 2016
- Mar 28, 2016 Update Set Collision Avoidance Tool: V2 Mar 28, 2016
- Mar 18, 2016 ServiceNow: What's New in Geneva & UI16 (Pt. 2) Mar 18, 2016
-
February 2016
- Feb 22, 2016 Reference Field Auto-Complete Attributes Feb 22, 2016
- Feb 6, 2016 GlideRecord & GlideAjax: Client-Side Vs. Server-Side Feb 6, 2016
- Feb 1, 2016 Make Your Log Entries Easier to Find Feb 1, 2016
-
January 2016
- Jan 29, 2016 A Better, One-Click Approval Jan 29, 2016
- Jan 25, 2016 Quickly Move Changes Between Update Sets Jan 25, 2016
- Jan 20, 2016 Customize the Reference Icon Pop-up Jan 20, 2016
- Jan 7, 2016 ServiceNow: Geneva & UI16 - What's new Jan 7, 2016
- Jan 4, 2016 Detect/Prevent Update Set Conflicts Before They Happen Jan 4, 2016
-
December 2015
- Dec 28, 2015 SN101: Boolean logic and ServiceNow's Condition Builder Dec 28, 2015
- Dec 17, 2015 Locate any record in any table, by sys_id in ServiceNow Dec 17, 2015
- Dec 16, 2015 Detecting Duplicate Records with GlideAggregate Dec 16, 2015
- Dec 11, 2015 Array.indexOf() not working in ServiceNow - Solution! Dec 11, 2015
- Dec 2, 2015 Understanding Dynamic Filters & Checking a Record Against a Filter Using GlideFilter Dec 2, 2015
-
October 2015
- Oct 20, 2015 Bookmarklet: Load the current page in the ServiceNow frame Oct 20, 2015
-
August 2015
- Aug 27, 2015 Easily Clone One User's Access to Another User Aug 27, 2015