Welcome to the APAC Services Hands on Training session.

👉 Ensure you can login to your Dynatrace Managed tenant
👉 Ensure you can SSH into your VM.
👉 Ensure you can reach: http://52.200.197.144.nip.io/p/mac
👉 Once logged in. Go to Settings > Integration > Dynatrace API > Token Settings and enable the new API format.
👉 Generate an API Token with the following permissions:

New API Key format: Good article from Barbara Schachner
I have created an auto-tag called myFirstTag. Please help me retrieve the JSON representation of this tag.
The Dynatrace customer you've been assigned to sells SaaS to their end customers. Currently they have 3 customers: Customer A, Customer B and Customer C.
Your customer is currently running on AWS but they have plans to extend their service to Azure and / or GCP in the near future.
Your customer is planning a large marketing event in just over 1 month. They expect to have over 1000 customers within the first few months.
Your customer would like to use a single monitoring tool (Dynatrace) to monitor the health of their entire customer environment. They need to see statistics, health and metrics of individual customers views in addition to a "global" health dashboard for all customers.
Your job is to help them achieve this goal. You have less than 1 month to deliver this outcome.
Each participant should have received access to a Dynatrace environment. In this environment you'll find a single host connected.
You've received the following architecture diagram from your customer.

http://staging.customera.VMIP.nip.io
http://customera.VMIP.nip.io
http://staging.customerb.VMIP.nip.io
http://customerb.VMIP.nip.io
http://staging.customerc.VMIP.nip.io
http://customerc.VMIP.nip.io
👁️ Take 10 minutes and use the chat box to discuss with colleagues:
👁️ Has anyone delivered Dynatrace configuration as code to your customers? What was involved?
We will deliver all of this configuration to the customer as code.
The customer can then:
Dynatrace can then:

Due to the scale that this customer expects, every configuration we deliver must be automated. That said, we can build the template manually then use automation to build subsequent configuration.
Monaco is an open source project from Dynatrace to apply Monitoring As Code at scale.
The monaco tool is idempotent. It will only make changes if required. Re-run as many times as you wish.
The monaco binary is already available on the VM as ./monaco so either run it from the VM or download the Windows binary and run it locally. Your choice.
Go to https://github.com/dynatrace-oss/dynatrace-monitoring-as-code/releases and download the latest monaco release for your operating system.
For ease during this demo, please rename the binary to ./monaco (if on windows) or monaco if on macOS or linux.
monaco binary into this directoryprojectsenvironments.yamlYou should now have:
monaco binarymonaco binary called projectsmonaco binary called environments.yaml
Open environments.yaml and type the following:
mac_training_environment:
- name: "mac_training_environment"
- env-url: "{{ .Env.MAC_TRAINING_ENVIRONMENT }}"
- env-token-name: "TOKEN_MAC_TRAINING_ENVIRONMENT"
Open a new command window and set your Dynatrace URL as an environment variable.
Notice that the name of this variable matches the env-url variable you used in environments.yaml
set MAC_TRAINING_ENVIRONMENT=https://dtmanaged.dynatrace.training/e/***
Repeat the process by setting your API token. Again notice that the name corresponds to the value used in environments.yaml:
set TOKEN_MAC_TRAINING_ENVIRONMENT=***
In the same command window, run monaco:
./monaco --environments=environments.yaml --dry-run
You should see:
> ./monaco --environments=environments.yaml --dry-run
INFO Dynatrace Monitoring as Code v1.0.1
INFO Executing projects in this order:
INFO Processing environment mac_training_environment...
INFO Validation finished without errors
Monaco has the following concepts:
Environments are simply Dynatrace environments, SaaS or Managed. monaco can push configuration to one or more environments.
As we've already seen, environments are defined in environments.yaml.
Projects are distinct units of configuration. The most basic is to create a single project which holds your configuration. You can then push this identical configuration out to one (or multiple) environments.
Projects are created by creating a new folder inside the projects folder.
A single configuration item in Dynatrace. The monaco tool currently supports the following configuration types:
✅ This has already been configured in your demo environments.
The customer has already annotated their Deployment with the following labels which we can leverage automatically in Dynatrace.
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: prod-web
namespace: customer-a
labels:
app: web
stage: prod
This is simple, give the default service account permission to view metadata about the customer-a, customer-b and customer-c namespaces.
kubectl -n customer-a create rolebinding default-view --clusterrole=view --serviceaccount=customer-a:default
kubectl -n customer-b create rolebinding default-view --clusterrole=view --serviceaccount=customer-b:default
kubectl -n customer-c create rolebinding default-view --clusterrole=view --serviceaccount=customer-c:default

Recall that your client creates a new namespace for each of their customers.
👉 Create an auto-tag rule via the UI which matches this definition:

Use the Dynatrace API to retrieve the JSON representation of that tag.
Use the Automatically Applied Tags endpoint in the Configuration Api to GET all tag IDs:
{
"values": [{
"id": "afd026e7-2abc-492f-873e-66bb571a572a",
"name": "customer"
}]
}
Use the other GET endpoint by passing this ID to retrieve the full JSON.
GET /api/config/v1/autoTags/afd026e7-2abc-492f-873e-66bb571a572a
Save this JSON as a file.
👉 Inside projects, create a folder called baseconfig. This folder will hold all of our configuration.
Inside projects/baseconfig, create a new folder called auto-tag.
Inside each configuration type folder you need one YAML file and one (or more) JSON files.
Every configuration needs a YAML containing required and optional content.
A minimal viable config looks like this:
config:
- {config name} : "{path of config json template}"
{config name}:
- name: "{a unique name}"
The JSON file(s) inside this folder essentially hold the content that you'd normally POST to Dynatrace.
The only difference is that you can use variables inside the JSON which reference the YAML file.
Let's see that in action...
Create projects/baseconfig/auto-tag/tags.yaml:
config:
- customer_tag: "customertag.json"
customer_tag:
- name: "customer"
Create projects/baseconfig/auto-tag/customertag.json
Paste in the JSON content that you retrieved from the Dynatrace API.
There are a few sections we don't need in customertag.json.
Remove the metadata section and id variable. Your JSON should now look like this:
{
"name": "customer",
"rules": [
{
"type": "PROCESS_GROUP",
"enabled": true,
"valueFormat": "{ProcessGroup:KubernetesNamespace}",
"propagationTypes": [
"PROCESS_GROUP_TO_SERVICE"
],
"conditions": [
{
"key": {
"attribute": "PROCESS_GROUP_PREDEFINED_METADATA",
"dynamicKey": "KUBERNETES_NAMESPACE",
"type": "PROCESS_PREDEFINED_METADATA_KEY"
},
"comparisonInfo": {
"type": "STRING",
"operator": "EXISTS",
"value": null,
"negate": false,
"caseSensitive": null
}
}
]
}
]
}
Notice that we have certain hard coded values in the JSON. We can template these.
Templates in monaco are denoted with double curly braces.
Replace customer with {{ .name }}:
{
"name": "{{ .name }}",
"rules": [...]
...
}
When monaco executes, it will look at each configuration in the YAML and process them sequentially. We have one configuration:
- customer_tag: "customertag.json"
monaco then looks at the JSON and the properties for customer_tag. The properties are:
customer_tag:
- name: "customer"
monaco replaces anything inside curly braces with the corresponding value (denoted by .VariableName).
In this way, "{{ .name }}" becomes "customer".
Delete any previous tag rules you have in the tenant.
Apply your configuration:
./monaco --environments=environments.yaml
You should see that one configuration has been applied:
./monaco --environments=environments.yaml
INFO Dynatrace Monitoring as Code v1.0.1
INFO Executing projects in this order:
INFO 1: projects\baseconfig (1 configs)
INFO Processing environment mac_training_environment...
INFO Processing project projects\baseconfig...
INFO Deployment finished without errors

We need a way to quickly distinguish the processes (pods) for each customer. This configuration will prepend the namespace (aka customer name) to the process group.
Go to Settings > Processes and containers > Process group naming. Add a new rule:

Name format is: {ProcessGroup:KubernetesNamespace} - {ProcessGroup:DetectedName}
Hint: Dynatrace API > Configuration API > Conditional Naming
{
"values": [{
"id": "c6e41ebd-726a-4d79-9fd6-d3bcc37b836b",
"name": "Prepend Customer Name to PG"
}]
}
{
"metadata": {
"configurationVersions": [
0
],
"clusterVersion": "1.206.95.20201116-094826"
},
"id": "c6e41ebd-726a-4d79-9fd6-d3bcc37b836b",
"type": "PROCESS_GROUP",
"nameFormat": "{ProcessGroup:KubernetesNamespace} - {ProcessGroup:DetectedName}",
"displayName": "Prepend Customer Name to PG",
"enabled": true,
"rules": [
{
"key": {
"attribute": "PROCESS_GROUP_PREDEFINED_METADATA",
"dynamicKey": "KUBERNETES_NAMESPACE",
"type": "PROCESS_PREDEFINED_METADATA_KEY"
},
"comparisonInfo": {
"type": "STRING",
"operator": "EXISTS",
"value": null,
"negate": false,
"caseSensitive": null
}
}
]
}
Remove metadata and id. Use variables in JSON.
{
"type": "PROCESS_GROUP",
"nameFormat": "{ProcessGroup:KubernetesNamespace} - {ProcessGroup:DetectedName}",
"displayName": "{{ .name }}",
"enabled": true,
"rules": [{
"key": {
"attribute": "PROCESS_GROUP_PREDEFINED_METADATA",
"dynamicKey": "KUBERNETES_NAMESPACE",
"type": "PROCESS_PREDEFINED_METADATA_KEY"
},
"comparisonInfo": {
"type": "STRING",
"operator": "EXISTS",
"value": null,
"negate": false,
"caseSensitive": null
}
}]
}
projects/baseconfig/conditional-naming-processgrouprules.yamlprepend-customer-name.jsonconfig:
- prepend_customer_name: "prepend-customer-name.json"
prepend_customer_name:
- name: "Prepend Customer Name to PG"
prepend-customer-name.json should have this content:
{
"type": "PROCESS_GROUP",
"nameFormat": "{ProcessGroup:KubernetesNamespace} - {ProcessGroup:DetectedName}",
"displayName": "{{ .name }}",
"enabled": true,
"rules": [{
"key": {
"attribute": "PROCESS_GROUP_PREDEFINED_METADATA",
"dynamicKey": "KUBERNETES_NAMESPACE",
"type": "PROCESS_PREDEFINED_METADATA_KEY"
},
"comparisonInfo": {
"type": "STRING",
"operator": "EXISTS",
"value": null,
"negate": false,
"caseSensitive": null
}
}]
}
Delete the manually created process group naming rule from the Dynatrace UI.

Run monaco with the --dry-run flag to check for syntax errors:
./monaco --environments=environments.yaml --dry-run
When satisfied, run without the --dry-run flag to push your new config:
./monaco --environments=environments.yaml
Notice there are now two configurations applied:
./monaco --environments=environments.yaml
INFO Dynatrace Monitoring as Code v1.0.1
INFO Executing projects in this order:
INFO 1: projects\baseconfig (2 configs)
INFO Processing environment mac_training_environment...
INFO Processing project projects\baseconfig...
INFO Deployment finished without errors

For fun, run the monaco tool again as many times as you wish. Notice it is perfectly safe.
Build configuration to prepend the customer name to the service.
Hints:
conditional-naming-serviceWe have now built the following:
We have 6 websites:
Basic XHR detection and JQuery support should be enabled by default for all websites. Customer wants 20% of traffic to be captured in staging environments and 100% capture in production.
👉 Use the monaco tool to define applications for each of these websites.
application123 should be "123"⚠️ Do not define the application URL rules yet, we'll do that together next.
Time: 10 minutes
Your customer made a mistake! Their clients are paying for Dynatrace and some have complained that they're not seeing all their visits.
They should actually be capturing the following traffic amounts for each customer:
👉 Please correct the traffic capture levels for your customer using monaco.
Time: 5 minutes
Time to apply URL rules to the applications. So far we've dealt with configurations that stand alone. They do not reference, depend on or relate to any other configurations.
URL rules relate to the application for which they're assigned. So we have this:
Customer A - Staging | http://staging.customera |
Customer B - Staging | http://staging.customerb |
Customer C - Staging | http://staging.customerc |
Customer A - Production | http://customera |
Customer B - Production | http://customerb |
Customer C - Production | http://customerc |
projects/baseconfig/app-detection-ruleconfig:
- customer-a-staging: "rule.json"
- customer-a-production: "rule.json"
- customer-b-staging: "rule.json"
- customer-b-production: "rule.json"
- customer-c-staging: "rule.json"
- customer-c-production: "rule.json"
customer-a-staging:
- name: "/projects/baseconfig/application/customer-a-staging.name"
- application_id: "/projects/baseconfig/application/customer-a-staging.id"
- pattern: "http://staging.customera"
customer-b-staging:
- name: "/projects/baseconfig/application/customer-b-staging.name"
- application_id: "/projects/baseconfig/application/customer-b-staging.id"
- pattern: "http://staging.customerb"
customer-c-staging:
- name: "/projects/baseconfig/application/customer-c-staging.name"
- application_id: "/projects/baseconfig/application/customer-c-staging.id"
- pattern: "http://staging.customerc"
customer-a-production:
- name: "/projects/baseconfig/application/customer-a-prod.name"
- application_id: "/projects/baseconfig/application/customer-a-prod.id"
- pattern: "http://customera"
customer-b-production:
- name: "/projects/baseconfig/application/customer-b-prod.name"
- application_id: "/projects/baseconfig/application/customer-b-prod.id"
- pattern: "http://customerb"
customer-c-production:
- name: "/projects/baseconfig/application/customer-c-prod.name"
- application_id: "/projects/baseconfig/application/customer-c-prod.id"
- pattern: "http://customerc"
{
"applicationIdentifier": "{{ .application_id }}",
"filterConfig": {
"pattern": "{{ .pattern }}",
"applicationMatchType": "BEGINS_WITH",
"applicationMatchTarget": "URL"
}
}
./monaco --environments=environments.yaml

👉 We need to update the customer auto tag rule to include the application.
Hint: You only need to adjust the auto-tag/customertag.json file. You probably need to experiment in the UI first :)

Time: 15mins
You should now have code which creates the following components:
Create a management zone for each customer and environment which encompasses all processes, process groups, services and applications.
You should end up with 6 management zones:
Time: 20mins
So far we've only created configuration. What about deletion?
Create a file in the root folder (alongside ./monaco) called delete.yaml
delete.yaml has the following syntax:
delete:
- "ConfigurationType/ConfigurationName"
For example to delete an auto-tag which looks like this:
config:
- customer_tag: "customertag.json"
customer_tag:
- name: "customer"
Your delete.yaml would look like this:
delete:
- "auto-tag/customer"
Run ./monaco as normal and it will delete your config:
./monaco --environments=environments.yaml
INFO Dynatrace Monitoring as Code v1.0.1
INFO Executing projects in this order:
INFO 1: projects\baseconfig (15 configs)
INFO Processing environment mac_training_environment...
INFO Processing project projects\baseconfig...
INFO Deployment finished without errors
INFO Deleting 1 configs for environment mac_training_environment...
To prevent configuration deletion, just rename the delete.yaml to something else.
You now have a very stable base setup for your customer. Moreover your customer can take this configuration as code and apply it from their build pipelines.
You can share this configuration with customers and colleagues so they can easily understand what and how you have built things.
You have built this in a completely future-proof and flexible way that your customer can easily understand and amend to any new requirements.
You have successfully built the following:
If you'd like to extend this demo, there are a number of configurations you could still apply as code. Here are some ideas: