12 Feb 2025
06:25 AM
- last edited on
17 Feb 2025
07:12 AM
by
MaciejNeumann
Heya,
Most of us are using SSL cert extension but the catch here is that we cannot use Auto-tagging rules directly for certs to put on some tags to the crets. However, the certs are most of the time discovered from hosts so why not inherit the tags from the hosts itself?
Here is how you can do it with a simple workflow.
Step-1:
Get the certs that do not have the required Tags.
We can use simple DQL for this
fetch `dt.entity.python:certificate_monitor_certificate`, from: now()-7d
| fieldsAdd host_att, host_port, tags
| fieldsAdd tagStr = toString(tags)
| filterOut (matchesPhrase(tagStr,"Application") and matchesPhrase(tagStr,"Environment") and matchesPhrase(tagStr,"SnowAppId"))
Step-2:
Get the host on which these certs run and get the Tags from them.
fetch dt.entity.host
| filter id == "{{ _.item ['host_att'] }}"
| fieldsAdd tags
This is a step that uses the result from the previous step as a loop so go to options in the workflow step, select Loop task and in the list we mention the result of previous step like this.
{{ result("get_cert_details")["records"] }}
Step-3:
We use js here and the results from previous steps to place the extracted tags to each cert.
import { execution } from '@dynatrace-sdk/automation-utils';
import { monitoredEntitiesCustomTagsClient } from "@dynatrace-sdk/client-classic-environment-v2";
// Function to extract tags based on id
function getTagsById(data, id) {
for (const item of data) {
for (const record of item.records) {
if (record.id === id) {
return record.tags; // Return tags if id matches
}
}
}
return null; // Return null if id is not found
}
// Function to extract specific key-value pairs from an array of tags
function extractValues(array) {
const keysToExtract = ["Application", "SnowAppID"]; // Only include "Application" and "SnowAppID"
const result = [];
array.forEach(item => {
keysToExtract.forEach(key => {
if (item.startsWith(key + ":")) {
const value = item.split(":")[1]; // Extract value after the key
result.push({ key: key, value: value }); // Push key-value pair to result
}
});
});
return result;
}
export default async function ({ execution_id }) {
try {
const ex = await execution(execution_id); // Get the current execution details using the provided execution ID
const cert_res = await ex.result("get_cert_details"); // Get certificate details from the execution results
const certs = cert_res.records;
const host_tags_res = await ex.result("get_host_tags"); // Get host tags from the execution results
const required_substrings = ["Application", "SnowAppID"]; // Define required substrings to check in tags
for (const item of certs) {
const tagsPresent = required_substrings.every(substring =>
item.tags.some(tag => tag.includes(substring))
); // Check if all required substrings are present in the tags
if (!tagsPresent) {
const host_id = item.host_att; // Get host ID from certificate
const certTags = getTagsById(host_tags_res, host_id); // Get tags for the host
const postTags = extractValues(certTags); // Extract required key-value pairs from tags
console.log(postTags);
const entitySelector = `type("dt.entity.python:certificate_monitor_certificate"),entityName("${item['entity.name']}")`; // Create an entity selector for the certificate
console.log(entitySelector);
const data = await monitoredEntitiesCustomTagsClient.postTags({
entitySelector: entitySelector,
body: { tags: postTags },
}); // Post the extracted tags to the monitored entity
console.log(data);
}
}
return { triggeredBy: ex.trigger }; // Return the trigger information from the execution details
} catch (error) {
console.error("An error occurred:", error); // Log any errors that occur during the process
throw error;
}
}
voila once you run this all the crets discovered from hosts now are tagged with the required Tags.
(Make changes to this according to the Tags that you need to inherit from the host)
Adding the json of the workflow here,
metadata:
version: "1"
dependencies:
apps:
- id: dynatrace.automations
version: ^1.985.4
inputs: []
workflow:
title: certTagger
tasks:
post_tags:
name: post_tags
description: Build a custom task running js Code
action: dynatrace.automations:run-javascript
input:
script: >
// Optional import of SDK modules
import { execution } from '@dynatrace-sdk/automation-utils';
import { monitoredEntitiesCustomTagsClient } from
"@dynatrace-sdk/client-classic-environment-v2";
// Function to extract tags based on id
function getTagsById(data, id) {
for (const item of data) {
for (const record of item.records) {
if (record.id === id) {
return record.tags; // Return tags if id matches
}
}
}
return null; // Return null if id is not found
}
// Function to extract specific key-value pairs from an array of tags
function extractValues(array) {
const keysToExtract = ["Application", "SnowAppID", "Environment"];
const result = [];
array.forEach(item => {
keysToExtract.forEach(key => {
if (item.startsWith(key + ":")) {
const value = item.split(":")[1]; // Extract value after the key
result.push({ key: key, value: value }); // Push key-value pair to result
}
});
});
return result;
}
export default async function ({ execution_id }) {
try {
// Get the current execution details using the provided execution ID
const ex = await execution(execution_id);
// Get certificate details and host tags from the execution results
const cert_res = await ex.result("get_cert_details");
const certs = cert_res.records;
const host_tags_res = await ex.result("get_host_tags");
// Define required substrings to check in tags
const required_substrings = ["Application", "CFU", "SnowAppID", "Environment"];
// Iterate over each certificate
for (const item of certs) {
// Check if all required substrings are present in the tags
const tagsPresent = required_substrings.every(substring =>
item.tags.some(tag => tag.includes(substring))
);
// If not all required tags are present, fetch and add them
if (!tagsPresent) {
const host_id = item.host_att; // Get host ID from certificate
const certTags = getTagsById(host_tags_res, host_id); // Get tags for the host
const postTags = extractValues(certTags); // Extract required key-value pairs from tags
console.log(postTags);
// Create an entity selector for the certificate
const entitySelector = `type("dt.entity.python:certificate_monitor_certificate"),entityName("${item['entity.name']}")`;
console.log(entitySelector);
// Post the extracted tags to the monitored entity
const data = await monitoredEntitiesCustomTagsClient.postTags({
entitySelector: entitySelector,
body: { tags: postTags },
});
console.log(data);
}
}
// Return the trigger information from the execution details
return { triggeredBy: ex.trigger };
} catch (error) {
// Log any errors that occur during the process
console.error("An error occurred:", error);
throw error;
}
}
position:
x: 0
y: 3
predecessors:
- get_host_tags
conditions:
states:
get_host_tags: OK
get_host_tags:
name: get_host_tags
description: Executes DQL query
action: dynatrace.automations:execute-dql-query
input:
query: |-
fetch dt.entity.host
| filter id == "{{ _.item ['host_att'] }}"
| fieldsAdd tags
position:
x: 0
y: 2
predecessors:
- get_cert_details
conditions:
states:
get_cert_details: OK
concurrency: 1
withItems: item in {{ result("get_cert_details")["records"] }}
get_cert_details:
name: get_cert_details
description: Executes DQL query
action: dynatrace.automations:execute-dql-query
input:
query: >-
fetch `dt.entity.python:certificate_monitor_certificate`, from:
now()-7d
| fieldsAdd host_att, host_port, tags
| fieldsAdd tagStr = toString(tags)
| filterOut (matchesPhrase(tagStr,"Application") and
matchesPhrase(tagStr,"CFU") and matchesPhrase(tagStr,"Environment")
and matchesPhrase(tagStr,"SnowAppId"))
position:
x: 0
y: 1
predecessors: []
description: ""
trigger: {}
schemaVersion: 3
Thanks,
Maheedhar.