Dynatrace Workflows
use the Jinja templating engine to allow for dynamic configuration in Workflows. Those expressions provide, for example, access to execution parameters, schedule information, event context.
Expressions are supported in any input of any action (unless specified otherwise by the action developer) and the custom task conditions and options. A notable exception is the Run Javascript action, which does not support expression in its input to avoid the possibility of code injection. Instead, SDKs are provided to access the same information directly in JavaScript. For more information, see Run JavaScript action for Workflows.
Expressions enable you to reference results from predecessors in a task configuration and define custom conditions.
There are two kinds of delimiters.
{{ ... }} for expressions, it's used to access data from other tasks, executions, and functions.{% ... %} for statements and control structures like loops and if conditions.The list below is a reference list of the expressions available.
Beyond that, you can use any templating functionality Jinja provides.
Whether you use the REST API or expressions to access workflows, executions, scheduling rules, or calendars, the same arguments are available to control paging, filtering, ordering, search, and expansion.
/executions?name=foo&limit=50&offset=0 behaves the same as {{ executions(name='foo', limit=50, offset=0) in the same user context. Mind that while the REST API request returns JSON, an expression will return a native object, which is its string representation.
Access to resources happens in the context of the workflow actor (link to workflow settings).
calendars()Access business calendars and their attributes. The behavior and result are similar to an API request for the /business_calendars endpoint.
id was specifiedExamples:
# access to calendar '854a3452-a6d3-45bd-83e1-e8afa56eabcd'{{ calendars('854a3452-a6d3-45bd-83e1-e8afa56eabcd') }}
{"id": "854a3452-a6d3-45bd-83e1-e8afa56eabcd","created_at": "2022-12-07T12:42:45.732902Z","updated_at": "2022-12-20T09:36:40.303952Z","title": "Sample Calendar","weekstart": 1,"weekdays": [1,2,3,4,5],"holidays": [{"date": "2020-01-01","title": "New Year"},{"date": "2020-01-07","title": "International Programmers Day"}],"valid_from": "2020-01-01","valid_to": "2029-12-31",}
connection()Get a single connection by schema ID and connection name.
Examples:
# get the settings object ID by schema ID and name{{ connection('app:dynatrace.slack:connection', 'My Slack Connection') }}# get the settings object ID by schema ID and name from a previous task result{{ connection('app:dynatrace.slack:connection', result('get_my_config')['slack']['connection']) }}
environment()Information about the environment, for example, id or url.
id and environment url.Examples:
{"id": "xyz12345","url": "https://xyz12345.apps.dynatrace.com"}# get the full environment object{{ environment() }}# access current environment id{{ environment().id }} # -> "xyz12345"# access current environment url{{ environment().url }} # -> "https://xyz12345.apps.dynatrace.com"
event()Examples:
# Assuming the following sample event as a trigger for a Workflow Execution{"type": "security-problem-event","version": "0.0.2","event.id": "20d01063-14d7-4b99-aef1-77e92962fb7f","event.kind": "SECURITY_PROBLEM_EVENT","event.name": "Vulnerability resolved","event.type": "THIRD_PARTY","event.status": "Resolved","event.category": "Vulnerability","security-problem.display-id": "S-3750","security-problem.technology": "go","security-problem.vulnerability-type": "third-party"}# get the full event object{{ event() }}# access only the type attribute{{ event('type') }} # -> "security-problem-event"# access the full event object and then select the type attribute{{ event().type }} # -> "security-problem-event"# trying to access the 'technology' attribute of the 'security-problem' object# this will FAIL, as there is actually no 'security-problem' object in the example above{{ event().security-problem.technology }} # -> ERROR# NOTE: events often do not contain structured objects but flattened objects# the 'security-problem.technology' for example is a single attribute, that happens to have a "." in its name# in order to access attributes with "." in its name, use square brackets instead []{{ event()['security-problem.technology'] }} # -> "go"# as an alternative, use the get() function{{ event().get('security-problem.technology') }}# the get function also allows for default values, if the attribute is not defined{{ event().get('does.not.exist', 'my default value') }}
execution()Access the current execution context.
Examples:
# access the current execution object{{ execution() }}# access ID of current executionExecution ID {{ execution().id }}# access input of current executionExecution input {{ execution().input | default('no input defined') }}
Sample result
{"id": "b126f1cc-fce1-4d28-adcf-0805b98961b9","state": "RUNNING","started_at": "2022-12-21T18:33:11.084818Z","ended_at": null,"runtime": 12,"user": "716535cb-b992-47e4-90be-7d4b9785a501","title": "Get Security Problem events count","link": "https://xyz12345.apps.dynatrace.com/ui/apps/dynatrace.automations/executions/b126f1cc-fce1-4d28-adcf-0805b98961b9","workflow": {"id": "ef1f1ca4-1d6b-4c7d-b964-5f09096d77cd","title": "Get Security Problem events count","actor": "716535cb-b992-47e4-90be-7d4b9785a501","link": "https://xyz12345.apps.dynatrace.com/ui/apps/dynatrace.automations/workflows/ef1f1ca4-1d6b-4c7d-b964-5f09096d77cd"}}
executions()Access executions and their attributes. The behavior and result are similar to an API request for the /executions endpoint.
id was specifiedExamples:
#execution with ID 00c27c88-d066-4424-bc44-f49a75536e37 {{ executions('00c27c88-d066-4424-bc44-f49a75536e37') }}#executions of the workflow with ID '81a7fbb5-7f69-4005-81ec-26a39272bf09' (default limit applies for resultsize){{ executions(workflow='81a7fbb5-7f69-4005-81ec-26a39272bf09') }}#returns up to 5 executions ordered by created_at in descending order{{ executions(limit=5, order='-created_at') }}# returns the most recent execution of workflow '81a7fbb5-7f69-4005-81ec-26a39272bf09'{{ executions(workflow='81a7fbb5-7f69-4005-81ec-26a39272bf09', limit=1, order='-created_at') }}
input()Access the input of a workflow execution. The input is a merge of the default workflow inputs and the inputs provided at runtime.
Examples:
# input of workflow execution{{ input() }}# input attribute 'foo'# the input function allows to only return part of the object{{ input('foo') }}# alternatively, the whole input object can be returned and afterward access the attributes of the object# if the input object does not have an attribute "foo", the expression will fail with an "Undefined variables" message{{ input().foo }}# both options can be combined, for example, with a default filter (see filters){{ input().foo | default('foo is not defined') }}`# however for the following, at least the "foo" attribute needs to exist otherwise the same error will occur{{ input().foo.bar | default('foo is not defined') }}`# the get function can also be used to access object attributes{{ input.get('foo', 'foo is not defined') }}
now()The current timestamp in UTC or time zone is provided as the input parameter. The expression returns a native Python datetime object to allow formatting into any desired output. Use strftime and format codes as described in datetime Python documentation.
Examples:
# current time in UTC{{ now().strftime("%b %d %Y") }}# current time in Europe/Vienna{{ now('Europe/Vienna').strftime("%b %d %Y") }}
Sample result
2022-12-21 18:30:26.518253+00:00
timedelta()Difference between two datetime values. Expression returns native Python timedelta object to allow time calculations when working with now(). The JSON representation of this expression is the number of total seconds.
Examples:
# calculate yesterday by substracting one day{{ now() - timedelta(days=1) }}# offset current date and time by one week{{ now() + timedelta(weeks=1) }}# offset current date and time by one hour, thirty minutes, and fifteen seconds{{ now() - timedelta(hours=1, minutes=30, seconds=15) }}# double the timedelta value and increase it by five minutes{{ timedelta(hours=1)*2 + timedelta(minutes=5) }}
result()Access the result of a preceding task within the workflow.
Examples:
# result of 'task_1' in the workflow{{ result('task_1') }}# result attribute 'foo' of 'task_1'# the result function allows to only return part of the object# if the result object does not have an attribute "foo", the function will fail with an "Undefined variables" message{{ result('task_1.foo') }}# alternatively, the whole result object can be returned and afterwards access the attributes of the object# if the result object does not have an attribute "foo", the expression will fail with an "Undefined variables" message{{ result('task_1').foo }}# both options can be combined, for example, with a default filter (see filters){{ result('task_1').foo | default('foo is not defined') }}`# however for the following, at least the "foo" attribute needs to exist otherwise the same error will occur{{ result('task_1').foo.bar | default('foo is not defined') }}`# the get function can also be used to access object attributes{{ result('task_1').get('foo', 'foo is not defined') }}
scheduling_rules()Access Scheduling rules and their attributes. The behavior and result is similar to an API request for the scheduling_rules endpoint.
id was specifiedExamples:
# access scheduling rule 'a941f679-a6ee-4f33-9914-b15ea32eb326'{{ scheduling_rules('a941f679-a6ee-4f33-9914-b15ea32eb326') }}
Sample result
{"id": "a941f679-a6ee-4f33-9914-b15ea32eb326","created_at": "2022-12-07T12:42:45.806740Z","updated_at": "2022-12-07T12:42:45.806755Z","title": "First working day of the week","rule_type": "rrule","rrule": {"freq": "WEEKLY","bysetpos": [1],"interval": 1,"datestart": "2020-10-01","byworkday": "WORKING"},"business_calendar": "8b31fda3-3227-431c-aafe-aa849894010b"}
scheduling_rules_includes()Verify if a date is part of the selected dates of a scheduling rule.
You provide a scheduling rule identifier and a date as input, and the expression will evaluate to a Boolean (True or False). This can be used to implement logic in a task condition to control the workflow. The verification is done for a one year outlook maximum.
Examples:
# verify if scheduling rule 'd6882cd1-9af4-41f4-801a-f2999a54aded' includes '2021-06-04'{{ scheduling_rules_includes('d6882cd1-9af4-41f4-801a-f2999a54aded', '2021-06-04') }}# verify if scheduling rule 'd6882cd1-9af4-41f4-801a-f2999a54aded' includes '04.06.2021'{{ scheduling_rules_includes('d6882cd1-9af4-41f4-801a-f2999a54aded', '04.06.2021', format='%d.%m.%Y') }}
scheduling_rules_preview()Preview the next n dates for a scheduling rule. You can configure a start timestamp and count of dates you would like to retrieve. Optionally, you can also provide a formatting hint on how to interpret the provided start date.
Examples:
# next dates for scheduling rule 'd6882cd1-9af4-41f4-801a-f2999a54aded'{{ scheduling_rules_preview('d6882cd1-9af4-41f4-801a-f2999a54aded') }}# next 10 dates for the schedule 'd6882cd1-9af4-41f4-801a-f2999a54aded' starting from '04.06.2021'{{ scheduling_rules_preview('d6882cd1-9af4-41f4-801a-f2999a54aded', start='04.06.2021', count=10) }}# next 10 dates for Schedule 'd6882cd1-9af4-41f4-801a-f2999a54aded' starting from '04.06.2023' with explicit format hint{{ scheduling_rules_preview('d6882cd1-9af4-41f4-801a-f2999a54aded', start='04.06.2023', format="%d.%m.%Y", count=5) }}# format the first returned date object following pattern %d.%m.%Y{{ scheduling_rules_preview('d6882cd1-9af4-41f4-801a-f2999a54aded', start='04.06.2023')["next_executions"][0].strftime("%d.%m.%Y") }}
Sample result
{"valid": true,"next_executions": ["2023-06-05","2023-06-12","2023-06-19","2023-06-26","2023-07-03"]}
seconds_before()Returns the number of seconds until clock_time in timezone_name. If the time in timezone_name is < clock_time, and 0 otherwise.
Examples:
# get the number of seconds between now and 2 pm in New York# this returns 0 if it's already past 2 pm in New York# if it's currently 1 pm, returns 3600{{ seconds_before('14:00:00', 'America/New_York') }}
task()Access to a task and its attributes within an execution. This allows you to access information about any other task within the same workflow.
Examples:
# details of 'task_2' in the workflow{{ task('http_request_1') }}
Example task object
{"id": "http_request_1","name": "http_request_1","input": {"url": "http://www.google.com","method": "GET","headers": "","payload": ""},"state": "SUCCESS","action": "dynatrace.automations:http-function","active": true,"result": {"body": "... ","headers": {"date": "Wed, 21 Dec 2022 17:06:57 GMT","server": "gws","domain=.google.com; HttpOnly","content-type": "text/html; charset=ISO-8859-1","cache-control": "private, max-age=0","x-frame-options": "SAMEORIGIN","x-xss-protection": "0","cross-origin-opener-policy-report-only": "same-origin-allow-popups; report-to=\"gws\""},"status_code": 200},"runtime": 1,"ended_at": "2022-12-21T17:06:58.155478Z","position": {"x": 1,"y": 1},"execution": 'c4ff0688-0100-4618-93bd-e157b9a9280e',"conditions": {},"started_at": "2022-12-21T17:06:56.390869Z","state_info": "","predecessors": [],"triggered_by": [],"condition_results": {}}
workflows()Access workflows and their attributes. The behavior and result are similar to an API request for the /workflows endpoint.
id was specifiedExamples:
# return workflow with ID '39e054c0-cfa1-4c67-bdda-497ed20f08f3'{{ workflows('39e054c0-cfa1-4c67-bdda-497ed20f08f3') }}# 5 most recently updated workflows{{ workflows(limit=5, order='-updated_at') }}
problem_link()Outputs URL to a Davis problem in the
Problems. This expression evaluates correctly in workflows with Davis Problem event triggers only.
Example:
# URL to a Davis problem "https://xyz12345.apps.dynatrace.com/ui/apps/dynatrace.davis.problems/problem/20d01063-14d7-4b99-aef1-77e92962fb7f"{{ problem_link() }}
workflow_execution()Alias for execution()
A control structure is anything that influences the flow of a template. We use it for conditions and loops. Control structures need to be inside {% … %} blocks and typically span multiple lines.
For loops iterate over each item in a list.
Basic example with static listExample assume you have a previous task called 'get_error_logs' which has a list of logs in its 'records', and each item is an object with multiple attributes, among which 'status' and 'content':
We found the following logs:{% for log in result('get_error_logs').records %}- {{ log.status }}: {{ log.content }}{% endfor %}
Inside a loop block, there are additional variables available. For a full list, see Jinja documentation.
The example generating an insert SQL statement from a list of valuesControl structures can also be combined, for example, to iterate a list of lists and create a table-like structure for a notification.
The DB query returned:{% for row in result('get_entries_in_db').rows %}Row {{loop.index}}:{% for element in row %}Value: {{element}}{% endfor %}{% endfor %}
If no iteration occurred because the sequence was empty or the filtering removed all the items from the sequence, you can render a default block using else.
{% for log in result('get_error_logs').records %}- {{ log.status }}: {{ log.content }}{% else %}no logs found{% endfor %}
The if statement can be used to test if a variable is defined, not empty, or a comparison is true.
Example to check if there is an event object provided:
{% if 'event' in execution().params %}this execution was triggered by event{% endif %}
or even simpler by using the event() shortcut
{% if event() %}this was triggered by event{% endif %}
The if statements also offer an optional else if elif and else block:
{% if result('get_ssl_certificate').expires_in_days <= 14 %}We need to renew our certificate{% elif result('get_ssl_certificate').expires_in_days <= 30 %}Let's think about renewing our certificate{% else %}All good{% endif %}
Variables can be modified by filters using a pipe symbol (|) and may have optional arguments.
For example, {{ my_name | capitalize }} the first character will be uppercase, all others lowercase.
Use parentheses to apply arguments to filters, just like with a function call. For example, {{ my_list | join(', ') }} will join a list with commas and whitespace in between.
For the complete list of filters, see Jinja List of Builtin Filters documentation.
base64decodeDecodes a valid string or bytes object containing a base64 string to a string.
base64encodeEncodes a string or bytes object to a base64 string.
boolReturns true if the expression is truthy.
Example: {{ 'foo' | bool }} will return true.
capitalizeModifies the expressions so that the first character is uppercase and all others are lowercase.
defaultIf the value is undefined, it will return the provided value; otherwise the value of the variable.
Example: {{ my_variable | default('my_variable is not defined') }}.
dict2itemsConverts a dict to a list of key-value pairs in the form of { 'key': 'keyValue', 'value': 'valueOfPair' }.
Example: {{ {'a': 1, 'b': 2} | dict2items }} returns [{ 'key': 'a', 'value': 1 }, { 'key': 'b', 'value': 2 }]. With the key_name and value_name arguments, you can change the key-value key of the return value.
firstReturns the first item of a list.
format_numberFormats a number. By default, the format is 1,234,567.89. You can change the thousand separator and the amount of trailing digits.
Examples:
{{ 3.14159 | format_number(trailing_digits=0) }} results in 3.{{ 3.1415 | format_number(trailing_digits=3, decimal_separator=',') }} results in 3,142.{{ 1234 | format_number(thousand_separator='_') }} results in 1_234.00.from_jsonConverts a json string back to a Python object.
from_yamlConverts a yaml string back to a Python object.
items2dictConverts a list of key-value pairs to a dict.
Example: {{ [{ 'key': 'a', 'valueA': 1, 'valueB': 7 }, { 'key': 'b', 'valueA': 2, 'valueB': 8 }] |items2dict(value_name='valueA') }} returns {'a': 1, 'b': 2}. The arguments key_name and value_name can modify which keys are used to construct the dict.
joinReturns a string that contains the concatenation of the items in a list.
Example: {{ ['a', 'b', 'c'] | join('=') }} returns a=b=c.
json_querySelects a single value or structure from JSON data. The filter builds on top of the JMESPATH library. For examples and interactive tryout, see the JMESPATH documentation.
Example: {{ { 'key': 'a', 'value': { 'nestedValue': 'b' }} | json_query('value.nestedValue') }} returns b.
lastReturns the last item of a list.
lengthReturns the number of items in a list, or the number of characters in a string.
lowerConverts a string to lowercase.
md_tableConverts a DQL query result or a list of objects into a markdown table. Optionally, pass a list of column names to select specific columns. Use the header keyword argument to provide custom header labels (must match the number of columns). {{ result('query_task') | md_table }} converts all columns and uses field names as table header. {{ result('query_task') | md_table(['host.name', 'state'], header=['Host', 'Status']) }} selects specific columns as table header.
regex_findallGets all matches in a string.
Example: {{ 'DynaTrace' | regex_findall('[A-Z]') }} returns ['D', 'T'].
regex_replaceReplaces a part of a string with another part.
Examples:
{{ '1a3' | regex_replace('\\d', 'X') }}{{ 'Hello World' | regex_replace(' \\w+$', ' Moon') }}Additional keyword parameters for regex_replace:
count: Maximum number of pattern occurrences to replace. The default value is zero, which replaces all occurrences.mandatory_count: Aborts the evaluation if fewer occurrences have been replaced.regex_searchPerforms a search on the string and extracts the string (or list of strings) that matches the regular expression.
Examples:
{{ 'a12b34' | regex_search('b\\d\\d') }}\\g{groupName} or specified by the index with \\{groupIndex} (0 is always the whole match). The result is a list of matches according to the passed groups.{{ 'a12b34' | regex_search('b(?P<firstDigit>\\d)\\d', '\\gfirstDigit') | first }}{{ 'Hello World' | regex_search('^(?P<FirstWord>\\w+) (?P<SecondWord>\\w+)$', '\\gSecondWord', '\\1') }}replaceReplaces all occurrences of a substring with a new one.
Example: {{ 'Hello World' | replace('Hello', 'Greetings') }} returns Greetings World.
ternaryReturns the first argument value if it is truthy; otherwise the second argument value is returned. The third argument none_val is optional; if provided, it is returned when the first argument value is None.
Examples:
{{ True | ternary("foo", "bar") }} returns foo.{{ False | ternary("foo", "bar") }} returns bar.{{ None | ternary("foo", "bar", none_val="fizz") }} returns fizz.to_datetimeConverts a string to a datetime-object.
{{ "2019_08_01" | to_datetime('%Y_%m_%d') }}.{{ "2019-08-01T12:30:00Z" | to_datetime(timezone='Europe/Vienna') }}. For a list of all available timezones, see tzdata.strftime() on the datetime-object returned by the filter {{ ("2019-08-01T12:30:00Z" | to_datetime(timezone='Europe/Vienna')).strftime("%m-%d-%Y %H:%M:%S") }}.to_jsonConverts an object to its JSON representation.
to_yamlConverts an object to its YAML representation.
trimStrips leading and trailing characters (by default whitespace characters are trimmed).
truncateReturns a truncated copy of the string. The first parameter specifies the length, which by default is 255.
uniqueReturns a list of unique items in a list.
upperConverts a string to uppercase.
validate_regexValidates if a variable value matches regex. This filter also overrides the expression value validation defined for the input by the app author.
Example: {{ 'example_string' | validate_regex('[a-z_]+') }}.
Workflows