Manages a single
dynatrace_xmatters_notificationresource — an xMatters webhook notification channel wired to a Dynatrace alerting profile. Targetsdynatrace-oss/dynatrace ~> 1.98(confirmed against the live v1.100.0 schema).
- 📨 Manages one
dynatrace_xmatters_notificationSettings 2.0 object — an xMatters webhook integration that Dynatrace calls when a problem matches an alerting profile's rules. - 🧱 Renders the resource's only nested structure,
headers, from a caller-keyedmap(object({...}))of HTTP headers entirely insidemain.tf— there is no separate Terraform resource for headers; they live inside the one keystone resource block. - 🔒 Ships two secure-by-default toggles baked into the variable schema: the notification is
created disabled (
active = false) and TLS certificate validation stays enforced (insecure = false) until a caller deliberately opts out of either. - 🔌 Has no owned sibling modules; consumes exactly one cross-module input — the alerting
profile's
id, viaprofile.
💡 Why it matters: An xMatters channel that auto-activates on creation, or that silently disables TLS validation, can either page the wrong on-call rotation the moment
applyruns, or quietly weaken the transport security of every problem notification sent to xMatters. Both behaviors default off in this module so a human reviews the configuration — URL, headers, payload, and target alerting profile — before either risk goes live.
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
THIS["terraform-dynatrace-xmatters-notification"]:::thismodule
RES["dynatrace_xmatters_notification"]:::keystone
AP -->|"emits id, consumed as profile"| THIS
THIS -->|"renders one keystone resource"| RES
classDef thismodule fill:#1496FF,color:#FFFFFF,stroke:#0A2540,stroke-width:1px;
classDef keystone fill:#0A2540,color:#FFFFFF,stroke:#0A2540,stroke-width:1px;
classDef sibling fill:#E8E8E8,color:#333333,stroke:#B0B0B0,stroke-width:1px;
Validated via the Mermaid Chart MCP (valid: true) before embedding. This module has no owned
sibling modules of its own; terraform-dynatrace-alerting-profile is the one upstream module every
notification-channel module in this catalog (Slack, webhook, email, Jira, OpsGenie, PagerDuty,
ServiceNow, Trello, VictorOps, xMatters, Ansible Tower) attaches to via profile.
flowchart TB
NAME["var.name - required string"]
PROFILE["var.profile - required string, alerting-profile id"]
URL["var.url - required string"]
PAYLOAD["var.payload - required string"]
ACTIVE["var.active - optional bool, default false"]
INSECURE["var.insecure - optional bool, default false"]
LEGACY["var.legacy_id - optional string, default null"]
HEADERS["var.headers - map of object, default empty map"]
RES["dynatrace_xmatters_notification.this"]:::keystone
HBLOCK["headers block - max_items 1"]:::nested
HDR["dynamic header block - one per map entry, name = map key"]:::nested
NAME --> RES
PROFILE --> RES
URL --> RES
PAYLOAD --> RES
ACTIVE --> RES
INSECURE --> RES
LEGACY --> RES
HEADERS -->|"rendered only when length greater than 0"| HBLOCK
HBLOCK --> HDR
HDR -->|"secret_value wrapped in sensitive at point of use"| RES
RES -->|"id"| OUT_ID["output: id"]
RES -->|"name"| OUT_NAME["output: name"]
classDef keystone fill:#0A2540,color:#FFFFFF,stroke:#0A2540,stroke-width:1px;
classDef nested fill:#1496FF,color:#FFFFFF,stroke:#0A2540,stroke-width:1px;
Validated via the Mermaid Chart MCP (valid: true) before embedding.
Resource inventory:
| Item | Count | Notes |
|---|---|---|
| Keystone resource | 1 | dynatrace_xmatters_notification.this |
| Owned child resources | 0 | Standalone primitive — no for_each-driven sibling resources |
| Nested block types | 1 | headers (max_items = 1) wrapping a repeatable header set block (min_items = 1 once present) |
| Data sources | 0 | None |
ℹ️
headersis not a separate child resource wired with a resource-levelfor_each— it is a nested block inside the one keystone resource, populated via adynamic "headers"block whose innerdynamic "header"block iteratesvar.headers. The map-keyed shape gives callers stable, name-addressable configuration and enforces unique header names at plan/validate time; Terraform's own set-block diffing (see Architecture Notes) already handles the underlyingheaderset block's ordering independently of this.
| Requirement | Value |
|---|---|
| Terraform | >= 1.12.0 |
dynatrace-oss/dynatrace |
~> 1.98 (confirmed against the live v1.100.0 schema) |
provider {} block |
None — the caller configures the dynatrace provider (auth + dt_env_url) once, outside this module |
Schema notes that bite:
- No top-level attribute on
dynatrace_xmatters_notificationis provider-flagged sensitive —name,profile,url,payload,active,insecure, andlegacy_idare all plain. Only the nestedheader.secret_valueattribute, inside theheadersblock, issensitive = truein the live schema. secret_valueis never emitted as a module output.outputs.tfomits the entireheaderscollection (and every individual header attribute) — not just the secret one — so there is no path for any header material, secret or otherwise, to leave this module via an output.insecure(TLS certificate validation bypass for the outbound call Dynatrace makes to your xMatters endpoint) defaults tofalsein this module's variable, even though the live schema documents no provider-side default at all for this field — the module bakes in the safe value itself rather than deferring to undocumented API behavior.activeisrequired: truein the live provider schema, with no server-side default — everyapplymust send an explicittrue/false. This module absorbs that requirement by defaulting the caller-facing variable tofalse, somain.tfalways renders a concrete boolean: a newly-applied integration is inert (disabled) until a caller explicitly setsactive = true.payloadis alsorequired: truein the live schema with no documented default — modeled here as a required variable; there is no safe "empty payload" fallback to default to.headersis a single-instance wrapper block (nesting_mode = "list",max_items = 1) around a repeatableheaderset block (min_items = 1once the wrapper is present at all).main.tfrenders theheaders {}wrapper only when the caller's map is non-empty, so omittingheaders(or leaving it at its default{}) never produces an invalid empty wrapper block.legacy_idis optional and computed — modeled as a nullable, caller-settable variable rather than omitted or made required, per house convention for computed-but-settable fields. Leave itnullunless migrating a pre-Settings-2.0 reference that another REST API v1-based resource already keys against.- No force-new/immutable field is asserted for this resource in this README: the live
terraform providers schema -jsonschema dump used to author this module does not expose SDK-levelForceNewflags (that dump reports type/required/optional/computed/sensitive only). Treat any argument change as a potential in-place update per the general Settings 2.0 convention unless you observe replace-then-create behavior in a realterraform plan— do not assume this README's silence on force-new fields means none exist.
- API token (confirmed against the resource's own live documentation):
settings.read("Read settings"),settings.write("Write settings"). This corrects an earlier draft that used the classic Config API v1 permission names (ReadConfig/WriteConfig), which do not apply to this Settings-2.0-backed resource. - OAuth (the recommended path, per general Settings-2.0 guidance):
settings:objects:read,settings:objects:write— not independently confirmed on this resource's own documentation page, which states only the API-token scopes above. Verify against your own environment/IAM policy before a production OAuth grant. - This module does not touch IAM or Automation resources — no
account-idm-*orautomation:*scopes are needed for this module specifically.
ℹ️ Confidence note: the resource's own provider-registry documentation page was not directly queried to confirm an explicit "accepted authentication mechanisms" statement during this authoring pass — the scopes above are carried over from the confirmed dual-auth pattern of this resource's Settings-2.0 siblings in the same schema dump, not from a resource-specific authentication note. Verify against your environment (or the live provider docs) before treating this as final for a production change that depends on it.
- An OAuth client (or legacy API token) provisioned with the scopes/permissions above.
- A Dynatrace alerting profile must already exist (or be created in the same plan) — its
idis a required input to this module viaprofile. - An xMatters integration/webhook endpoint already provisioned on the xMatters side, with its
inbound webhook URL available to pass as
url. - If the xMatters endpoint requires custom HTTP headers (e.g. an API key header), have those values available from your own secrets manager before authoring the calling configuration — this module never accepts literal secret defaults.
| File | Role |
|---|---|
providers.tf |
required_version + required_providers pin — no provider {} block |
variables.tf |
name, profile, url, payload, active, insecure, legacy_id, headers |
main.tf |
Thin, total renderer — one keystone resource this, with dynamic "headers" / dynamic "header" |
outputs.tf |
id, name — no header attribute of any kind is ever output |
README.md |
This file |
SCOPE.md |
Lightweight standalone-module scope: design intent, Consumes/Emits, required scopes, prerequisites |
examples/quickstart/main.tf |
Minimal, directly-runnable example (validated with terraform init -backend=false / validate / fmt -check) |
module "xmatters_notification" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-xmatters-notification.git?ref=v1.0.0"
name = "xmatters-platform-team"
profile = module.alerting_profile.id # terraform-dynatrace-alerting-profile output
url = "https://example.xmattersapi.com/api/xm/1/flow/platform-team-integration"
payload = "{ProblemTitle} - {ProblemSeverity} - {ProblemURL}"
}ℹ️ You configure the
dynatraceprovider (OAuth client credentials, platform token, or legacy API token, plusdt_env_url) once, outside this module — this module never declares aprovider {}block or an auth-related variable. Seeexamples/quickstart/main.tffor a complete, directly-runnable snippet including theterraform/required_providersblock.
Consumes
| Input | Type | Source module |
|---|---|---|
profile |
string, required |
terraform-dynatrace-alerting-profile (its id output) — referenced by ID only; this module does not manage or validate the alerting profile itself |
Emits
| Output | Description | Consumed by |
|---|---|---|
id |
The Dynatrace object ID of this xMatters notification configuration. | No current consumer — exposed for reference/import and for any future aggregation module that might relate notification channels to other objects |
name |
The display name of this xMatters notification configuration. | Reference/documentation only |
1 · Minimal inert integration (secure-by-default)
module "xmatters_minimal" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-xmatters-notification.git?ref=v1.0.0"
name = "xmatters-platform-team"
profile = module.alerting_profile.id
url = "https://example.xmattersapi.com/api/xm/1/flow/platform-team-integration"
payload = "{ProblemTitle} - {ProblemSeverity}"
}💡
activeandinsecureare both omitted — they default tofalse. The integration is created disabled, and TLS certificate validation stays enforced. Nothing pages anyone until a caller reviews the configuration and explicitly setsactive = true.
2 · Activating a reviewed integration
module "xmatters_active" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-xmatters-notification.git?ref=v1.0.0"
name = "xmatters-platform-team"
profile = module.alerting_profile.id
url = "https://example.xmattersapi.com/api/xm/1/flow/platform-team-integration"
payload = "{ProblemTitle} - {ProblemSeverity}"
active = true
}
⚠️ Only setactive = trueafter the URL, payload, and target alerting profile have been reviewed — from this point on, matching problems will page through xMatters.
3 · Rich JSON payload for downstream automation
module "xmatters_json_payload" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-xmatters-notification.git?ref=v1.0.0"
name = "xmatters-json-integration"
profile = module.alerting_profile.id
url = "https://example.xmattersapi.com/api/xm/1/flow/json-integration"
payload = "{ProblemDetailsJSONv2}"
}ℹ️
{ProblemDetailsJSONv2}follows the Dynatrace Problems V2 API structure — use this when the receiving xMatters flow parses the payload as structured JSON rather than free text.
4 · Markdown-formatted payload
module "xmatters_markdown_payload" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-xmatters-notification.git?ref=v1.0.0"
name = "xmatters-markdown-integration"
profile = module.alerting_profile.id
url = "https://example.xmattersapi.com/api/xm/1/flow/markdown-integration"
payload = "{ProblemDetailsMarkdown}"
}5 · HTML-formatted payload
module "xmatters_html_payload" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-xmatters-notification.git?ref=v1.0.0"
name = "xmatters-html-integration"
profile = module.alerting_profile.id
url = "https://example.xmattersapi.com/api/xm/1/flow/html-integration"
payload = "{ProblemDetailsHTML}"
}ℹ️ Use
{ProblemDetailsHTML}only when the receiving xMatters flow is configured to render HTML — otherwise the recipient sees raw markup.
6 · Tag-scoped payload referencing entity tags
module "xmatters_tag_scoped" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-xmatters-notification.git?ref=v1.0.0"
name = "xmatters-tag-scoped-integration"
profile = module.alerting_profile.id
url = "https://example.xmattersapi.com/api/xm/1/flow/tag-scoped-integration"
payload = "{ProblemTitle} - environment={Tags[environment]} - team={Tags[owning-team]}"
}ℹ️
{Tags[key]}resolves to an empty string if the tag key exists with no value, and is left unreplaced if the tag key does not exist at all on any impacted entity.
7 · Custom HTTP headers — plain values only
module "xmatters_plain_headers" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-xmatters-notification.git?ref=v1.0.0"
name = "xmatters-with-headers"
profile = module.alerting_profile.id
url = "https://example.xmattersapi.com/api/xm/1/flow/headers-integration"
payload = "{ProblemTitle} - {ProblemSeverity}"
headers = {
"Content-Type" = {
value = "application/json"
}
}
}8 · Custom HTTP headers including a secret value
variable "xmatters_api_key" {
type = string
sensitive = true
}
module "xmatters_secret_header" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-xmatters-notification.git?ref=v1.0.0"
name = "xmatters-authenticated-integration"
profile = module.alerting_profile.id
url = "https://example.xmattersapi.com/api/xm/1/flow/authenticated-integration"
payload = "{ProblemTitle} - {ProblemSeverity}"
headers = {
"Content-Type" = {
value = "application/json"
}
"Authorization" = {
secret_value = var.xmatters_api_key
}
}
}🔒
var.xmatters_api_keymust come from your own secrets manager (e.g. Azure Key Vault) at plan time — never hardcode a literal secret in checked-in.tf/.tfvars. Internally,main.tfwrapsheader.value.secret_valuein the built-insensitivefunction at the exact point it is assigned to the resource'sheader.secret_valueargument (see Architecture Notes for the full mechanics). This module never outputs this value, or any other header attribute, under any name.
9 · Multiple headers combined (plain, secret, and empty-value)
module "xmatters_multi_header" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-xmatters-notification.git?ref=v1.0.0"
name = "xmatters-multi-header-integration"
profile = module.alerting_profile.id
url = "https://example.xmattersapi.com/api/xm/1/flow/multi-header-integration"
payload = "{ProblemTitle} - {ProblemSeverity}"
headers = {
"Content-Type" = { value = "application/json" }
"X-Requested-For" = { value = "" }
"Authorization" = { secret_value = var.xmatters_api_key }
}
}💡 The map key is each header's name — adding or removing one entry never perturbs the others, and duplicate header names are rejected at
plan/validatetime because map keys must be unique.
10 · TLS certificate validation bypass (explicit opt-out)
module "xmatters_insecure" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-xmatters-notification.git?ref=v1.0.0"
name = "xmatters-legacy-selfsigned-endpoint"
profile = module.alerting_profile.id
url = "https://legacy-xmatters-gateway.internal.example.com/api/xm/1/flow/legacy-integration"
payload = "{ProblemTitle} - {ProblemSeverity}"
insecure = true
}
⚠️ insecure = truedisables TLS certificate validation for every outbound call Dynatrace makes to this xMatters endpoint, removing protection against man-in-the-middle tampering. Only set this for a documented, reviewed exception (e.g. a known internal self-signed certificate chain) — never as a default workaround for a certificate error. Fix the certificate instead where possible.
11 · Migrating a legacy REST API v1 reference
module "xmatters_legacy_migration" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-xmatters-notification.git?ref=v1.0.0"
name = "xmatters-migrated-integration"
profile = module.alerting_profile.id
url = "https://example.xmattersapi.com/api/xm/1/flow/migrated-integration"
payload = "{ProblemTitle} - {ProblemSeverity}"
legacy_id = "1234567890123456"
}ℹ️ Leave
legacy_idunset (null, the default) unless another REST API v1-based resource in your environment already references this notification by its legacy key — Dynatrace computes and manages this value server-side otherwise.
12 · Caller-side fan-out across multiple teams
variable "xmatters_integrations" {
type = map(object({
url = string
profile = string
}))
}
module "xmatters_per_team" {
for_each = var.xmatters_integrations
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-xmatters-notification.git?ref=v1.0.0"
name = "xmatters-${each.key}"
profile = each.value.profile
url = each.value.url
payload = "{ProblemTitle} - {ProblemSeverity} - team=${each.key}"
}💡 This module is a standalone primitive with no owned
for_eachcollection of its own — a caller wraps the module call itself infor_eachto fan out one xMatters integration per team, environment, or on-call rotation, each still defaulting toactive = falseuntil reviewed.
13 · Dormant, fully-staged configuration awaiting change-control sign-off
module "xmatters_staged_for_review" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-xmatters-notification.git?ref=v1.0.0"
name = "xmatters-new-oncall-integration"
profile = module.alerting_profile.id
url = "https://example.xmattersapi.com/api/xm/1/flow/new-oncall-integration"
payload = "{ProblemDetailsJSONv2}"
headers = {
"Content-Type" = { value = "application/json" }
"Authorization" = { secret_value = var.xmatters_api_key }
}
# Left false deliberately: staged for a change-control reviewer to flip once
# the URL, payload, and headers above have been signed off.
active = false
}ℹ️ This is the same secure-by-default posture as Example 1, shown here with a fuller configuration to illustrate a common regulated-environment workflow: apply the complete, reviewed configuration first with
active = false, then flipactive = truein a follow-up, separately-reviewed change once change control has signed off.
14 · 🏗️ End-to-end composition
Wires a terraform-dynatrace-alerting-profile instance's id output into this module's profile
input — the complete cross-module pattern every example above assumes.
module "alerting_profile" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-alerting-profile.git?ref=v1.0.0"
name = "platform-team-availability-and-errors"
rules = {
availability-all-entities = {
severity_level = "AVAILABILITY"
include_mode = "INCLUDE_ANY"
delay_in_minutes = 5
tags = ["team:platform"]
}
errors-all-entities = {
severity_level = "ERRORS"
include_mode = "INCLUDE_ANY"
delay_in_minutes = 5
tags = ["team:platform"]
}
}
}
module "xmatters_notification" {
source = "git::https://github.com/microsoftexpert/terraform-dynatrace-xmatters-notification.git?ref=v1.0.0"
name = "xmatters-platform-team"
profile = module.alerting_profile.id # <- cross-module wire-in
url = "https://example.xmattersapi.com/api/xm/1/flow/platform-team-integration"
payload = "{ProblemTitle} - {ProblemSeverity} - {ProblemURL}"
headers = {
"Content-Type" = { value = "application/json" }
"Authorization" = { secret_value = var.xmatters_api_key }
}
# Reviewed and activated only after confirming the profile's rules and this
# integration's headers/payload are correct.
active = true
}💡
profile = module.alerting_profile.idis an implicit reference, notdepends_on— Terraform orders the alerting profile's creation before this notification automatically because the notification's configuration literally depends on the profile's computedid.
Required:
| Name | Type | Description |
|---|---|---|
name |
string |
Display name of the notification configuration |
profile |
string |
ID of the Dynatrace alerting profile this notification attaches to — pass a terraform-dynatrace-alerting-profile instance's id output |
url |
string |
The xMatters webhook URL |
payload |
string |
The notification message content, supporting Dynatrace's {Placeholder} syntax |
Optional:
| Name | Type | Default | Description |
|---|---|---|---|
active |
bool |
false |
Enables/disables the integration — secure-by-default |
insecure |
bool |
false |
Accepts any TLS certificate from the xMatters endpoint when true — secure-by-default |
legacy_id |
string |
null |
REST API v1 legacy key; leave null unless migrating |
headers |
map(object({...})) |
{} |
Extra HTTP headers, keyed by header name |
Full object schemas
variable "headers" {
type = map(object({
value = optional(string) # plain header value; may be empty
secret_value = optional(string) # secret header value; may be empty — sensitive = true in the live schema
}))
default = {}
}Each map entry's key is the header's name (required by the provider schema); main.tf derives the
rendered name argument from the map key, so header names are guaranteed unique at plan/validate
time rather than only failing at apply against a live environment.
| Output | Description | Notes |
|---|---|---|
id |
The Dynatrace object ID of this xMatters notification configuration | Always present |
name |
The display name of this xMatters notification configuration | Always present |
ℹ️ No header attribute — plain or secret — is ever emitted as a module output.
secret_valueissensitive = truein the live provider schema; this module omits the entireheaderscollection fromoutputs.tfrather than re-exporting it in any form.
- The
sensitive-at-point-of-use mechanic (the load-bearing detail inmain.tf). Terraform'svariableblock can only mark an entire variablesensitive = true— there is no language construct to flag a single nested attribute inside amap(object(...))type constraint. Marking the wholevar.headersvariable sensitive here would have two consequences, both undesirable: it would redact every header's non-secretvalueand the header names themselves, and — more fundamentally — Terraform's language rules do not permit a sensitive value, or a value derived from one, to be used as the collection expression driving afor_each(this includes thefor_eachinside adynamicblock), because the keys/values drivingfor_eachdetermine resource instance addressing and are surfaced in plan output and state; using a sensitive value there would undermine the whole point of marking it sensitive. Sincemain.tfiteratesvar.headerstwice — once indynamic "headers"and again in the nesteddynamic "header"— the entire map has to remain a plain, non-sensitive value for those twofor_eachexpressions to be legal at all. Instead,main.tfleavesvar.headersun-marked and wraps only the actual secret material at the exact point it is assigned into the resource argument:secret_value = try(sensitive(header.value.secret_value), null). The built-insensitivefunction marks that specific returned value as sensitive from that point forward, so Terraform's plan/apply output redacts it in the renderedheaderblock — without ever making the map, its keys, or the non-secretvaluefield sensitive, and without tripping the for_each restriction, because the sensitivity is applied downstream of thefor_eachevaluation, not upstream of it. The provider's own schema independently marksheader.secret_valuesensitive = true, so Terraform would redact this value in plan/apply output regardless of this module's own wrapping — the explicitsensitivecall is a defense-in-depth/clarity measure documented at the variable, not the sole source of redaction. - Ordering via implicit reference.
profileis expected to be wired from aterraform-dynatrace-alerting-profileinstance'sidoutput. Passing that output directly (rather than a literal string) creates an implicit dependency, so Terraform creates the alerting profile before this notification automatically — never usedepends_onas a substitute for this real reference. - Why
headersis a map, not a list, even though the provider modelsheaderas aset. Terraform's own set-block diffing already tolerates reordering (asetblock type diffs by full content, not position), so modelingvar.headersas a caller-keyed map is not working around positional brittleness in the underlyingheaderset. It exists instead to (a) give callers stable, name-addressable configuration in their own.tfvars/module calls, and (b) enforce unique header names atplan/validatetime — Terraform rejects duplicate map keys outright — rather than only surfacing a duplicate-header conflict when the API rejects the apply. try(x, default)on every optional field, even where the variable's own default already guarantees a non-null value (e.g.active,insecure) — defensive against a future provider or variable-schema revision reintroducing nullability, matching the sibling notification modules' convention.- Environment-scoped only.
dynatrace_xmatters_notificationis a Settings 2.0 object scoped to a single Dynatrace environment — unlike IAM/Account Management resources, there is no account-vs-environment API-surface split to document for this module. - No force-new claims made. The live schema dump used to author this module
(
terraform providers schema -json) does not expose SDK-levelForceNewflags. Do not treat this README's silence on immutable fields as confirmation that none exist — verify actual replace-vs-update behavior with a realterraform planif this matters for your change.
| Concern | Safe default | Opt-out (caller must set explicitly) |
|---|---|---|
| Notification activation | active defaults to false — a newly-applied integration does not fire |
Caller sets active = true after reviewing the URL, payload, headers, and target alerting profile |
| TLS certificate validation | insecure defaults to false — certificate validation is enforced on the outbound call to the xMatters endpoint |
Caller sets insecure = true explicitly, as a documented, reviewed exception |
| Header secret material | secret_value is wrapped in sensitive at the point of use and never appears in any module output |
N/A — non-negotiable; source secret header values from your own secrets manager |
| Extra HTTP headers | headers defaults to {} — no additional headers are sent unless the caller adds them |
Caller populates headers explicitly, keyed by header name |
| Legacy REST API v1 id | legacy_id defaults to null — Dynatrace computes and manages it server-side |
Caller sets legacy_id explicitly only when migrating a pre-Settings-2.0 reference |
terraform init -backend=false
terraform validate
terraform fmt -checkPin the module source to an explicit tag — ?ref=v1.0.0 — never a branch. This library is
plan-only: a human runs terraform apply from CI after reviewing the plan.
terraform validate/terraform fmt -checkconfirm: HCL syntax is well-formed, every required variable (name,profile,url,payload) is supplied by the caller, theheadersmap conforms to itsobject({...})type constraint, and no argument not present in the provider schema is passed todynatrace_xmatters_notification.this.- Only a real
terraform plan(orapply) against a live Dynatrace environment can confirm: that theprofileid actually resolves to an existing alerting profile, that the configured OAuth client or API token actually holdssettings:objects:write/settings.write, that the xMattersurlendpoint accepts the rendered payload format, and the real-world effect ofinsecureagainst that endpoint's actual certificate chain. - No cloud apply is performed by this authoring process — this module's static-analysis proof gate
ends at
validate/fmt, consistent with this library's plan-only posture.
$ terraform output
xmatters_notification_id = "vu9U3hXa3q0AAAABABBkeG1hdHRlcnMtbm90aWZpY2F0aW9uAAA="
xmatters_notification_name = "xmatters-platform-team"
ℹ️ The module's own outputs are named
idandname; a calling root module typically re-exposes them under a more descriptive name (as inexamples/quickstart/main.tf), which is why the sample above showsxmatters_notification_id/xmatters_notification_namerather than the bareid/name.
| Symptom | Cause | Fix |
|---|---|---|
Authentication error at apply, even though the OAuth client has scopes granted |
Provisioned only a legacy API token, or granted account-scoped IAM policy scopes instead of settings:objects:* |
Confirm the OAuth client's IAM policy grants settings:objects:read/settings:objects:write (or the API token has settings.read/settings.write) — this is an environment-scoped Settings 2.0 resource, not an IAM/Account Management resource |
Error: Invalid for_each argument referencing a sensitive value, if you modify main.tf |
Marking var.headers itself sensitive = true (not something this module does, but a common mistake when extending it) reintroduces Terraform's for-each-on-sensitive-value restriction |
Keep sensitive wrapped only around header.value.secret_value at the point of use, as authored — never mark the whole headers variable sensitive |
terraform apply succeeds but xMatters never receives a notification even with active = true |
The referenced profile's alerting profile has no matching rules, or its rules' severity/tag scope doesn't cover the entities involved |
Confirm the dynatrace_alerting profile (owned by terraform-dynatrace-alerting-profile) has at least one rule whose scope covers the entities in question |
| A header's secret value shows up masked in Terraform plan output but still appears in plaintext in xMatters' own request logs | This module's sensitive wrapping only affects Terraform's own plan/apply/state display — it does not control how the receiving xMatters endpoint logs inbound requests |
Review xMatters-side logging/retention settings for the webhook flow independently; Terraform-side redaction is not an end-to-end control |
insecure = true was set as a quick fix for a TLS certificate error |
Common workaround habit — but this disables certificate validation for every subsequent notification call to that endpoint | Fix the actual certificate chain at the xMatters endpoint; treat insecure = true as a reviewed, documented exception, never a default troubleshooting step |
| Duplicate-looking headers or a header silently overwritten | Two entries in var.headers used the same map key (header name) |
Map keys must be unique by construction — Terraform rejects duplicate keys at parse time; rename one of the conflicting header entries |
- Terraform Registry —
dynatrace-oss/dynatraceprovider, resourcedynatrace_xmatters_notification - Sibling module:
terraform-dynatrace-alerting-profile(suppliesprofile) - This module's
SCOPE.md
💙 "Infrastructure as Code should be standardized, consistent, and secure."