Manages a single Splunk On-Call (VictorOps) problem-notification integration (
dynatrace_victor_ops_notification) against thedynatrace-oss/dynatrace~> 1.98provider β a standalone primitive with one keystone resource and no owned children.
- π Renders exactly one resource β
dynatrace_victor_ops_notification.thisβ a Splunk On-Call (VictorOps) notification channel that pages an on-call group when a problem matching an associated Dynatrace alerting profile opens. - π Attaches to a pre-existing alerting profile by ID (
profile); does not create, own, or validate that profile β seeterraform-dynatrace-alerting-profile. - π Treats both
api_keyandrouting_keyas sensitive, caller-supplied secrets β never a literal default, never re-exported as an output. - π§± No nested
block_typesin the live schema β every attribute is flat, somain.tfis an unconditional, total 1:1 mapping fromvariables.tfwith nodynamicblocks required. - π« Ships disabled by default (
active = false) β a newly-applied integration cannot page anyone until the caller explicitly reviews and activates it.
π‘ Why it matters: a VictorOps/Splunk On-Call integration that silently lacks a working API key, a message body, or an explicit activation decision is worse than no integration at all β it looks configured in the Dynatrace UI while quietly never paging anyone (or, if misconfigured the other direction, paging immediately on
applybefore anyone has reviewed it). This module's type contract (requiredapi_key/message, secure-by-defaultactive) makes both failure modes visible atplantime instead of discoverable only during a real incident.
If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:
- β Star this repository to help others discover this Terraform module.
- π€ Connect with me on LinkedIn: linkedin.com/in/microsoftexpert
- β Buy me a coffee: buymeacoffee.com/microsoftexpert
Whether it's a star, a professional connection, or a coffee, every gesture helps keep these modules actively maintained and continually improving. Thank you for being part of the community!
flowchart LR
AP["terraform-dynatrace-alerting-profile"]:::sibling
TM["terraform-dynatrace-victorops-notification"]:::this
KR["dynatrace_victor_ops_notification"]:::keystone
VO["Splunk On-Call / VictorOps"]:::external
AP -- "id to profile" --> TM
TM -- "manages" --> KR
KR -- "pages via routing_key" --> VO
classDef this fill:#1496FF,color:#FFFFFF,stroke:#0B5FA5,stroke-width:1px;
classDef keystone fill:#0A2540,color:#FFFFFF,stroke:#08182b,stroke-width:1px;
classDef sibling fill:#E8EAED,color:#1A1A1A,stroke:#B0B4BA,stroke-width:1px;
classDef external fill:#F5F5F5,color:#1A1A1A,stroke:#B0B4BA,stroke-width:1px,stroke-dasharray: 3 3;
This module sits downstream of terraform-dynatrace-alerting-profile (which owns the alerting profile's
severity rules and event filters) and upstream of nothing in this catalog β no sibling module consumes
a VictorOps notification's id today. Validated via the Mermaid Chart MCP before embedding
(valid: true).
flowchart TB
subgraph Inputs["Module Inputs"]
name["name: string (required)"]
profile["profile: string (required)"]
api_key["api_key: string (required, sensitive)"]
routing_key["routing_key: string (required, sensitive)"]
message["message: string (required)"]
active["active: bool (optional, default false)"]
end
R["dynatrace_victor_ops_notification.this"]:::keystone
name -- "renders" --> R
profile -- "renders" --> R
api_key -- "renders, never logged" --> R
routing_key -- "renders, never logged" --> R
message -- "renders" --> R
active -- "renders" --> R
R -- "exposes" --> out_id["output: id"]
R -- "exposes" --> out_name["output: name"]
classDef keystone fill:#0A2540,color:#FFFFFF,stroke:#08182b,stroke-width:1px;
Validated via the Mermaid Chart MCP before embedding (valid: true).
Resource inventory:
| Resource | Count | Role |
|---|---|---|
dynatrace_victor_ops_notification.this |
1 | Keystone β the only resource this module manages |
| Item | Value |
|---|---|
| Terraform | >= 1.12.0 |
| Provider | dynatrace-oss/dynatrace, ~> 1.98 |
provider {} block |
None β the caller configures auth and dt_env_url outside this module |
Schema notes that bite (confirmed against the live provider schema, provider v1.100.0):
api_keyis the only attribute the provider itself flagssensitive = true. This module marks the corresponding variablesensitive = trueand additionally elevates it from provider-optional to module-required β the provider would letapplysucceed with no API key at all, producing an integration that looks configured but can never authenticate to Splunk On-Call.routing_keyis not flagged sensitive by the provider schema, but this module marks itsensitive = trueanyway β a deliberate house judgment call beyond the raw schema. A routing key is credential-adjacent (it directs alert traffic into a specific escalation group), so it gets the same "never print in plan/apply CLI output" treatment even though Dynatrace's own schema doesn't require it.- Neither
api_keynorrouting_keyis ever emitted as a module output βoutputs.tfexposes onlyidandname, by design, regardless of thesensitiveflag on either variable. messageis provider-required with no documented default (confirmed"required": truein the live schema) β this module requires it explicitly rather than inventing a plausible-sounding default for a free-text field.activeis provider-required with no server-side default β this module supplies a concrete module-level default offalse(nevernull) so the rendered argument is always populated. This is also the secure-by-default choice: a newly-applied VictorOps integration is created disabled and cannot page anyone until the caller explicitly setsactive = true.legacy_idis a computed, read-only attribute (the classic REST API v1 cross-reference ID) present in the live schema but not modeled as a module input or output β there is no documented consumer for it in this catalog.- No nested
block_typeson this resource β every attribute is flat, somain.tfneeds nodynamicblocks and notry(x, null)guards (every variable that reaches the resource block is already guaranteed non-null: five variables are required with no default, andactivecarries a concrete default).
dynatrace_victor_ops_notification is Settings 2.0-backed (schema ID builtin:problem.notifications)
and accepts either OAuth client credentials/platform token or a legacy API token. This is not an
IAM/Account Management or Platform/Automation resource, so no account-idm-* or automation:* scopes
are needed.
- OAuth (recommended) or platform token:
settings:objects:read,settings:objects:write. - API token (fallback): the resource's own documentation states explicitly β "This resource
requires the API token scopes Read settings (
settings.read) and Write settings (settings.write)" β confirmed directly against the live v1.100.0 schema doc. - This module does not touch IAM, Automation, or classic-Config-API-only resources.
- An OAuth client (or platform token, or legacy API token) provisioned with the scopes/permissions above, already bound via an IAM policy (OAuth/platform token path) or token permission grant (API token path).
- A pre-existing Dynatrace alerting profile β created by a
terraform-dynatrace-alerting-profileinstance in the same or a prior plan β whoseidoutput is passed into this module'sprofileinput. This module does not create or validate the alerting profile. - A Splunk On-Call (VictorOps) account with a REST endpoint integration configured for Dynatrace,
yielding the
api_keyandrouting_keyvalues (Settings -> Integrations -> Rest Endpoint -> Dynatrace, within Splunk On-Call). Source both from your own secrets manager β never as literal values in.tfor.tfvarscommitted to version control.
terraform-dynatrace-victorops-notification/
βββ providers.tf # required_version, required_providers pin β no provider {} block
βββ variables.tf # name, profile, api_key, routing_key, message, active β the full contract
βββ main.tf # dynatrace_victor_ops_notification.this β thin, total renderer
βββ outputs.tf # id, name (api_key/routing_key never output)
βββ SCOPE.md # lightweight standalone-module scope (this module's cross-module contract)
βββ README.md # this file
βββ examples/
βββ quickstart/
βββ main.tf # standalone, directly-runnable Quick Start snippet
module "victorops_notification" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-victorops-notification.git?ref=v1.0.0"
name = "victorops-primary-oncall"
profile = module.alerting_profile.id # terraform-dynatrace-alerting-profile's `id` output
api_key = var.victorops_api_key # sourced from your own secrets manager
routing_key = var.victorops_routing_key # sourced from your own secrets manager
message = "{ProblemTitle} - {ProblemImpact} - {ProblemSeverity} - {ProblemURL}"
# active intentionally omitted β defaults to false (secure-by-default)
}The caller configures the dynatrace provider (auth and dt_env_url) themselves, outside this module.
This module never declares a provider {} block or an auth-related variable.
Consumes
| Input | Type | Source module |
|---|---|---|
profile |
string, required |
terraform-dynatrace-alerting-profile (its id output) |
Emits
| Output | Description | Consumed by |
|---|---|---|
id |
The Dynatrace object ID of this VictorOps (Splunk On-Call) notification configuration | No in-catalog consumer today; exposed for reference/import |
name |
The display name of this VictorOps (Splunk On-Call) notification configuration | Documentation/reference only |
1 Β· Minimal inactive integration (secure-by-default)
module "victorops_notification" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-victorops-notification.git?ref=v1.0.0"
name = "victorops-staging-check"
profile = module.alerting_profile.id
api_key = var.victorops_api_key
routing_key = var.victorops_routing_key
message = "{ProblemTitle} - {ProblemURL}"
}π
activeis omitted, so it defaults tofalse. This integration renders in Dynatrace but pages no one until a caller explicitly setsactive = trueβ the secure-by-default posture this module follows for notification integrations.
2 Β· Activating a reviewed integration
module "victorops_notification" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-victorops-notification.git?ref=v1.0.0"
name = "victorops-primary-oncall"
profile = module.alerting_profile.id
api_key = var.victorops_api_key
routing_key = var.victorops_routing_key
message = "{ProblemTitle} - {ProblemImpact} - {ProblemSeverity} - {ProblemURL}"
active = true
}
β οΈ Setactive = trueonly after the routing key, alerting-profile scope, and message content have all been reviewed β this is the explicit opt-out the secure default requires.
3 Β· Custom message using the full placeholder set
module "victorops_notification" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-victorops-notification.git?ref=v1.0.0"
name = "victorops-detailed-message"
profile = module.alerting_profile.id
api_key = var.victorops_api_key
routing_key = var.victorops_routing_key
message = <<-EOT
[{State}] {ProblemTitle}
Impact: {ProblemImpact} | Severity: {ProblemSeverity}
Entities: {NamesOfImpactedEntities}
Details: {ProblemDetailsText}
Link: {ProblemURL}
EOT
active = true
}βΉοΈ Placeholders are resolved server-side by Dynatrace at notification-send time; this module renders the literal
messagestring unmodified β it does not interpret or validate placeholder syntax.
4 Β· Referencing a pre-existing alerting profile by literal ID
module "victorops_notification" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-victorops-notification.git?ref=v1.0.0"
name = "victorops-legacy-profile-link"
profile = "b6a8a1b2-9c3d-4e2f-8a1b-1234567890ab" # pre-existing profile, not managed by Terraform
api_key = var.victorops_api_key
routing_key = var.victorops_routing_key
message = "{ProblemTitle} - {ProblemURL}"
}
β οΈ Prefer wiringprofile = module.alerting_profile.idwherever the profile is also managed by this Terraform library β a literal ID here means Terraform cannot order the profile's creation before this notification and cannot detect if the profile is later destroyed out-of-band.
5 Β· Sourcing secrets from Terraform Cloud/Enterprise workspace variables
variable "victorops_api_key" {
type = string
sensitive = true
}
variable "victorops_routing_key" {
type = string
sensitive = true
}
module "victorops_notification" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-victorops-notification.git?ref=v1.0.0"
name = "victorops-tfc-sourced"
profile = module.alerting_profile.id
api_key = var.victorops_api_key
routing_key = var.victorops_routing_key
message = "{ProblemTitle} - {ProblemURL}"
}π Populate
victorops_api_key/victorops_routing_keyas sensitive workspace variables in HCP Terraform/Enterprise β never as plain.tfvarscommitted to version control.
6 Β· Sourcing secrets from an external secrets-manager data source
data "azurerm_key_vault_secret" "victorops_api_key" {
name = "victorops-dynatrace-api-key"
key_vault_id = data.azurerm_key_vault.this.id
}
data "azurerm_key_vault_secret" "victorops_routing_key" {
name = "victorops-dynatrace-routing-key"
key_vault_id = data.azurerm_key_vault.this.id
}
module "victorops_notification" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-victorops-notification.git?ref=v1.0.0"
name = "victorops-keyvault-sourced"
profile = module.alerting_profile.id
api_key = data.azurerm_key_vault_secret.victorops_api_key.value
routing_key = data.azurerm_key_vault_secret.victorops_routing_key.value
message = "{ProblemTitle} - {ProblemURL}"
}π This is the standard pattern this module follows for credential-bearing inputs: secret material is provisioned out of band, from the caller's own secrets manager, never as a module default.
7 Β· Minimal message referencing only title and URL
module "victorops_notification" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-victorops-notification.git?ref=v1.0.0"
name = "victorops-terse-message"
profile = module.alerting_profile.id
api_key = var.victorops_api_key
routing_key = var.victorops_routing_key
message = "{ProblemTitle} β {ProblemURL}"
active = true
}π‘
messageis required with no invented default (see Β§ Schema notes that bite) β even a minimal, two-placeholder message must be supplied explicitly.
8 Β· Multiple routing destinations via caller-layer for_each
locals {
victorops_teams = {
platform = {
routing_key = var.victorops_routing_key_platform
name = "victorops-platform-team"
}
database = {
routing_key = var.victorops_routing_key_database
name = "victorops-database-team"
}
}
}
module "victorops_notification" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-victorops-notification.git?ref=v1.0.0"
for_each = local.victorops_teams
name = each.value.name
profile = module.alerting_profile.id
api_key = var.victorops_api_key
routing_key = each.value.routing_key
message = "{ProblemTitle} - {ProblemImpact} - {ProblemURL}"
active = true
}βΉοΈ This module itself has no owned
for_eachchildren (it is a single-resource standalone primitive) β breadth at scale is achieved by the caller wrapping the module call infor_each, keyed by a caller-chosen map key, never a list index.
9 Β· Environment-conditional activation (dev vs. prod)
module "victorops_notification" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-victorops-notification.git?ref=v1.0.0"
name = "victorops-${var.environment}"
profile = module.alerting_profile.id
api_key = var.victorops_api_key
routing_key = var.victorops_routing_key
message = "[${upper(var.environment)}] {ProblemTitle} - {ProblemURL}"
active = var.environment == "prod"
}
β οΈ Makingactivea computed expression is a legitimate pattern, but it moves the secure-by-default decision into caller-controlled logic β verifyvar.environment == "prod"evaluates as expected in every workspace before relying on it in production.
10 Β· Pairing with a narrowly-scoped (management-zone) alerting profile
module "management_zone" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-management-zone.git?ref=v1.0.0"
name = "payments-processing"
}
module "alerting_profile" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-alerting-profile.git?ref=v1.0.0"
name = "payments-processing-availability"
management_zone_id = module.management_zone.id
rules = {
availability-payments = {
severity_level = "AVAILABILITY"
include_mode = "NONE"
delay_in_minutes = 5
}
}
}
module "victorops_notification" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-victorops-notification.git?ref=v1.0.0"
name = "victorops-payments-processing"
profile = module.alerting_profile.id
api_key = var.victorops_api_key
routing_key = var.victorops_routing_key_payments
message = "{ProblemTitle} - {ImpactedEntityNames} - {ProblemURL}"
active = true
}π‘ Scoping the upstream alerting profile to a management zone (rather than leaving it environment-wide) is this module's narrowest-matching-scope default for alerting profiles β this VictorOps integration only ever pages for problems within that zone.
11 Β· Per-severity routing via multiple module instances
module "victorops_notification_critical" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-victorops-notification.git?ref=v1.0.0"
name = "victorops-critical-escalation"
profile = module.alerting_profile_critical.id
api_key = var.victorops_api_key
routing_key = var.victorops_routing_key_critical
message = "CRITICAL: {ProblemTitle} - {ProblemURL}"
active = true
}
module "victorops_notification_warning" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-victorops-notification.git?ref=v1.0.0"
name = "victorops-warning-escalation"
profile = module.alerting_profile_warning.id
api_key = var.victorops_api_key
routing_key = var.victorops_routing_key_warning
message = "WARNING: {ProblemTitle} - {ProblemURL}"
active = true
}βΉοΈ Each severity tier gets its own alerting profile (with its own
rules) and its own VictorOps module instance with a distinctrouting_keyβ this module has no built-in severity branching, so distinct routing per severity is achieved by composing multiple profile/notification pairs.
12 Β· Importing an existing VictorOps integration
module "victorops_notification" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-victorops-notification.git?ref=v1.0.0"
name = "victorops-preexisting"
profile = module.alerting_profile.id
api_key = var.victorops_api_key
routing_key = var.victorops_routing_key
message = "{ProblemTitle} - {ProblemURL}"
active = true
}terraform import 'module.victorops_notification.dynatrace_victor_ops_notification.this' <existing-notification-settings-object-id>
β οΈ After import, runterraform planand confirm no unexpected diff before the nextapplyβapi_keycannot be read back from the Dynatrace API (it is provider-marked sensitive/write-only in practice), so the imported state'sapi_keywill not reflect the real value until the caller supplies it explicitly via this module's variable.
13 Β· Consuming this module's id output for documentation/inventory
output "victorops_integration_ids" {
description = "Map of VictorOps notification names to their Dynatrace object IDs, for change-management documentation."
value = {
for k, m in module.victorops_notification : k => m.id
}
}βΉοΈ No sibling module in this catalog consumes a VictorOps notification's
idtoday β this output is exposed for reference,terraform import, and inventory/documentation tooling.
14 Β· ποΈ End-to-end composition β alerting profile through VictorOps notification
module "alerting_profile" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-alerting-profile.git?ref=v1.0.0"
name = "production-availability-critical"
rules = {
availability-all-entities = {
severity_level = "AVAILABILITY"
include_mode = "NONE"
delay_in_minutes = 5
}
errors-tagged-services = {
severity_level = "ERRORS"
include_mode = "INCLUDE_ANY"
delay_in_minutes = 10
tags = ["env:production", "tier:critical"]
}
}
}
module "victorops_notification" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-victorops-notification.git?ref=v1.0.0"
name = "victorops-production-critical"
profile = module.alerting_profile.id # wires the sibling module's `id` output into `profile`
api_key = var.victorops_api_key
routing_key = var.victorops_routing_key
message = "{ProblemTitle} - {ProblemImpact} - {ProblemSeverity} - {ProblemURL}"
active = true
}
output "alerting_profile_id" {
value = module.alerting_profile.id
description = "The alerting profile created and consumed by the VictorOps notification below."
}
output "victorops_notification_id" {
value = module.victorops_notification.id
description = "The VictorOps notification configuration wired to the alerting profile above."
}π‘ This is the mandatory end-to-end wiring:
terraform-dynatrace-alerting-profile'sidoutput flows directly into this module'sprofileinput, giving Terraform an implicit reference that orders the profile's creation before the notification β nodepends_onrequired.
| Variable | Type | Required | Default | Notes |
|---|---|---|---|---|
name |
string |
Yes | β | Display name in the Dynatrace UI notification list |
profile |
string |
Yes | β | Alerting-profile ID; pass terraform-dynatrace-alerting-profile's id output |
api_key |
string |
Yes | β | sensitive = true; source from your own secrets manager |
routing_key |
string |
Yes | β | sensitive = true (house judgment call, not provider-mandated) |
message |
string |
Yes | β | No non-arbitrary default exists for a free-text field |
active |
bool |
No | false |
Secure-by-default β set true explicitly to activate |
Full variable schemas
variable "name" {
type = string
}
variable "profile" {
type = string
}
variable "api_key" {
type = string
sensitive = true
}
variable "routing_key" {
type = string
sensitive = true
}
variable "message" {
type = string
}
variable "active" {
type = bool
default = false
}All six variables are flat scalars β there are no nested object schemas in this module, since the
underlying dynatrace_victor_ops_notification resource has no nested block_types. See each
variable's full description heredoc in variables.tf for the complete schema provenance, including
the documented deviations from the raw provider schema (api_key and message elevated to required;
routing_key elevated to sensitive).
| Output | Description | Sensitive? |
|---|---|---|
id |
The Dynatrace object ID of this VictorOps (Splunk On-Call) notification configuration | No |
name |
The display name of this VictorOps (Splunk On-Call) notification configuration | No |
api_key and routing_key are never output, regardless of their sensitive = true status at the
variable layer β this module omits them from outputs.tf entirely rather than outputting them marked
sensitive.
- Total, unconditional renderer.
main.tfmaps all six variables directly ontodynatrace_victor_ops_notification.thiswith notry(x, null)guards anywhere. This is possible (and correct, following this module's "thin, total renderer" convention) because five of the six variables have no default β Terraform refuses to plan without a caller-supplied value β and the sixth (active) always carries a concrete, non-null default offalse. Nothing reaching the resource block can ever benull. - Two schema deviations, both documented in
variables.tf, both intentional: api_keyis provider-optional but this module makes it module-required. Anapplywith no API key would succeed while producing an integration that can never authenticate to Splunk On-Call β an inert, silently-broken channel. Requiring it surfaces that dependency atplantime.messageis provider-required with no documented default; the module simply passes that requirement through rather than inventing a plausible-sounding default string for free-text content.- One sensitivity deviation, also intentional:
routing_keyis not provider-flagged sensitive, but this module marks itsensitive = trueanyway, since it is credential-adjacent (it directs alert traffic to a specific escalation group) even though it cannot itself authenticate to an API the wayapi_keycan. - Implicit reference, not
depends_on.profileis wired viamodule.alerting_profile.idin every example that composes with the sibling module β this gives Terraform a real reference that orders resource creation, consistent with this module suite's convention to respect record ordering via implicit references. No example in this README usesdepends_onas a substitute. - No environment-vs-account-scope split to document. Unlike IAM/Account Management resources, this resource is entirely environment-scoped (Settings 2.0 object against a single Dynatrace environment) β there is no dual-provider-config concern here.
legacy_idis intentionally invisible. It exists on the live schema as a computed, read-only classic-API cross-reference, but this module does not expose it as an output β no consumer in this catalog needs the REST API v1 equivalent ID.
| Concern | Safe default | Opt-out (caller must set explicitly) |
|---|---|---|
| Notification activation | active defaults to false β a newly-applied integration cannot page anyone |
Explicit active = true, only after reviewing routing key, alerting-profile scope, and message content |
api_key handling |
No literal default permitted; sensitive = true; never re-exported as an output |
N/A β non-negotiable, not a default to opt out of |
routing_key handling |
Treated as sensitive = true even though the provider schema doesn't require it; never re-exported as an output |
N/A β non-negotiable house judgment call |
message content |
No invented default β caller must supply real message text, surfacing the dependency at plan time rather than shipping a generic placeholder string |
N/A β required field, no opt-out |
| Alerting-profile scope | This module does not choose or narrow scope itself β it inherits whatever scope the referenced terraform-dynatrace-alerting-profile instance defines |
Caller controls scope entirely via the upstream profile module |
terraform init -backend=false
terraform validate
terraform fmt -checkPin the module source at ?ref=v1.0.0 β never an unpinned branch. This library is plan-only: no
terraform apply is performed as part of authoring or CI validation; a human applies from a controlled
pipeline after reviewing the plan.
- Covered by
init -backend=false/validate/fmt -check: type-contract correctness (every required variable enforced at parse time,sensitiveflags present, no malformed HCL), provider version constraint satisfiability, and formatting conventions. This is the offline proof gate β it runs with no Dynatrace credentials and touches no live environment. - Only exercised by a real
terraform planagainst a configured environment: whether the suppliedprofileID actually resolves to an existing alerting profile, whether the suppliedapi_key/routing_keyare accepted by Splunk On-Call at the API level, and whether the caller's OAuth scopes/API token permissions are sufficient. None of this is verifiable offline β this module's static analysis proves the shape of the request, not that Dynatrace or Splunk On-Call will accept it.
$ terraform output
id = "vu9U3hXd3q0AAAABABlidWlsdGluOnByb2JsZW0ubm90aWZpY2F0aW9ucwAGdGVuYW50AAZ0ZW5hbnQAJGNhOTk4YTA3LTVh"
name = "victorops-production-critical"
api_key and routing_key never appear in terraform output β they are omitted from outputs.tf
entirely.
| Symptom | Cause | Fix |
|---|---|---|
apply fails with an authentication/authorization error, but the OAuth client or API token has the documented settings:objects:* / settings.* scopes |
The provider is configured for one auth mechanism (e.g. API token) while DYNATRACE_HTTP_OAUTH_PREFERENCE or the credential environment variables point at a different one |
Confirm which of DT_CLIENT_ID/DT_CLIENT_SECRET, DYNATRACE_PLATFORM_TOKEN, or DYNATRACE_API_TOKEN is actually set in the calling environment, and that DYNATRACE_HTTP_OAUTH_PREFERENCE matches the intended mechanism β this is a provider-config concern, not something this module can detect |
VictorOps never receives a page even though active = true and apply succeeded |
routing_key does not match a configured routing rule in Splunk On-Call, or the profile's rules/filters never actually match the triggering problem |
Verify the routing key against Splunk On-Call's own configuration, and confirm the upstream terraform-dynatrace-alerting-profile instance has at least one non-inert rule scoped to match the entities in question |
plan shows profile changing on every run |
profile is wired to a literal string ID rather than module.alerting_profile.id, and the literal ID doesn't exactly match the live object's ID format |
Reference the sibling module's id output directly instead of a hardcoded literal (see Example 4's caveat) |
terraform import succeeds but the next plan shows a diff on api_key |
api_key cannot be read back from the Dynatrace API after import; imported state does not know the real value until the caller supplies it |
Supply the real api_key value via the module variable (sourced from your secrets manager) before the first post-import apply |
A CI pipeline's validate step fails on this module specifically, while siblings pass |
message or api_key left unset β both are module-required with no default, unlike some sibling notification modules where the equivalent field may be optional |
Confirm the calling code supplies both explicitly; check this module's variables.tf rather than assuming parity with a sibling notification module's schema |
- Provider resource reference:
dynatrace_victor_ops_notificationβ Dynatrace Terraform provider docs (dynatrace-oss/dynatrace, subcategory "Notifications"). - Sibling module:
terraform-dynatrace-alerting-profile(profileinput's source module). - This module's
SCOPE.md(cross-module contract, permissions, prerequisites).
π "Infrastructure as Code should be standardized, consistent, and secure."