How to Automate Logging of Asset Value Changes External to Assets

Still need help?

The Atlassian Community is here for you.

Ask the community


 

Platform Notice: Cloud - This article applies to Atlassian products on the cloud platform.

   

Summary

Assets for JSM lacks a Global Audit Log to review Attribute Value Changes made to Objects. To review changes via the UI, the individual objects must be viewed to see their history.

In this article, Automation Rules that log these changes will be covered.

Environment

Jira Cloud - JSM - Assets

Solution

Assets have a REST API, which will be used to achieve our goal. There are many steps to configure this functionality.

This will be achieved using Automation for Jira's Send Web Request Action with the Get Object History Endpoint. This endpoint will enable accessing the values & attributes that have been modified as well as the actor's information.

It is advised that a Service Account is used for this process, rather than a privileged account. Service Accounts are just accounts that have been set up to handle processes that shouldn't be attributed to or actioned by a single user.

This account does not need administrative permissions on Assets. It requires a JSM License to access the Assets REST API. Alternatively, the credentials can be set to Hidden in the Automation for Jira Send Web Request Action.

This should secure them against other privileged users using the credentials for other purposes.

Step 1: Basic Authentication for REST API

To start, Basic Authentication will need to be configured, as mentioned in the following Article. It is not necessary to base64 encode the string since Automation can do that for us:

It is important to note that the string email:api-token combined is enough to authenticate with Jira. This means that this string, and the base64 encoded version, can be as valuable as the account's password.

Now that the API Token has been generated at https://id.atlassian.com/manage-profile/security/api-tokens by the account that will be performing these actions, on to the next step.


Step 2: Acquire Assets' Workspace ID

Then, the Site's Assets Workspace ID has to be acquired. This can be done using the process described in this Article:

A Workspace ID is how Atlassian references a Site's instance of Assets. An Assets REST API Endpoint is typically https://api.atlassian.com/jsm/assets/workspace/<workspace-id>/v1.

Either a program like curl can be used to action the call to get the ID, or Automation for Jira's Send Web Request Action can do it, while a Log Action can put it in that rule's Audit Log for reference later.

Step 3: Putting it all together into an Automation Rule

Here is the final rule in text form:

Trigger: Object → Updated in <Schema> - This article covers Updated. Considerations regarding Created & Deleted will be talked about at the end.

Action: Send Web Request → GET Assets REST API Object History Endpoint

Send Web Request Details - Click here to expand...
Send Web Request Action Details
Web request URL*: https://api.atlassian.com/jsm/assets/workspace/<workspace-id>/v1/object/{{object.id}}/history
Headers:
  Authorization: Basic base64Encode("user@domain.com:<api-token>")     Check Hidden           ✓ Hidden
  Content-Type: application/json                                       Do not check Hidden    X Hidden
Method: GET
Web request body*: Empty
Wait for response:
  Check this         ✓ Delay execution of subsequent rule actions until we've received a response for this web request
  Do not check this  X Continue running the rule even if the request response is not successful (i.e. non-200 response)


The above Action will return the History of the Object. Be sure to insert the <workspace-id> that was acquired in Step 2 into the Web request URL. And to replace <api-token> with the one created earlier in Step 1 for the Authorization Header.

The following Action depends on the end destination for this information. One option is to use the Send Email Action to deliver it to a receiving mailbox.
Another is to use the Send Web Request Action, such as if the information needs to be received and ingested by a system.
The Send Email Action will be used in this case, since the prior Action shows how to utilize Send Web Request.

If Send a Web Request is used instead, much of the formatting in the next Action is not necessary, since the Response from the REST API is already in JSON format.

Action: Send Email →

Send Email Details - Click here to expand...
Send Email Action Details
To: {{object.Contact}}
Subject: {{object}} {{object.Label}} in {{object.objectType}} of Assets' {{object.objectSchema}} Schema updated 
Content:
  Hello,

  There has been an update to {{object.key}} {{object.label}}'s attribute values.

  {{#webResponse.body}} 
    {{#if(asDateTime(created.left(21).concat("+0000")).isAfter(now.minusMinutes(5)))}}
      {{^first}}----{{/}}
      Timestamp & User: On {{asDateTime(created.left(21).concat("+0000")).longDateTime}} by User {{actor.displayName}}
      Attribute: {{affectedAttribute}}
      Old Value: {{oldValue}}
      New Value: {{newValue}}
    {{/}}
  {{/}}

  Thanks,
  Asset Logging Automation


I will do my best to break this down from top to bottom so that what is happening is clear.

  1. The To: has a Smart Value referencing the Object that was updated. It is trying to access a Contact Attribute on that Object. The idea is that the Contact would contain an Email so that whoever managed that Object will receive the Log Information. This makes the destination of the log data dynamic in nature.
  2. The Subject: will evaluate to something like ABC-123 FrontDeskPC in Workstations of Assets' Devices schema updated. See the image below for an example.
  3. The Content: (or Body:) is going to evaluate to: 


Content Body Example - Click here to expand...
Content Body Example
  Hello,

  There has been an update to {{object.key}} {{object.label}}'s attribute values.

  Timestamp & User: On February 2, 2023 6:23:12 AM EST by User Payden Pringle
  Attribute: Hostname
  Old Value: DESKTOP-ABCD1234
  New Value: FrontDeskPC
  ----
  Timestamp & User: On February 2, 2023 6:23:12 AM EST by User Payden Pringle
  Attribute: IP Address
  Old Value: 192.168.0.100
  New Value: 192.168.1.102
  ----
  Timestamp & User: On February 2, 2023 6:23:12 AM EST by User Payden Pringle
  Attribute: RAM
  Old Value: 16GB
  New Value: 32GB

  Thanks,
  Asset Logging Automation 

With that example, let's break down why it works, from top the bottom:

  1. The first sentence is fairly simple: the Key & Label for the Triggering Object are evaluated.
  2. #webResponse.body is the bulk of data received from the REST API Endpoint. Putting a # at the front means that it will cycle through each sub-item within the body. i.e. each history log entry and perform what is within the block for each entry.
  3. #if(asDateTime(created.left(21).concat("+0000")).isAfter(now.minusMinutes(5))) is the most complicated portion of this Rule. It is checking if the DateTime on the history log entry was made within the last 5 minutes. It's doing this by turning the DateTime on the entry into an actual DateTime, rather than a String, by parsing it into the right format, then using asDateTime(). Once that's done, it uses isAfter() to check it against what is returned by now.minuteMinutes(5). The # here is not evaluating through a list, but instead saying that if the evaluation returns true, then take the actions within the block. In other words, if it's false, it won't output the information. If it's true, it will. This must be done because there's no way to filter the Object History from the Endpoint, so it has to be filtered here. Only newly modified attributes are desired, rather than the most recent 50+ changes. This could be modified to return the full History instead and it would be far simpler. This will create a lot of repeat information if it is done.
  4. ^first is saying "do what's inside this block for every iteration, except the first iteration", which is just a nice way to make sure the ---- only appears between entries, and not at the top.
  5. Finally, our goal. Again, the history log entry's created DateTime is being formatted into an actual DateTime from a String, then turning into more friendly text. It returns the Actor's name who actioned the value update on the Object, the Attribute's name, the old value, and the new value.

And it's done. The History of an Object is being sent out to be logged elsewhere. Such as in a mailbox where it can be easily searched or potentially to a reporting system that can process the data.


Created & Deleted Objects

For the Create Event, the Object History will contain the User who created the Object, so it is useful to use the Send Web Request Action on the Object History Endpoint to get this information.

For the Delete Event, the Object no longer exists, so trying to acquire the Object History will simply fail. The information available to be sent will be accessible through the object smart value provided by the Trigger.

We cannot use that Smart Value to attain the History, which is why the API is required to get this information. This means that the structure of each Rule will be somewhat different, if one is created for each Event Type.

Considerations

Upkeep - Many Rules are Required

Due to how the Object Trigger works, a single rule like this will only handle one of the three event types (Create, Update, Delete) for a single Schema.

This means 3 of these rules are required for each Schema if all events are desired. If there are 5 Schema, that's 15 rules.

It is not advised to try to combine these rules as Automation for Jira has Service Limits that are likely to be hit in that case. It is better to spread out the executions among many rules to avoid having them Throttled.

Assets Discovery and other Imports

Imports can cause many Object Events in a short period of time. It is advised to disable these rules before the imports occur.

The information the Rules would log is likely recorded elsewhere (the source of the import).

Future Improvements

We have open Feature Requests that, if implemented, will enhance what is possible with this functionality, such as:

With these Requests open, Conditions must be used if these rules are meant to only fire in certain circumstances, rather than all circumstances.

Requests and Bugs like these can be found on our Public Issue Tracker.

Further Reading


Last modified on Jan 29, 2024

Was this helpful?

Yes
No
Provide feedback about this article
Powered by Confluence and Scroll Viewport.