Is there a way to set custom property values from your groovy script in your datasource?
Hey there. I’m relatively new to logic monitor and I received a request from a client.
The request is as follows.
They wanted us to be able to provide not only blocked processes alerts for their SQL dbs but also for us to provide a breakdown of what is blocking what.
My solution to this was to edit the datasource that contains the processblocked datapoint. I made some good progress. I was able to create a custom property field for the instances that I’m monitoring. I then edited the script for the collector attribute so that in theory that if particular datapoint (processessblocked) shows a value of greater than 0, it uses hostSets() to change that custom property value to the blocker session ids using some custom sql script within.
When I test it using the test button pointed at my sql server, it works exactly as i expected. It shows the default value of the property unless it hits that block process value of 1 or more. then the property reads with the needed info.
However after I save the datasource. And put this in to practice, no matter what the custom property on those instances never update or change.
I also tried applying similar logic to the active discovery script with the same results. Seems to work fine through testing but then nothing in the actual instance changes.
I can post any or all of the groovy script if need be. Is it something simple like some sort of syntax I’m not getting or am I not even in the right area to do what I’m attempting to do?
Any help would be appreciated and like I said, I’ll gladly provide any more info or code if need be.
Thanks
domenicbetters
OP3 years agoI’d guess there’s a problem with those collectors accessing the internet. In postman, there’s an option on the right that looks like “</>”. If you pop that open, it’ll auto generate simple code to make your API call. If you select cURL, you should be able to paste that into a bash shell on the collector (presuming the token in that code is recent). If that fails with a similar error, that would mean a problem on the host, not related to the collector software.
Good idea, and after testing it, it works fine through powershell and bash. So looks like it is a collector issue.
LM User
·3 years agoI’d guess there’s a problem with those collectors accessing the internet. In postman, there’s an option on the right that looks like “</>”. If you pop that open, it’ll auto generate simple code to make your API call. If you select cURL, you should be able to paste that into a bash shell on the collector (presuming the token in that code is recent). If that fails with a similar error, that would mean a problem on the host, not related to the collector software.
domenicbetters
OP3 years agoAlright, I know you’ve already got yours working but I’ve been meaning to simplifying this into a single function for years and never got around to it. Well, now I have. Hopefully the documentation is sufficient. I’m not sure it’ll fix the issue you’re having with the devices on the lab collectors, but I’d be interested to know how it goes for you.
Actually, I dipped into your github and tested out your version already. I love how it’s cleaned up but unfortunately, in regard to that issue with the two training devices, that unknownhostexception persists.
It seems the issue has something to do with API calls in general, and if I can get anything out of support, I’ll post an update. But for the purpose of the original post, I’d say mission accomplished.
LM User
·3 years agoAlright, I know you’ve already got yours working but I’ve been meaning to simplifying this into a single function for years and never got around to it. Well, now I have. Hopefully the documentation is sufficient. I’m not sure it’ll fix the issue you’re having with the devices on the lab collectors, but I’d be interested to know how it goes for you.
domenicbetters
OP3 years agoWell, I still haven’t been able to resolve this issue with a few of our training devices. That aside, the modified script works as intended and I finally have a solution to my original problem posed in first message of this post.
Huge thanks to Stuart for letting me bug him every step of the way.
So the solution was to use API calls within int he collector attribute script inside the mysql global performance datasource.
I won’t share the whole script but I will post parts that seem relevant to my issue and solution.
A lot of extra imports were needed to get this working and before the base script starts i establish to GET requests to obtain info needed to get to my final GET and PUT request
My final resource path --- "/device/devices/"+hostProps.get("system.deviceID")+"/devicedatasources/${datasouceid}/instances/"+instanceids
requires a deviceID, a datasourceID and an instanceID. So the first get pulls the correct datasource ID and the second get uses that datasourceID to grab the instance IDs.
Then Once the collector begins its SQL query loop to find the values for all those datapoints, I insert my final GET and PUT, as well as using a custom SQL query script to find the blocker info I need.
Now when the collector does it’s thing it will run that SQL query and per instance, update my custom property with either the blocker details or “no blocks”.
LM User
·3 years agoIt’s as if the other collector can’t resolve the DNS name.
domenicbetters
OP3 years agoAre those devices monitored by a different collector that might have different DNS/internet path?
hmmm they are monitored by their own collectors. I am gonna go through them both to see if anything is different or maybe I messed something up in all my previous tests and failed scriptings.
domenicbetters
OP3 years agoIs it micoresolutions or microresolutions?
micoresolutions
LM User
·3 years agoIs it micoresolutions or microresolutions?
LM User
·3 years agoAre those devices monitored by a different collector that might have different DNS/internet path?
domenicbetters
OP3 years agoSo the script works great for my main test device. However the collector does keep throwing that error
“java.net.UnknownHostException: micoresolutions.logicmonitor.com”
When I run it on another device. It is still the same account info so nothing should be different about it.
As a follow up, this specifically seems to only be affecting our other two virtual machines in our training environment here at the office
domenicbetters
OP3 years agoTesting out the final script now. Hopefully I can post the finished code after a rousing success.
domenicbetters
OP3 years agoGonna call it a day for now. I think tomorrow I will be able to add the last bit of magic to get this all to work.
domenicbetters
OP3 years agoCloseableHttpClient httpinstanceid = HttpClients.createDefault();
they are defined here right below the http get comments
and thanks for the shortening!
LM User
·3 years agoAh, Groovy error messages. So helpful.
FYI, this:
datapull = new JsonSlurper().parseText(responseBody.toString());
datapull = datapull.data.items.id.value.toString()
datapull = datapull.replace("[","")
datasouceid = datapull.replace("]","")
Can be just this:
datapull = new JsonSlurper().parseText(responseBody.toString())[0]Are you closing something that doesn’t exist?
httpdsid.close()httpinstanceid.close()Where are these objects defined?
domenicbetters
OP3 years agomy latest error when i test the script on a device other than the one i usually use
domenicbetters
OP3 years agoThis is the whole thing so far. Possible errors still. im gonna try switching tot he debug window tester.
domenicbetters
OP3 years ago// *** Establish API Creds ***
def accessId = hostProps.get("lmaccess.id");
def accessKey = hostProps.get("lmaccess.key");
def account = 'micoresolutions';
// *** Get Datasource ID for device as string. ***
def resourcePath = '/device/devices/'+hostProps.get("system.deviceID")+'/devicedatasources'
def url = "https://" + account + ".logicmonitor.com" + "/santaba/rest" + resourcePath;
//get current time
epoch = System.currentTimeMillis();
//calculate signature
requestVars = "GET" + epoch + resourcePath;
hmac = Mac.getInstance("HmacSHA256");
secret = new SecretKeySpec(accessKey.getBytes(), "HmacSHA256");
hmac.init(secret);
hmac_signed = Hex.encodeHexString(hmac.doFinal(requestVars.getBytes()));
signature = hmac_signed.bytes.encodeBase64();
// HTTP GET
CloseableHttpClient httpdsid = HttpClients.createDefault();
httpGet = new HttpGet(url);
URI uri = new URIBuilder(httpGet.getURI())
.addParameter("fields", "id")
.addParameter("filter", "dataSourceName:Microsoft_SQLServer_GlobalPerformance")
.build();
httpGet.setURI(uri);
httpGet.addHeader("Authorization" , "LMv1 " + accessId + ":" + signature + ":" + epoch);
response = httpdsid.execute(httpGet);
responseBody = EntityUtils.toString(response.getEntity());
code = response.getStatusLine().getStatusCode();
datapull = new JsonSlurper().parseText(responseBody.toString());
datapull = datapull.data.items.id.value.toString()
datapull = datapull.replace("[","")
datasouceid = datapull.replace("]","")
httpdsid.close()
// *** get instance ID for each instance on device ***
resourcePath = "/device/devices/"+hostProps.get("system.deviceID")+"/devicedatasources/${datasouceid}/instances"
url = "https://" + account + ".logicmonitor.com" + "/santaba/rest" + resourcePath;
//get current time
epoch = System.currentTimeMillis();
//calculate signature
requestVars = "GET" + epoch + resourcePath;
hmac = Mac.getInstance("HmacSHA256");
secret = new SecretKeySpec(accessKey.getBytes(), "HmacSHA256");
hmac.init(secret);
hmac_signed = Hex.encodeHexString(hmac.doFinal(requestVars.getBytes()));
signature = hmac_signed.bytes.encodeBase64();
// HTTP GET
CloseableHttpClient httpinstanceid = HttpClients.createDefault();
httpGet = new HttpGet(url);
httpGet.addHeader("Authorization" , "LMv1 " + accessId + ":" + signature + ":" + epoch);
response = httpinstanceid.execute(httpGet);
responseBody = EntityUtils.toString(response.getEntity());
code = response.getStatusLine().getStatusCode();
datapull = new JsonSlurper().parseText(responseBody.toString());
device = datapull.data.items.id
httpinstanceid.close()
domenicbetters
OP3 years agoNext hiccup. I have the script built out to the point where to GETs eventually pull in the various instance IDs attached to the device. Works perfectly for one device. The other. I get another damn red text error.
Although i believe this one may just be an error in how info is cached in the test box
LM User
·3 years agoYeah, I’d love to see it.
LM User
·3 years agoNow I’m getting an authentication failed when i add in the filter elements. Why is groovy so mean to me? LOL
It’s in Groovy’s nature. It’s why it’s not a very popular language outside very specific circles.
domenicbetters
OP3 years agoI got it. I’m using Apache for what it’s worth to putt the request. When you make a request with parameters using it you have to use
import org.apache.http.client.utils.URIBuilder
Then Build the URI like so
URI uri = new URIBuilder(httpGet.getURI())
.addParameter("fields", "id")
.addParameter("filter", "dataSourceName:Microsoft_SQLServer_GlobalPerformance")
.build();
httpGet.setURI(uri);
I can post the whole code if you want to see.
domenicbetters
OP3 years agoNow I’m getting an authentication failed when i add in the filter elements. Why is groovy so mean to me? LOL
LM User
·3 years agoFWIW: i had that happen once to me last week. In the time it took me to verify the DS was indeed on that device and I came back to postman, the same request resulted in data.
LM User
·3 years agoBe careful with that. Postman corrects errors like encoding spaces and quote marks. In groovy, you’ll need to escape the double quotes.
/device/devices/{deviceId}/devicedatasources?filter=dataSourceName:\”Microsoft_SQLServer_GlobalPerformance\”&fields=id,dataSourceNameWhen all else fails, output your URL after creating it in groovy to make sure it looks right.
domenicbetters
OP3 years ago/device/devices/{deviceId}/devicedatasources?filter=dataSourceName:Microsoft_SQLServer_GlobalPerformance&fields=id,dataSourceName
Taking the quotes off of the datasource name value fixed it for me in postman. now to test in logic mon
domenicbetters
OP3 years agoAs a brief follow up. I made a clone of the datasource to see if that one would show up with this get
{{url}}/device/devices/{id}/devicedatasources/
Turns out with device “A” I did fine my clone in the list
however device “B” does not show it.
I verified both devices had the datasource in question by checking my resources tab in the UI
domenicbetters
OP3 years agoIf you’ve got postman working, you’re doing well. That’s where most people trip up.
So, you need to construct this URL to do your patch:
/device/devices/{deviceId}/devicedatasources/{hdsId}/instances/{id}To do that, you need three variables: deviceId, hdsId, and id. Sounds like you already have deviceId.
To get hdsId (hardware datasource id, don’t ask why it’s called that, it doesn’t matter), you’ll need to do a GET on :
/device/devices/{deviceId}/devicedatasources?filter=dataSourceName:"Microsoft_SQLServer_GlobalPerformance"&fields=id,dataSourceNameThe response will give you, as you’ve seen, the list of datasources on that device, but with the filter specified above, you should only get a response if there’s been an instance discovered on the device whose device ID is {deviceId}. If there’s not one there, you have chosen the id of a device that doesn’t have an instance for that datasource.
From there, you can use the id from the response (which is different for every device, yay!) to get the list of instances that you will be attempting to patch. Do a GET on:
/device/devices/{deviceId}/devicedatasources/{hdsId}/instancesThe id shown in the response for each instance is the id that should go in the PATCH url.
Also, when you go to do your patch, there’s a query parameter: opType that will affect how your patch works, especially when it comes to properties. The swagger docs don’t even tell you what the possible values are much less what they do, so here it is:
So, the possible values are add, replace, and refresh.
Add - You specify the properties you want to add. Existing properties are untouched. New properties are added. If your payload has a property that already exists, that property isn’t modified.
Replace - You specify the properties you want to update. New properties are added. If your payload has a property that already exists, that property is updated to match your payload.
Refresh - This is the nuclear option which will delete all properties and replace them with the properties in your payload.
You’d probably want to do replace.
Hey there, I’m circling back to this. I wonder why When I run the get that finds the datasouce id.
This part
/device/devices/{deviceId}/devicedatasources?filter=dataSourceName:"Microsoft_SQLServer_GlobalPerformance"&fields=id,dataSourceNameI come up blank. I found the datasource ID before in a very roundabout way but i realize if I want this script to work for any device, I’ll need to automate it a little more.
When I run a get on all datasources, it’s not even listed there. Is there a logic monitor setting I need to enable to make the datasource visible to the API?
domenicbetters
OP3 years agoI got hit with an auth failure as well. I made sure its pulling the proper id and key. I tried seeing if anything was amiss myself but im coming up blank
Also I made sure data was in json string format and not a map or object or anything else.
domenicbetters
OP3 years agoSure I’ll give yours a shot first. And that was the doc I saw yesterday! Thanks for finding that one.
LM User
·3 years agoI think this works. When I tested it with the wrong verb, i got a 401 (auth fail). When i tested it with the right verb, i got a 403 (permission denied) (because the api creds i used are RO). I think that means i did get authenticated, but my creds don’t have permission. I did finally find this, which would probably work too; but do me a favor and see if the function above works.
domenicbetters
OP3 years agoLol. Have you ever generated the auth signature in groovy before. I see lots of examples in other languages and that post about generating it using postman but nothing so much in groovy. Although I could have sworn I saw one yesterday but for the life of me, I can’t find it now.
LM User
·3 years agoGood thinking. In the mean time I’m gonna take a step back and figure out getting a proper auth signature in my script so i dont need to just copy and paste for testing. It will need to be done regardless so while I wait for a response from them ill tackle this
If your experience with the chat queue is like mine, you’ll have an hour or so to get it working while you’re waiting. How else would I have the time to be on the community as much as I am?
https://tenor.com/view/seriously-really-little-girl-gif-14394090domenicbetters
OP3 years agoGood thinking. In the mean time I’m gonna take a step back and figure out getting a proper auth signature in my script so i dont need to just copy and paste for testing. It will need to be done regardless so while I wait for a response from them ill tackle this
LM User
·3 years agoRemember, the http client object is part of the santaba library (LM code), so IMO should be supported by LM. I’d push them in chat for help getting it working.
domenicbetters
OP3 years agoI think groovy just doesn’t like patch at all. I’m gonna try figuring out a way instead to make put work.
LM User
·3 years agoWell, your payload needs adjusting. Needs to be a list of maps:
This is what the .patch() method got as its arguments:
It would seem that the second argument (which is your payload?) is a LazyMap but needs to be json. The error message gives a ton of useless information, but doesn’t get far enough to tell you what type of object needs to be passed in as the payload. I’m guessing string. Here’s how you convert an array/map to json:
domenicbetters
OP3 years agodef payload = [name: "description", value: "New York"]
I believe I tried using a map, assuming this is the correct way to do so. Same result sadly.
Appreciate all the help btw.
LM User
·3 years agoI can’t remember what type of object should be passed in with the patch, but it seems like it wants a map. Try not converting it to json first.
domenicbetters
OP3 years agoThese are obviously the JSON equivalents of the Groovy map you might be using to construct the json.
Maybe that’s my hang up. I’ve tried every which way of json and groovy mapping i can think of and i still get this error
LM User
·3 years agoFWIW: I just had a similar boneheaded moment with LM Support. I had an LM Logs pipeline alert setup, but it wasn’t triggering the alert because I had left it disabled.
Aaaaanyway, the body would simply be the stuff you want to update. Doing a PATCH to:
{{url}}/device/devices/:deviceid/devicedatasources/:hdsid/instances/:instanceid?opType=replaceThe colons in my url are for Postman path variables. If you put the above URL in your postman, you should see it pop some additional fields into your Params tab. You can then put in the IDs without changing your url.
With a body like this:
I was able to patch my instance. If you wanted multiple properties patched:
These are obviously the JSON equivalents of the Groovy map you might be using to construct the json.
domenicbetters
OP3 years agoCan I pick your brain then on the proper syntax for the payload / body i would use for patching
LM User
·3 years agoWell now i feel dumb. it was simple as dropping the ‘’
Don’t beat yourself up. Your mind was obviously working on the more difficult problems that you’ve still to surmount.
domenicbetters
OP3 years agoWell now i feel dumb. it was simple as dropping the ‘’
LM User
·3 years agoYeah, that creates a string. In your working example, you create a map, not a string. Remove the single quotes from around the headers when you define it.
domenicbetters
OP3 years agodef headers = ‘["Authorization": "LMv1 PHf4xEdZHA5sz8U5ECM8:NzJjNzMzZDkyZjdmMDY0MWYwMWUwZmU3Nzc1ZDUzZjIwY2I3OWEyZGU3NGU5MmE1YzhiNWM3NTA5NDViZmFhZA==:1684428033047","Content-Type":"application/json"]’
LM User
·3 years agoAh, ok good. Show me how you’re defining the headers variable?
domenicbetters
OP3 years agoI made sure it wasn’t expired. Like i said when i just hard code the header i get a success. but when i try to put the same info into a variable and use it I get
I do understand that yea this method won’t work long term either way for that very reason. I’m just taking this one small step at a time so I know what I’m doing is working every step of the way if that makes sense
when the auth does expire i get an auth error within my test. not from the script itself
LM User
·3 years agoBecause your auth token from Postman expired. The pre-request script that you have in postman has to be built in groovy so that your signature is generated at runtime. AFAIK, authenticating using jsessionid will not work because those sessions also timeout.
No doubt, this would be easier in python, but you have to use groovy because you’re trying to expand the functionality of an existing groovy script. So, groovy is the right way to go.
domenicbetters
OP3 years agoSo I began trying to port over what I have done with postman into my groovy script. (I know you said you had much better success using python, but I still wanna give it a try first)
Interesting note. I was able to successfully make a get request using this line.
def http_body = httpClient.get(endpoint_url, ['Authorization': ‘{{the authtoken copied from postman','Content-Type': 'application/json','Cookie':'JSESSIONID=17513E3CB1FC4E623F829A629A16B811'] );
now when i try to take everything in those square brackets for the header and put them into a variable and use them the same way
def http_body = httpClient.get(endpoint_url, headers);
i get an error. interesting.
domenicbetters
OP3 years agoThanks for the heads up about replace vs. refresh. I was using refresh but I’ll switch it
LM User
·3 years agoIf you’ve got postman working, you’re doing well. That’s where most people trip up.
So, you need to construct this URL to do your patch:
/device/devices/{deviceId}/devicedatasources/{hdsId}/instances/{id}To do that, you need three variables: deviceId, hdsId, and id. Sounds like you already have deviceId.
To get hdsId (hardware datasource id, don’t ask why it’s called that, it doesn’t matter), you’ll need to do a GET on :
/device/devices/{deviceId}/devicedatasources?filter=dataSourceName:"Microsoft_SQLServer_GlobalPerformance"&fields=id,dataSourceNameThe response will give you, as you’ve seen, the list of datasources on that device, but with the filter specified above, you should only get a response if there’s been an instance discovered on the device whose device ID is {deviceId}. If there’s not one there, you have chosen the id of a device that doesn’t have an instance for that datasource.
From there, you can use the id from the response (which is different for every device, yay!) to get the list of instances that you will be attempting to patch. Do a GET on:
/device/devices/{deviceId}/devicedatasources/{hdsId}/instancesThe id shown in the response for each instance is the id that should go in the PATCH url.
Also, when you go to do your patch, there’s a query parameter: opType that will affect how your patch works, especially when it comes to properties. The swagger docs don’t even tell you what the possible values are much less what they do, so here it is:
So, the possible values are add, replace, and refresh.
Add - You specify the properties you want to add. Existing properties are untouched. New properties are added. If your payload has a property that already exists, that property isn’t modified.
Replace - You specify the properties you want to update. New properties are added. If your payload has a property that already exists, that property is updated to match your payload.
Refresh - This is the nuclear option which will delete all properties and replace them with the properties in your payload.
You’d probably want to do replace.
domenicbetters
OP3 years agoThanks again for pointing me at the API methods. I’m messing around in postman first to make sure i can do all the things I want. I’m still struggling a bit to find the correct datasource though.
I’m at the right device using
get device/devices/{id}/datasources
From there i have a list of all the datasources but i dont see the one that i need.
Microsoft_SQLServer_GlobalPerformance. I assume I’m misinterpreting how this should work again, but unsure.
**edit** did a little bit of of digging and was able to find the datasourceid even though it wasn’t in that list i got from the get method
**edit 2** I succesfully patched the property i wanted to patch through postman. now to use it in logic mon
domenicbetters
OP3 years agoI did put a support ticket requesting just that. an example of a patch in groovy. lets see what they say
LM User
·3 years agoYeah, except that the problem is that you already have this datasource collection script already gathering the data, you just have no easy way to push it back into LM. LM should provide Groovy examples of doing PATCHs. All the examples I have are for GETs, not PATCHs.
domenicbetters
OP3 years agoNo this helps a good bit. Thanks. Time to bust out some python
LM User
·3 years agoOk, well, it can’t be through any of the built in methods since they will only allow you to output numerical data. So, unless you can somehow map the value of “blocker” to a number, your best bet will be to write a block of code that takes the value of “blocker” and either writes it as a property or the instance description or something. We’re back to this. You’ll need to write a block of code to go in the collection script to PATCH each instance with the property/value of “blocker”.
LM has pretty good Python and Go SDKs, but they don’t have an SDK for their native Groovy language, so you’ll have to write this all out on your own. Even the examples on their website mostly use Python instead of Groovy to access their own API. (Ug, why isn’t python natively supported in LM yet?) You might put in a ticket to support to see if they have examples in Groovy that they can point you to. I gave up trying to get Groovy to work and most of my custom datasources are written in python.
I realize this doesn’t help that much, but it’s the direction you’ll probably have to go.
domenicbetters
OP3 years agoAlso it is greyed out and doesn’t give me the option to switch it if i wanted.
. Do you have the collection script querying for the blocker, you just need something to do with it?
Yes exactly. The collection script can pull the desired info. I am just unclear what the best approach is to display that to the user through alerts and the triggered emails
LM User
·3 years agoAlso it is greyed out and doesn’t give me the option to switch it if i wanted.
Yeah, you can’t change it once the DS is created. You wouldn’t want to since the code is written for batchscript, not script.
Restate your objective here? You want to write a property during every collection to the instance. instanceProps.get() isn’t going to do that. Do you have the collection script querying for the blocker, you just need something to do with it?
domenicbetters
OP3 years agoOOTB.
I was tweaking it but still using the OOTB
LM User
·3 years agoAh that explains why it’s not working. Batchscript creates one task meant to collect all the data for all the instances. instanceProps.get() doesn’t know which instance to get the property from. Is this an OOTB DS or custom?
domenicbetters
OP3 years agoIt’s set to batchscript. Should it be script?
Also it is greyed out and doesn’t give me the option to switch it if i wanted.
LM User
·3 years agoWait, is this batchscript or script? Look above discovery at the “Collector” field.
LM User
·3 years agoNo, that’s the right place. instanceProps.get(“blockerdetails”) should do it. What’s your collection script look like (don’t forget to redact any keys)?
domenicbetters
OP3 years agoPerhaps I am setting what I think are the instance props in the wrong spot.
I’m trying to specifically get to this prop here.
For reference I have the host prop “blockerdetails” set as middle level so i can see when im calling the whole server prop vs what i assumed was the instance.
My apologies if this is some no brainer stuff I should have know in the first place. I was kinda thrown into the deep end here at work, hahahaha
LM User
·3 years agoCollector attribute script = collection script. *Props.get() methods should work there. Do the properties exist on your instances?
domenicbetters
OP3 years agoI was trying to run it in the collector attribute script as a test. I assume it wouldn’t work there then you are saying.
LM User
·3 years agoNo, that should work if the property exists. You can’t call instanceProps.get() from discovery though, only collection. To verify the properties are present, you can look up the task details in the collector debug console. Here are the docs on the *Props.get() methods.
domenicbetters
OP3 years agoIs there some extra syntax to calling props from an instance?
I tried instanceProps.get(“nameofprop”) and it returns null
Do I need to do something like instanceProps.get(“instance.name.nameofprop”) ?
LM User
·3 years agoI don’t think hostProps.get() does anything in production. Even if it did, you’d want instanceProps.set(), but AFAIK that doesn’t do anything either. There are two ways to write a property to an instance: 1) discovery and 2) API (or you could hack the collector).
Discovery:
If you want, your discovery script can set/update instance level properties every time it runs. The most frequent you can force this to run is every 15 minutes, so that may not work. However, if you want to go that route, you’d need to include something in the 5th term of each discovery script output line. Each output line has 5 terms, separated by double hashtags. The last is a url parameter style list of properties. So, if your discovery right now is outputting:
id##name##description
You’d need it to output:
id##name##description####prop1=value1&prop2=value2
Where prop1 and prop2 are the names of the properties you want to add and value1 and value2 are the values of those properties, respectively. More here.
API
If you need it more often, you would need a function in your collection script to call the API to patch the instance’s properties. The UI does this with a PUT on the instance, so you’d have to have the entire definition of the instance in json format so you could update it. There is a PATCH option, which might fit your use case better. All of this requires now that your DS have access to API credentials as properties. All of that is doable, but I haven’t done it before. In this case, you’re basically replicating what instanceProps.set() would do.
Put in another feedback ticket for instanceProps.set() (and hostProps.set()).Also, this is exactly what LM Actions should do, if it ever becomes more than an idea.