1	{"ddtags": "env:prod,service:api", "hostname": "ip-10-0-1-23", "message": "user [REDACTED-EMAIL] signed in", "service": "api"}
2	{"ddtags": "env:prod", "hostname": "ip-10-0-1-24", "message": "no email here", "service": "worker"}
3	{"hostname": "ip-10-0-1-25", "message": "multiple [REDACTED-EMAIL] and [REDACTED-EMAIL]", "service": "api"}
4	{"attributes": {"request.id": "abc-123", "user.email": "[REDACTED-EMAIL]"}, "ddtags": "env:staging", "hostname": "ip-10-0-2-99", "message": "ok", "service": "auth"}
5	{"hostname": "ip-10-0-1-50", "message": "json blob {\"email\":\"[REDACTED-EMAIL]\"}", "service": "api"}
6


1	email_re = r'[\w._%+\-]+@[\w.-]+\.[A-Za-z]{2,}'
2
3	if is_string(.message) {
4	  .message = replace(string!(.message), email_re, "[REDACTED-EMAIL]")
5	}
6
7	if is_object(.attributes) {
8	  attrs, _err = object(.attributes)
9	  if is_object(attrs) {
10	    .attributes = map_values(attrs) -> |v| {
11	      if is_string(v) { replace(string!(v), email_re, "[REDACTED-EMAIL]") } else { v }
12	    }
13	  }
14	}
15	.
16


1	#!/usr/bin/env bash
2	set -euo pipefail
3	DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
4	ACTUAL="$DIR/.actual.ndjson"
5	vector vrl -p "$DIR/program.vrl" -i "$DIR/inputs.ndjson" -q -o > "$ACTUAL"
6	diff -u <(jq -c . "$DIR/expected.ndjson") <(jq -c . "$ACTUAL")
7	echo "redact_emails_dd_agent_logs VRL OK"
8


1	variable "name" {
2	  description = "The name of the Helm release."
3	  type        = string
4	  default     = "immuta-dd"
5	}
6
7	variable "namespace" {
8	  description = "The Kubernetes namespace to deploy the Datadog agent into."
9	  type        = string
10	  default     = "datadog"
11	}
12
13	variable "create_namespace" {
14	  description = "Whether to create the namespace. Set to false if the namespace already exists."
15	  type        = bool
16	  default     = true
17	}
18
19	variable "chart_version" {
20	  description = "The version of the Datadog Helm chart to use."
21	  type        = string
22	}
23
24	variable "env" {
25	  description = "The environment that resources are deployed to. i.e. nonprod, prod"
26	  type        = string
27	}
28
29	variable "global_segment" {
30	  description = "The global segment to use for tagging and other purposes."
31	  type        = string
32	}
33
34	variable "image_tag" {
35	  description = "The tag of the Datadog agent image to use."
36	  type        = string
37	}
38
39	variable "cluster_name" {
40	  description = "The name of the Kubernetes cluster, to be used for tagging."
41	  type        = string
42	}
43
44	variable "datadog_tags" {
45	  description = "A map of global tags to apply to all metrics, traces, and logs."
46	  type        = list(string)
47	  default     = []
48	}
49
50	variable "enable_now_obfusicator_options" {
51	  description = "Whether to enable the NOWRDS obfuscator options in the Datadog agent configuration."
52	  type        = bool
53	  default     = false
54	}
55
56	variable "now_obfusicator_options" {
57	  description = "A map of options to configure the NOWRDS obfuscator in the Datadog agent configuration."
58	  type        = map(string)
59	  default = {
60	    replace_digits = "true"
61	  }
62	}
63
64	variable "datadog_api_key_secret_name" {
65	  description = "The name of the Kubernetes secret holding the Datadog API key."
66	  type        = string
67	  default     = "datadog-api-key"
68	}
69
70	variable "cluster_agent_token_secret_name" {
71	  description = "The name of the Kubernetes secret for the cluster agent token."
72	  type        = string
73	  default     = "datadog-clusteragent-token"
74	}
75
76	variable "log_level" {
77	  description = "The log level for the Datadog agent."
78	  type        = string
79	  default     = "ERROR"
80	}
81
82	variable "apm_enabled" {
83	  description = "Whether to enable APM for the Datadog agent."
84	  type        = bool
85	  default     = true
86	}
87
88	variable "container_exclude" {
89	  description = "A list of container names to exclude from monitoring."
90	  type        = string
91	  default     = "kube_namespace:datadog name:enterprise"
92	}
93
94	variable "container_exclude_logs" {
95	  description = "A list of container names to exclude from log collection."
96	  type        = string
97	  default     = "image:registry.k8s.io/autoscaling/cluster-autoscaler image:registry.ic-ops.net/cache-k8s-registry/autoscaling/cluster-autoscaler"
98	}
99
100	variable "tags" {
101	  description = "A list of global tags to apply to all metrics, traces, and logs."
102	  type        = map(any)
103	  default     = {}
104	}
105
106	variable "agents_priority_class_name" {
107	  description = "The priority class name for the Datadog agents."
108	  type        = string
109	  default     = "cloud-ops-system"
110	}
111
112	variable "enable_nowrds_monitoring" {
113	  description = "Enable monitoring for NOWRDS."
114	  type        = bool
115	  default     = false
116	}
117
118	variable "enable_mrds_monitoring" {
119	  description = "Enable monitoring for MRDS."
120	  type        = bool
121	  default     = false
122	}
123
124	variable "logs_intake_url" {
125	  description = "OPW-compatible logs intake URL. When set, the agent ships logs to this endpoint via DD_OBSERVABILITY_PIPELINES_WORKER_LOGS_URL. For cluster-internal Vector use module.vector.service_endpoint_dd_agent. When null (default), logs ship direct to Datadog SaaS."
126	  type        = string
127	  default     = null
128
129	  validation {
130	    condition     = var.logs_intake_url == null || can(regex("^https?://", var.logs_intake_url))
131	    error_message = "logs_intake_url must be a URL starting with http:// or https://, or null."
132	  }
133	}
134


1	# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2	# Deploys the Datadog Agent to a Kubernetes cluster using the official Helm chart.
3	# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5	# ---------------------------------------------------------------------------------------------------------------------
6	# SET TERRAFORM REQUIREMENTS FOR RUNNING THIS MODULE
7	# ---------------------------------------------------------------------------------------------------------------------
8
9	terraform {
10	  required_version = ">= 1.3.0, < 2.0"
11
12	  required_providers {
13	    helm = {
14	      source  = "hashicorp/helm"
15	      version = "~> 2.0"
16	    }
17	    kubernetes = {
18	      source  = "hashicorp/kubernetes"
19	      version = "~> 2.0"
20	    }
21	  }
22	}
23
24	locals {
25	  apm_rules = file("${path.module}/processing-rules/${var.env}/apm.json")
26	  log_rules = file("${path.module}/processing-rules/${var.env}/rules.json")
27	  nowrds_instance = var.enable_nowrds_monitoring ? (<<-EON
28	    - dbm: true
29	      ssl: allow
30	      host: "%%env_NOWRDS_HOST%%"
31	      port: "%%env_NOWRDS_PORT%%"
32	      username: "%%env_NOWRDS_USERNAME%%"
33	      password: "%%env_NOWRDS_PASSWORD%%"
34	      collect_schemas:
35	        enabled: true
36	      database_autodiscovery:
37	        enabled: true
38	      tags:
39	        - "dbinstanceidentifier:%%env_NOWRDS_INSTANCE_NAME%%"
40	        - "env:%%env_DD_ENV%%"
41	  EON
42	  ) : null
43	}
44	# ---------------------------------------------------------------------------------------------------------------------
45	# CREATE A NAMESPACE
46	# ---------------------------------------------------------------------------------------------------------------------
47	resource "kubernetes_namespace" "this" {
48	  count = var.create_namespace ? 1 : 0
49	  metadata {
50	    name = var.namespace
51	  }
52	}
53
54	resource "kubernetes_config_map" "datadog_processing_rules" {
55	  metadata {
56	    name      = "rules-config"
57	    namespace = var.create_namespace ? kubernetes_namespace.this[0].metadata[0].name : var.namespace
58	  }
59	  data = {
60	    //"apm"  = local.apm_rules
61	    "apm" = file("${path.module}/processing-rules/${var.env}/apm.json")
62	    //"logs" = local.log_rules
63	    "logs" = file("${path.module}/processing-rules/${var.env}/rules.json")
64	  }
65	  depends_on = [kubernetes_namespace.this]
66	}
67
68	resource "kubernetes_manifest" "external-secret-dd-api-key" {
69	  manifest = {
70	    apiVersion = "external-secrets.io/v1"
71	    kind       = "ExternalSecret"
72	    metadata = {
73	      name      = "datadog-api-key"
74	      namespace = var.create_namespace ? kubernetes_namespace.this[0].metadata[0].name : var.namespace
75	    }
76	    spec = {
77	      refreshInterval = "1h0m0s"
78	      secretStoreRef = {
79	        name = "vault"
80	        kind = "ClusterSecretStore"
81	      }
82	      target = {
83	        name           = "datadog-api-key"
84	        creationPolicy = "Owner"
85	      }
86	      data = [{
87	        secretKey = "api-key"
88	        remoteRef = {
89	          key      = "saas/infrastructure/datadog-api-key"
90	          property = "apiKey"
91	        }
92	      }]
93	    }
94	  }
95	}
96
97	resource "kubernetes_manifest" "external-secret-dd-api-token" {
98	  manifest = {
99	    apiVersion = "external-secrets.io/v1"
100	    kind       = "ExternalSecret"
101	    metadata = {
102	      name      = "datadog-clusteragent-token"
103	      namespace = var.create_namespace ? kubernetes_namespace.this[0].metadata[0].name : var.namespace
104	    }
105	    spec = {
106	      refreshInterval = "1h0m0s"
107	      secretStoreRef = {
108	        name = "vault"
109	        kind = "ClusterSecretStore"
110	      }
111	      target = {
112	        name           = "datadog-clusteragent-token"
113	        creationPolicy = "Owner"
114	      }
115	      data = [{
116	        secretKey = "token"
117	        remoteRef = {
118	          key      = "saas/infrastructure/datadog-helper/${var.env}/${var.global_segment}"
119	          property = "token"
120	        }
121	      }]
122	    }
123	  }
124	}
125
126	resource "kubernetes_manifest" "external-secret" {
127	  count = (var.enable_mrds_monitoring || var.enable_nowrds_monitoring) ? 1 : 0
128
129	  manifest = {
130	    apiVersion = "external-secrets.io/v1"
131	    kind       = "ExternalSecret"
132	    metadata = {
133	      name      = "datadog-env"
134	      namespace = var.create_namespace ? kubernetes_namespace.this[0].metadata[0].name : var.namespace
135	    }
136	    spec = {
137	      refreshInterval = "1h0m0s"
138	      secretStoreRef = {
139	        name = "vault"
140	        kind = "ClusterSecretStore"
141	      }
142	      target = {
143	        name           = "datadog-env"
144	        creationPolicy = "Owner"
145	      }
146	      data = concat(var.enable_mrds_monitoring ? [
147	        {
148	          secretKey = "MRDS_HOST"
149	          remoteRef = {
150	            key      = "saas/infrastructure/datadog-helper/${var.env}/${var.global_segment}"
151	            property = "rds-host"
152	          }
153	        },
154	        {
155	          secretKey = "MRDS_PORT"
156	          remoteRef = {
157	            key      = "saas/infrastructure/datadog-helper/${var.env}/${var.global_segment}"
158	            property = "rds-port"
159	          }
160	        },
161	        {
162	          secretKey = "MRDS_USERNAME"
163	          remoteRef = {
164	            key      = "saas/infrastructure/datadog-helper/${var.env}/${var.global_segment}"
165	            property = "rds-username"
166	          }
167	        },
168	        {
169	          secretKey = "MRDS_PASSWORD"
170	          remoteRef = {
171	            key      = "saas/infrastructure/datadog-helper/${var.env}/${var.global_segment}"
172	            property = "rds-password"
173	          }
174	        },
175	        {
176	          secretKey = "MRDS_REGION"
177	          remoteRef = {
178	            key      = "saas/infrastructure/datadog-helper/${var.env}/${var.global_segment}"
179	            property = "rds-region"
180	          }
181	        },
182	        {
183	          secretKey = "MRDS_INSTANCE_NAME"
184	          remoteRef = {
185	            key      = "saas/infrastructure/datadog-helper/${var.env}/${var.global_segment}"
186	            property = "rds-instance"
187	          }
188	        },
189	        ] : [], var.enable_nowrds_monitoring ? [
190	        {
191	          secretKey = "NOWRDS_HOST"
192	          remoteRef = {
193	            key      = "saas/infrastructure/datadog-helper/${var.env}/${var.global_segment}"
194	            property = "NOWRDS_HOST"
195	          }
196	        },
197	        {
198	          secretKey = "NOWRDS_PORT"
199	          remoteRef = {
200	            key      = "saas/infrastructure/datadog-helper/${var.env}/${var.global_segment}"
201	            property = "NOWRDS_PORT"
202	          }
203	        },
204	        {
205	          secretKey = "NOWRDS_USERNAME"
206	          remoteRef = {
207	            key      = "saas/infrastructure/datadog-helper/${var.env}/${var.global_segment}"
208	            property = "NOWRDS_USERNAME"
209	          }
210	        },
211	        {
212	          secretKey = "NOWRDS_PASSWORD"
213	          remoteRef = {
214	            key      = "saas/infrastructure/datadog-helper/${var.env}/${var.global_segment}"
215	            property = "NOWRDS_PASSWORD"
216	          }
217	        },
218	        {
219	          secretKey = "NOWRDS_INSTANCE_NAME"
220	          remoteRef = {
221	            key      = "saas/infrastructure/datadog-helper/${var.env}/${var.global_segment}"
222	            property = "NOWRDS_INSTANCE_NAME"
223	          }
224	        }
225	      ] : [])
226	    }
227	  }
228	  depends_on = [kubernetes_namespace.this]
229	}
230	# ---------------------------------------------------------------------------------------------------------------------
231	# DEPLOY A HELM CHART
232	# ---------------------------------------------------------------------------------------------------------------------
233	resource "helm_release" "this" {
234	  name = var.name
235	  #namespace = kubernetes_namespace.this.metadata[0].name
236	  namespace = var.create_namespace ? kubernetes_namespace.this[0].metadata[0].name : var.namespace
237	  wait      = false # Set to true if you want Terraform to wait for resources to be ready
238
239	  repository = "https://helm.datadoghq.com"
240	  chart      = "datadog"
241	  version    = var.chart_version
242	  values = [
243	    templatefile("${path.module}/values.yaml.tftpl", {
244	      cluster_name               = var.cluster_name
245	      env                        = var.env == "staging" ? "nonprod" : var.env
246	      global_segment             = var.global_segment
247	      image_tag                  = var.image_tag
248	      watch_namespace            = var.create_namespace ? kubernetes_namespace.this[0].metadata[0].name : var.namespace
249	      tags                       = concat(["global_segment:${var.global_segment}"], var.datadog_tags)
250	      agents_priority_class_name = var.agents_priority_class_name
251
252	      apm_enabled = var.apm_enabled
253
254	      container_exclude      = var.container_exclude
255	      container_exclude_logs = var.container_exclude_logs
256
257	      enable_mrds_monitoring         = var.enable_mrds_monitoring
258	      enable_nowrds_monitoring       = var.enable_nowrds_monitoring
259	      enable_now_obfusicator_options = var.enable_now_obfusicator_options
260	      now_obfusicator_options        = var.now_obfusicator_options
261
262	      datadog_api_key_secret_name     = var.datadog_api_key_secret_name
263	      cluster_agent_token_secret_name = var.cluster_agent_token_secret_name
264	      log_level                       = var.log_level
265
266	      logs_intake_url = var.logs_intake_url
267	    })
268	  ]
269
270	  depends_on = [
271	    kubernetes_namespace.this,
272	    kubernetes_manifest.external-secret,
273	    kubernetes_manifest.external-secret-dd-api-key,
274	    kubernetes_manifest.external-secret-dd-api-token
275	  ]
276	}
277
278	module "netpol" {
279	  source = "git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/security/network-policy.git?ref=v1.1.0"
280
281	  name             = join("-", [var.name, "netpol"])
282	  namespace        = var.namespace
283	  network_policies = local.netpols
284
285	  tags = merge(var.tags, { "sourceModule" = "/internal-modules/kubernetes/monitoring/datadog" })
286
287	  depends_on = [
288	    kubernetes_namespace.this
289	  ]
290	}
291
292


1	targetSystem: "linux"
2	registry: public.ecr.aws/datadog
3	datadog:
4	  operator:
5	    enabled: false
6	  apiKeyExistingSecret: ${datadog_api_key_secret_name}
7	  clusterName: ${cluster_name}
8	  securityContext:
9	    seLinuxOptions:
10	      user: "system_u"
11	      role: "system_r"
12	      type: "spc_t"
13	      level: "s0"
14	  logLevel: ${log_level}
15	  containerExclude: "${container_exclude}"
16	  containerExcludeLogs: "${container_exclude_logs}"
17	  logs:
18	    enabled: true
19	    containerCollectAll: true
20	    autoMultiLineDetection: true
21	  criSocketPath: /run/containerd/containerd.sock
22	  dogstatsd:
23	    port: 8125
24	    useHostPort: true
25	    nonLocalTraffic: true
26	  otlp:
27	    receiver:
28	      protocols:
29	        grpc:
30	          enabled: true
31	          endpoint: 0.0.0.0:4317
32	        http:
33	          enabled: true
34	          endpoint: 0.0.0.0:4318
35	  apm:
36	    enabled: ${apm_enabled}
37	    instrumentation:
38	      skipKPITelemetry: true
39	  processAgent:
40	    enabled: false
41	  networkMonitoring:
42	    enabled: false
43	  tags:
44	    - "global_segment:${global_segment}"
45	%{ for tag in tags ~}
46	    - "${tag}"
47	%{ endfor ~}
48	  confd:
49	    journald_conf.yaml: |-
50	      logs:
51	        - type: journald
52	          path: /var/log/journal/
53	          start_position: beginning
54	          source: journald
55	          exclude_units:
56	            - kubelet.service
57	  env:
58	    - name: DD_AUTOCONFIG_INCLUDE_FEATURES
59	      value: "containerd"
60	    - name: DD_ENV
61	      value: ${env}
62	    - name: DD_KUBERNETES_NODE_LABELS_AS_TAGS
63	      value: '{"kubernetes.io/hostname":"node_name","kubernetes.io/os":"node_os","beta.kubernetes.io/instance-type":"node_instance_type","node.kubernetes.io/lifecycle":"node_lifecycle","failure-domain.beta.kubernetes.io/region":"node_region","failure-domain.beta.kubernetes.io/zone":"node_zone"}'
64	    - name: DD_LOGS_CONFIG_PROCESSING_RULES
65	      valueFrom:
66	        configMapKeyRef:
67	          name: rules-config
68	          key: logs
69	    - name: DD_APM_REPLACE_TAGS
70	      valueFrom:
71	        configMapKeyRef:
72	          name: rules-config
73	          key: apm
74	    - name: DD_CLOUD_PROVIDER_METADATA
75	      value: "aws"
76	    - name: CLUSTER_NAME
77	      value: "${cluster_name}"
78	    - name: DD_APM_PROBABILISTIC_SAMPLER_ENABLED
79	      value: true
80	    - name: DD_APM_PROBABILISTIC_SAMPLER_SAMPLING_PERCENTAGE
81	      value: 15
82	%{ if logs_intake_url != null ~}
83	    - name: DD_OBSERVABILITY_PIPELINES_WORKER_LOGS_ENABLED
84	      value: "true"
85	    - name: DD_OBSERVABILITY_PIPELINES_WORKER_LOGS_URL
86	      value: "${logs_intake_url}"
87	%{ endif ~}
88	clusterAgent:
89	  env:
90	    - name: DD_ENV
91	      value: ${env}
92	    - name: DD_KUBERNETES_NODE_LABELS_AS_TAGS
93	      value: '{"kubernetes.io/hostname":"node_name","kubernetes.io/os":"node_os","beta.kubernetes.io/instance-type":"node_instance_type","node.kubernetes.io/lifecycle":"node_lifecycle","failure-domain.beta.kubernetes.io/region":"node_region","failure-domain.beta.kubernetes.io/zone":"node_zone"}'
94	    - name: DD_LOGS_CONFIG_PROCESSING_RULES
95	      valueFrom:
96	        configMapKeyRef:
97	          name: rules-config
98	          key: logs
99	    - name: DD_APM_REPLACE_TAGS
100	      valueFrom:
101	        configMapKeyRef:
102	          name: rules-config
103	          key: apm
104	    - name: DD_CLOUD_PROVIDER_METADATA
105	      value: "aws"
106	    - name: CLUSTER_NAME
107	      value: "${cluster_name}"
108	%{ if enable_mrds_monitoring ~}
109	    - name: MRDS_HOST
110	      valueFrom:
111	        secretKeyRef:
112	          name: datadog-env
113	          key: MRDS_HOST
114	    - name: MRDS_PORT
115	      valueFrom:
116	        secretKeyRef:
117	          name: datadog-env
118	          key: MRDS_PORT
119	    - name: MRDS_USERNAME
120	      valueFrom:
121	        secretKeyRef:
122	          name: datadog-env
123	          key: MRDS_USERNAME
124	    - name: MRDS_PASSWORD
125	      valueFrom:
126	        secretKeyRef:
127	          name: datadog-env
128	          key: MRDS_PASSWORD
129	    - name: MRDS_REGION
130	      valueFrom:
131	        secretKeyRef:
132	          name: datadog-env
133	          key: MRDS_REGION
134	    - name: MRDS_INSTANCE_NAME
135	      valueFrom:
136	        secretKeyRef:
137	          name: datadog-env
138	          key: MRDS_INSTANCE_NAME
139	%{ endif ~}
140	%{ if enable_nowrds_monitoring ~}
141	    - name: NOWRDS_HOST
142	      valueFrom:
143	        secretKeyRef:
144	          name: datadog-env
145	          key: NOWRDS_HOST
146	    - name: NOWRDS_PORT
147	      valueFrom:
148	        secretKeyRef:
149	          name: datadog-env
150	          key: NOWRDS_PORT
151	    - name: NOWRDS_USERNAME
152	      valueFrom:
153	        secretKeyRef:
154	          name: datadog-env
155	          key: NOWRDS_USERNAME
156	    - name: NOWRDS_PASSWORD
157	      valueFrom:
158	        secretKeyRef:
159	          name: datadog-env
160	          key: NOWRDS_PASSWORD
161	    - name: NOWRDS_INSTANCE_NAME
162	      valueFrom:
163	        secretKeyRef:
164	          name: datadog-env
165	          key: NOWRDS_INSTANCE_NAME
166	%{ endif ~}
167	  confd:
168	%{ if enable_mrds_monitoring || enable_nowrds_monitoring ~}
169	    postgres.yaml: |-
170	      cluster_check: true
171	      init_config:
172	      instances:
173	%{ if enable_mrds_monitoring ~}
174	        - dbm: true
175	          ssl: allow
176	          host: "%%env_MRDS_HOST%%"
177	          port: "%%env_MRDS_PORT%%"
178	          username: "%%env_MRDS_USERNAME%%"
179	          password: "%%env_MRDS_PASSWORD%%"
180	          collect_schemas:
181	            enabled: true
182	          database_autodiscovery:
183	            enabled: true
184	          tags:
185	            - "dbinstanceidentifier:%%env_MRDS_INSTANCE_NAME%%"
186	            - "env:%%env_DD_ENV%%"
187	%{ endif ~}
188	%{ if enable_nowrds_monitoring ~}
189	        - dbm: true
190	          ssl: allow
191	          host: "%%env_NOWRDS_HOST%%"
192	          port: "%%env_NOWRDS_PORT%%"
193	          username: "%%env_NOWRDS_USERNAME%%"
194	          password: "%%env_NOWRDS_PASSWORD%%"
195	          collect_schemas:
196	            enabled: true
197	          database_autodiscovery:
198	            enabled: true
199	%{ if enable_now_obfusicator_options ~}
200	          obfuscator_options:
201	%{ for key, option in now_obfusicator_options ~}
202	            ${key}: ${option}
203	%{ endfor ~}
204	%{ endif ~}
205	          tags:
206	            - "dbinstanceidentifier:%%env_NOWRDS_INSTANCE_NAME%%"
207	            - "env:%%env_DD_ENV%%"
208	%{ endif ~}
209	%{ endif ~}
210	  enabled: true
211	  image:
212	    tag: ${image_tag}
213	  priorityClassName: ${agents_priority_class_name}
214	  tokenExistingSecret: ${cluster_agent_token_secret_name}
215	  admissionController:
216	    enabled: true
217	    configMode: socket
218	agents:
219	  enabled: true
220	  image:
221	    tag: ${image_tag}
222	  priorityClassName: ${agents_priority_class_name}
223	  tolerations:
224	    - operator: Exists
225	  useHostNetwork: true
226	  volumes:
227	    - name: journald
228	      hostPath:
229	        path: "/var/log/journal"
230	    - name: machineid
231	      hostPath:
232	        path: "/etc/machine-id"
233	  volumeMounts:
234	    - name: journald
235	      mountPath: "/var/log/journal"
236	      readOnly: true
237	    - name: machineid
238	      mountPath: "/etc/machine-id"
239	      readOnly: true
240


1	## Requirements
2
3	| Name | Version |
4	|------|---------|
5	|  [terraform](#requirement\_terraform) | >= 1.3.0, < 2.0 |
6	|  [helm](#requirement\_helm) | ~> 2.0 |
7	|  [kubernetes](#requirement\_kubernetes) | ~> 2.0 |
8
9	## Providers
10
11	| Name | Version |
12	|------|---------|
13	|  [helm](#provider\_helm) | ~> 2.0 |
14	|  [kubernetes](#provider\_kubernetes) | ~> 2.0 |
15
16	## Modules
17
18	| Name | Source | Version |
19	|------|--------|---------|
20	|  [netpol](#module\_netpol) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/security/network-policy.git | v1.1.0 |
21
22	## Resources
23
24	| Name | Type |
25	|------|------|
26	| [helm_release.this](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource |
27	| [kubernetes_config_map.datadog_processing_rules](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/config_map) | resource |
28	| [kubernetes_manifest.external-secret](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource |
29	| [kubernetes_manifest.external-secret-dd-api-key](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource |
30	| [kubernetes_manifest.external-secret-dd-api-token](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource |
31	| [kubernetes_namespace.this](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/namespace) | resource |
32
33	## Inputs
34
35	| Name | Description | Type | Default | Required |
36	|------|-------------|------|---------|:--------:|
37	|  [agents\_priority\_class\_name](#input\_agents\_priority\_class\_name) | The priority class name for the Datadog agents. | `string` | `"cloud-ops-system"` | no |
38	|  [apm\_enabled](#input\_apm\_enabled) | Whether to enable APM for the Datadog agent. | `bool` | `true` | no |
39	|  [chart\_version](#input\_chart\_version) | The version of the Datadog Helm chart to use. | `string` | n/a | yes |
40	|  [cluster\_agent\_token\_secret\_name](#input\_cluster\_agent\_token\_secret\_name) | The name of the Kubernetes secret for the cluster agent token. | `string` | `"datadog-clusteragent-token"` | no |
41	|  [cluster\_name](#input\_cluster\_name) | The name of the Kubernetes cluster, to be used for tagging. | `string` | n/a | yes |
42	|  [container\_exclude](#input\_container\_exclude) | A list of container names to exclude from monitoring. | `string` | `"kube_namespace:datadog name:enterprise"` | no |
43	|  [container\_exclude\_logs](#input\_container\_exclude\_logs) | A list of container names to exclude from log collection. | `string` | `"image:registry.k8s.io/autoscaling/cluster-autoscaler image:registry.ic-ops.net/cache-k8s-registry/autoscaling/cluster-autoscaler"` | no |
44	|  [create\_namespace](#input\_create\_namespace) | Whether to create the namespace. Set to false if the namespace already exists. | `bool` | `true` | no |
45	|  [datadog\_api\_key\_secret\_name](#input\_datadog\_api\_key\_secret\_name) | The name of the Kubernetes secret holding the Datadog API key. | `string` | `"datadog-api-key"` | no |
46	|  [datadog\_tags](#input\_datadog\_tags) | A map of global tags to apply to all metrics, traces, and logs. | `list(string)` | `[]` | no |
47	|  [enable\_mrds\_monitoring](#input\_enable\_mrds\_monitoring) | Enable monitoring for MRDS. | `bool` | `false` | no |
48	|  [enable\_now\_obfusicator\_options](#input\_enable\_now\_obfusicator\_options) | Whether to enable the NOWRDS obfuscator options in the Datadog agent configuration. | `bool` | `false` | no |
49	|  [enable\_nowrds\_monitoring](#input\_enable\_nowrds\_monitoring) | Enable monitoring for NOWRDS. | `bool` | `false` | no |
50	|  [env](#input\_env) | The environment that resources are deployed to. i.e. nonprod, prod | `string` | n/a | yes |
51	|  [global\_segment](#input\_global\_segment) | The global segment to use for tagging and other purposes. | `string` | n/a | yes |
52	|  [image\_tag](#input\_image\_tag) | The tag of the Datadog agent image to use. | `string` | n/a | yes |
53	|  [log\_level](#input\_log\_level) | The log level for the Datadog agent. | `string` | `"ERROR"` | no |
54	|  [logs\_intake\_url](#input\_logs\_intake\_url) | OPW-compatible logs intake URL. When set, the agent ships logs to this endpoint via DD\_OBSERVABILITY\_PIPELINES\_WORKER\_LOGS\_URL. For cluster-internal Vector use module.vector.service\_endpoint\_dd\_agent. When null (default), logs ship direct to Datadog SaaS. | `string` | `null` | no |
55	|  [name](#input\_name) | The name of the Helm release. | `string` | `"immuta-dd"` | no |
56	|  [namespace](#input\_namespace) | The Kubernetes namespace to deploy the Datadog agent into. | `string` | `"datadog"` | no |
57	|  [now\_obfusicator\_options](#input\_now\_obfusicator\_options) | A map of options to configure the NOWRDS obfuscator in the Datadog agent configuration. | `map(string)` |
{
  "replace_digits": "true"
}
 | no |
58	|  [tags](#input\_tags) | A list of global tags to apply to all metrics, traces, and logs. | `map(any)` | `{}` | no |
59
60	## Outputs
61
62	| Name | Description |
63	|------|-------------|
64	|  [helm\_chart\_revision](#output\_helm\_chart\_revision) | Revision of the release |
65	|  [helm\_chart\_status](#output\_helm\_chart\_status) | Status of the release |
66	|  [helm\_chart\_values](#output\_helm\_chart\_values) | Helm Chart values of the chart |
67	|  [helm\_chart\_version](#output\_helm\_chart\_version) | Version of the release |
68
69	## OPW Logs Intake (optional)
70
71	By default, the agent ships logs directly to Datadog SaaS over HTTPS. To
72	redirect logs through a Datadog Observability Pipelines Worker (OPW), or
73	through a Vector deployment functioning as one, set `logs_intake_url`:
74
75	```hcl
76	module "datadog_agent" {
77	  # ...
78	  logs_intake_url = module.vector.service_endpoint_dd_agent
79	  # e.g. "http://vector.vector.svc.cluster.local:8282"
80	}
81	```
82
83	This sets two env vars on the agent containers:
84
85	- `DD_OBSERVABILITY_PIPELINES_WORKER_LOGS_ENABLED=true`
86	- `DD_OBSERVABILITY_PIPELINES_WORKER_LOGS_URL=<logs_intake_url>`
87
88	Agent-side processing rules (`DD_LOGS_CONFIG_PROCESSING_RULES`) continue to
89	execute before transmission, regardless of destination. Metrics and APM
90	(traces) are unaffected and continue shipping direct to Datadog.
91
92	When `logs_intake_url` is `null`, the rendered helm values are byte-identical
93	to the v1.1.x line — there is no behavior change for clusters that do not opt in.
94
95	Reference: <https://docs.datadoghq.com/observability_pipelines/sources/datadog_agent/>
96


1	#!/usr/bin/env bash
2	# Renders values.yaml.tftpl under two input scenarios and diffs against the
3	# locked expected outputs. Catches accidental drift in the helm values output.
4	set -euo pipefail
5
6	DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7	MODULE_DIR="$(cd "$DIR/../.." && pwd)"
8	TMP="$(mktemp -d)"
9	trap 'rm -rf "$TMP"' EXIT
10
11	render() {
12	  local intake_url="$1"
13	  local out="$2"
14
15	  cat > "$TMP/main.tf" <<EOF
16	output "rendered" {
17	  value = templatefile("\${path.module}/values.yaml.tftpl", {
18	    cluster_name                    = "test-cluster"
19	    env                             = "nonprod"
20	    global_segment                  = "test"
21	    image_tag                       = "7.55.0"
22	    watch_namespace                 = "datadog"
23	    tags                            = ["env:test"]
24	    agents_priority_class_name      = "cloud-ops-system"
25	    apm_enabled                     = true
26	    container_exclude               = ""
27	    container_exclude_logs          = ""
28	    enable_mrds_monitoring          = false
29	    enable_nowrds_monitoring        = false
30	    enable_now_obfusicator_options  = false
31	    now_obfusicator_options         = { replace_digits = "true" }
32	    datadog_api_key_secret_name     = "datadog-api-key"
33	    cluster_agent_token_secret_name = "datadog-clusteragent-token"
34	    log_level                       = "ERROR"
35	    logs_intake_url                 = ${intake_url}
36	  })
37	}
38	EOF
39	  ln -sf "$MODULE_DIR/values.yaml.tftpl" "$TMP/values.yaml.tftpl"
40	  (cd "$TMP" && terraform init -backend=false -input=false >/dev/null && terraform apply -auto-approve -input=false >/dev/null)
41	  (cd "$TMP" && terraform output -raw rendered) > "$out"
42	  rm -rf "$TMP"/.terraform "$TMP"/terraform.tfstate*
43	}
44
45	render "null"                                                "$DIR/.actual-direct.yaml"
46	render '"http://vector.vector.svc.cluster.local:8282"'       "$DIR/.actual-via-vector.yaml"
47
48	diff -u "$DIR/expected-direct.yaml"     "$DIR/.actual-direct.yaml"
49	diff -u "$DIR/expected-via-vector.yaml" "$DIR/.actual-via-vector.yaml"
50
51	echo "datadog-agent values render OK"
52


1	targetSystem: "linux"
2	registry: public.ecr.aws/datadog
3	datadog:
4	  operator:
5	    enabled: false
6	  apiKeyExistingSecret: datadog-api-key
7	  clusterName: test-cluster
8	  securityContext:
9	    seLinuxOptions:
10	      user: "system_u"
11	      role: "system_r"
12	      type: "spc_t"
13	      level: "s0"
14	  logLevel: ERROR
15	  containerExclude: ""
16	  containerExcludeLogs: ""
17	  logs:
18	    enabled: true
19	    containerCollectAll: true
20	    autoMultiLineDetection: true
21	  criSocketPath: /run/containerd/containerd.sock
22	  dogstatsd:
23	    port: 8125
24	    useHostPort: true
25	    nonLocalTraffic: true
26	  otlp:
27	    receiver:
28	      protocols:
29	        grpc:
30	          enabled: true
31	          endpoint: 0.0.0.0:4317
32	        http:
33	          enabled: true
34	          endpoint: 0.0.0.0:4318
35	  apm:
36	    enabled: true
37	    instrumentation:
38	      skipKPITelemetry: true
39	  processAgent:
40	    enabled: false
41	  networkMonitoring:
42	    enabled: false
43	  tags:
44	    - "global_segment:test"
45	    - "env:test"
46	  confd:
47	    journald_conf.yaml: |-
48	      logs:
49	        - type: journald
50	          path: /var/log/journal/
51	          start_position: beginning
52	          source: journald
53	          exclude_units:
54	            - kubelet.service
55	  env:
56	    - name: DD_AUTOCONFIG_INCLUDE_FEATURES
57	      value: "containerd"
58	    - name: DD_ENV
59	      value: nonprod
60	    - name: DD_KUBERNETES_NODE_LABELS_AS_TAGS
61	      value: '{"kubernetes.io/hostname":"node_name","kubernetes.io/os":"node_os","beta.kubernetes.io/instance-type":"node_instance_type","node.kubernetes.io/lifecycle":"node_lifecycle","failure-domain.beta.kubernetes.io/region":"node_region","failure-domain.beta.kubernetes.io/zone":"node_zone"}'
62	    - name: DD_LOGS_CONFIG_PROCESSING_RULES
63	      valueFrom:
64	        configMapKeyRef:
65	          name: rules-config
66	          key: logs
67	    - name: DD_APM_REPLACE_TAGS
68	      valueFrom:
69	        configMapKeyRef:
70	          name: rules-config
71	          key: apm
72	    - name: DD_CLOUD_PROVIDER_METADATA
73	      value: "aws"
74	    - name: CLUSTER_NAME
75	      value: "test-cluster"
76	    - name: DD_APM_PROBABILISTIC_SAMPLER_ENABLED
77	      value: true
78	    - name: DD_APM_PROBABILISTIC_SAMPLER_SAMPLING_PERCENTAGE
79	      value: 15
80	clusterAgent:
81	  env:
82	    - name: DD_ENV
83	      value: nonprod
84	    - name: DD_KUBERNETES_NODE_LABELS_AS_TAGS
85	      value: '{"kubernetes.io/hostname":"node_name","kubernetes.io/os":"node_os","beta.kubernetes.io/instance-type":"node_instance_type","node.kubernetes.io/lifecycle":"node_lifecycle","failure-domain.beta.kubernetes.io/region":"node_region","failure-domain.beta.kubernetes.io/zone":"node_zone"}'
86	    - name: DD_LOGS_CONFIG_PROCESSING_RULES
87	      valueFrom:
88	        configMapKeyRef:
89	          name: rules-config
90	          key: logs
91	    - name: DD_APM_REPLACE_TAGS
92	      valueFrom:
93	        configMapKeyRef:
94	          name: rules-config
95	          key: apm
96	    - name: DD_CLOUD_PROVIDER_METADATA
97	      value: "aws"
98	    - name: CLUSTER_NAME
99	      value: "test-cluster"
100	  confd:
101	  enabled: true
102	  image:
103	    tag: 7.55.0
104	  priorityClassName: cloud-ops-system
105	  tokenExistingSecret: datadog-clusteragent-token
106	  admissionController:
107	    enabled: true
108	    configMode: socket
109	agents:
110	  enabled: true
111	  image:
112	    tag: 7.55.0
113	  priorityClassName: cloud-ops-system
114	  tolerations:
115	    - operator: Exists
116	  useHostNetwork: true
117	  volumes:
118	    - name: journald
119	      hostPath:
120	        path: "/var/log/journal"
121	    - name: machineid
122	      hostPath:
123	        path: "/etc/machine-id"
124	  volumeMounts:
125	    - name: journald
126	      mountPath: "/var/log/journal"
127	      readOnly: true
128	    - name: machineid
129	      mountPath: "/etc/machine-id"
130	      readOnly: true
131


1	targetSystem: "linux"
2	registry: public.ecr.aws/datadog
3	datadog:
4	  operator:
5	    enabled: false
6	  apiKeyExistingSecret: datadog-api-key
7	  clusterName: test-cluster
8	  securityContext:
9	    seLinuxOptions:
10	      user: "system_u"
11	      role: "system_r"
12	      type: "spc_t"
13	      level: "s0"
14	  logLevel: ERROR
15	  containerExclude: ""
16	  containerExcludeLogs: ""
17	  logs:
18	    enabled: true
19	    containerCollectAll: true
20	    autoMultiLineDetection: true
21	  criSocketPath: /run/containerd/containerd.sock
22	  dogstatsd:
23	    port: 8125
24	    useHostPort: true
25	    nonLocalTraffic: true
26	  otlp:
27	    receiver:
28	      protocols:
29	        grpc:
30	          enabled: true
31	          endpoint: 0.0.0.0:4317
32	        http:
33	          enabled: true
34	          endpoint: 0.0.0.0:4318
35	  apm:
36	    enabled: true
37	    instrumentation:
38	      skipKPITelemetry: true
39	  processAgent:
40	    enabled: false
41	  networkMonitoring:
42	    enabled: false
43	  tags:
44	    - "global_segment:test"
45	    - "env:test"
46	  confd:
47	    journald_conf.yaml: |-
48	      logs:
49	        - type: journald
50	          path: /var/log/journal/
51	          start_position: beginning
52	          source: journald
53	          exclude_units:
54	            - kubelet.service
55	  env:
56	    - name: DD_AUTOCONFIG_INCLUDE_FEATURES
57	      value: "containerd"
58	    - name: DD_ENV
59	      value: nonprod
60	    - name: DD_KUBERNETES_NODE_LABELS_AS_TAGS
61	      value: '{"kubernetes.io/hostname":"node_name","kubernetes.io/os":"node_os","beta.kubernetes.io/instance-type":"node_instance_type","node.kubernetes.io/lifecycle":"node_lifecycle","failure-domain.beta.kubernetes.io/region":"node_region","failure-domain.beta.kubernetes.io/zone":"node_zone"}'
62	    - name: DD_LOGS_CONFIG_PROCESSING_RULES
63	      valueFrom:
64	        configMapKeyRef:
65	          name: rules-config
66	          key: logs
67	    - name: DD_APM_REPLACE_TAGS
68	      valueFrom:
69	        configMapKeyRef:
70	          name: rules-config
71	          key: apm
72	    - name: DD_CLOUD_PROVIDER_METADATA
73	      value: "aws"
74	    - name: CLUSTER_NAME
75	      value: "test-cluster"
76	    - name: DD_APM_PROBABILISTIC_SAMPLER_ENABLED
77	      value: true
78	    - name: DD_APM_PROBABILISTIC_SAMPLER_SAMPLING_PERCENTAGE
79	      value: 15
80	    - name: DD_OBSERVABILITY_PIPELINES_WORKER_LOGS_ENABLED
81	      value: "true"
82	    - name: DD_OBSERVABILITY_PIPELINES_WORKER_LOGS_URL
83	      value: "http://vector.vector.svc.cluster.local:8282"
84	clusterAgent:
85	  env:
86	    - name: DD_ENV
87	      value: nonprod
88	    - name: DD_KUBERNETES_NODE_LABELS_AS_TAGS
89	      value: '{"kubernetes.io/hostname":"node_name","kubernetes.io/os":"node_os","beta.kubernetes.io/instance-type":"node_instance_type","node.kubernetes.io/lifecycle":"node_lifecycle","failure-domain.beta.kubernetes.io/region":"node_region","failure-domain.beta.kubernetes.io/zone":"node_zone"}'
90	    - name: DD_LOGS_CONFIG_PROCESSING_RULES
91	      valueFrom:
92	        configMapKeyRef:
93	          name: rules-config
94	          key: logs
95	    - name: DD_APM_REPLACE_TAGS
96	      valueFrom:
97	        configMapKeyRef:
98	          name: rules-config
99	          key: apm
100	    - name: DD_CLOUD_PROVIDER_METADATA
101	      value: "aws"
102	    - name: CLUSTER_NAME
103	      value: "test-cluster"
104	  confd:
105	  enabled: true
106	  image:
107	    tag: 7.55.0
108	  priorityClassName: cloud-ops-system
109	  tokenExistingSecret: datadog-clusteragent-token
110	  admissionController:
111	    enabled: true
112	    configMode: socket
113	agents:
114	  enabled: true
115	  image:
116	    tag: 7.55.0
117	  priorityClassName: cloud-ops-system
118	  tolerations:
119	    - operator: Exists
120	  useHostNetwork: true
121	  volumes:
122	    - name: journald
123	      hostPath:
124	        path: "/var/log/journal"
125	    - name: machineid
126	      hostPath:
127	        path: "/etc/machine-id"
128	  volumeMounts:
129	    - name: journald
130	      mountPath: "/var/log/journal"
131	      readOnly: true
132	    - name: machineid
133	      mountPath: "/etc/machine-id"
134	      readOnly: true
135


1	# ---------------------------------------------------------------------------------------------------------------------
2	# METRICS SERVER
3	# ---------------------------------------------------------------------------------------------------------------------
4	locals {
5	  datadog_agent_enabled = (
6	    var.datadog_agent_enabled != null
7	    ? var.datadog_agent_enabled
8	    : lookup(var.datadog_agent_enabled_cluster_types, local.cluster_type, true)
9	  )
10	}
11
12	# Datadog Agent
13	module "datadog_agent" {
14	  source = "git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/monitoring/datadog-agent?ref=v1.2.0"
15	  count  = local.datadog_agent_enabled ? 1 : 0
16
17	  providers = {
18	    kubernetes = kubernetes.local-cluster
19	    helm       = helm.local-cluster
20	  }
21
22	  chart_version                  = var.datadog_agent_version
23	  image_tag                      = var.datadog_agent_image_tag
24	  create_namespace               = var.datadog_create_namespace
25	  cluster_name                   = var.cluster_name
26	  global_segment                 = var.global_segment
27	  datadog_tags                   = var.datadog_tags
28	  enable_nowrds_monitoring       = var.datadog_enable_nowrds_monitoring
29	  enable_now_obfusicator_options = var.datadog_enable_now_obfusicator_options
30	  enable_mrds_monitoring         = var.datadog_enable_mrds_monitoring
31	  now_obfusicator_options        = var.datadog_now_obfusicator_options
32	  tags                           = var.tags
33	  env                            = var.env
34
35	  logs_intake_url = local.vector_dd_agent_logs_enabled ? module.vector[0].service_endpoint_dd_agent : null
36	}
37
38	# ---------------------------------------------------------------------------------------------------------------------
39	# REQUIRED PARAMETERS
40	# These variables must be set.
41	# ---------------------------------------------------------------------------------------------------------------------
42	variable "datadog_agent_version" {
43	  description = "Version of otel-collector helm chart"
44	  type        = string
45	  default     = ""
46	  validation {
47	    condition     = var.datadog_agent_version == "" && local.datadog_agent_enabled ? false : true
48	    error_message = "datadog_agent_version must be set when datadog_agent is enabled"
49	  }
50	}
51
52	variable "datadog_agent_image_tag" {
53	  description = "Version of otel-collector image tag"
54	  type        = string
55	  default     = ""
56	  validation {
57	    condition     = var.datadog_agent_image_tag == "" && local.datadog_agent_enabled ? false : true
58	    error_message = "datadog_agent_image_tag must be set when datadog_agent is enabled"
59	  }
60	}
61
62	# ---------------------------------------------------------------------------------------------------------------------
63	# OPTIONAL PARAMETERS
64	# These variables are configurable, but have reasonable defaults set.
65	# ---------------------------------------------------------------------------------------------------------------------
66	variable "datadog_agent_enabled_cluster_types" {
67	  description = "Types of clusters datadog_agent should be enabled on by default"
68	  type        = map(string)
69	  default = {
70	    "mgmt"  = true
71	    "cps"   = true
72	    "saas"  = true
73	    "infra" = false
74	    "dev"   = false
75	  }
76	}
77
78	variable "datadog_tags" {
79	  description = "Map of tags to apply to datadog metrics"
80	  type        = list(string)
81	  default     = []
82	}
83
84	variable "datadog_enable_nowrds_monitoring" {
85	  description = "Enable monitoring for NOWRDS."
86	  type        = bool
87	  default     = false
88	}
89
90	variable "datadog_enable_mrds_monitoring" {
91	  description = "Enable multitenant RDS monitoring"
92	  type        = bool
93	  default     = false
94	}
95
96	variable "datadog_enable_now_obfusicator_options" {
97	  description = "Whether to enable the NOWRDS obfuscator options in the Datadog agent configuration."
98	  type        = bool
99	  default     = false
100	}
101
102	variable "datadog_now_obfusicator_options" {
103	  description = "A map of options to configure the NOWRDS obfuscator in the Datadog agent configuration."
104	  type        = map(string)
105	  default = {
106	    replace_digits = "true"
107	  }
108	}
109
110	variable "datadog_create_namespace" {
111	  type    = bool
112	  default = false
113	}
114
115	variable "datadog_agent_enabled" {
116	  description = "Override for explicitly enabling or disabling otel-collector"
117	  type        = bool
118	  default     = null
119	}
120
121	# ---------------------------------------------------------------------------------------------------------------------
122	# DD AGENT → VECTOR LOGS ROUTING (OPW)
123	# ---------------------------------------------------------------------------------------------------------------------
124	locals {
125	  vector_dd_agent_logs_enabled = (
126	    var.vector_dd_agent_logs_enabled != null
127	    ? var.vector_dd_agent_logs_enabled
128	    : local.vector_enabled
129	  )
130	}
131
132	variable "vector_dd_agent_logs_enabled" {
133	  description = "Override for routing DD Agent logs through Vector. When null (default), inherits from local.vector_enabled (i.e., on wherever Vector is on). Set to false to keep DD Agent shipping logs directly to DD SaaS even when Vector is deployed."
134	  type        = bool
135	  default     = null
136	}
137
138	# Plan-time guard: vector_dd_agent_logs_enabled cannot be true if vector itself is off.
139	# Mirrors the existing data "external" "vector_validate" pattern in vector.tf.
140	data "external" "vector_dd_agent_logs_validate" {
141	  count = local.vector_dd_agent_logs_enabled && !local.vector_enabled ? 1 : 0
142
143	  program = ["Error: vector_dd_agent_logs_enabled requires vector_enabled"]
144	}
145


1	# ---------------------------------------------------------------------------------------------------------------------
2	# VECTOR
3	# ---------------------------------------------------------------------------------------------------------------------
4	locals {
5	  vector_enabled = (
6	    var.vector_enabled != null
7	    ? var.vector_enabled
8	    : lookup(var.vector_enabled_cluster_types, local.cluster_type, true)
9	  )
10	  vector_endpoint = "http://vector.vector.svc.cluster.local:4317"
11	}
12
13	module "vector" {
14	  source = "git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/monitoring/vector.git?ref=v1.1.0"
15	  count  = local.vector_enabled ? 1 : 0
16
17	  providers = {
18	    kubernetes = kubernetes.local-cluster
19	    helm       = helm.local-cluster
20	  }
21
22	  chart_version               = var.vector_chart_version
23	  image                       = var.vector_image
24	  image_tag_version           = var.vector_image_tag
25	  replicas                    = var.vector_replicas
26	  datadog_site                = var.vector_datadog_site
27	  datadog_api_key_vault_path  = var.vector_datadog_api_key_vault_path
28	  audit_drop_patterns         = var.vector_audit_drop_patterns
29	  email_redaction_enabled     = var.vector_email_redaction_enabled
30	  email_redaction_replacement = var.vector_email_redaction_replacement
31	  additional_config           = var.vector_additional_config
32
33	  # NOTE: The external-secrets internal-module (v1.2.0) has an empty outputs.tf
34	  # and does not expose cluster_secret_store_name. Until that output is added
35	  # to the external-secrets module, the ClusterSecretStore name is passed
36	  # explicitly via var.vector_external_secrets_cluster_store_name.
37	  # TODO: replace with module.external_secrets[0].cluster_secret_store_name
38	  # once the external-secrets internal-module exposes that output.
39	  external_secrets_cluster_store_name = var.vector_external_secrets_cluster_store_name
40
41	  depends_on = [
42	    module.cilium,
43	    module.external_secrets,
44	  ]
45	}
46
47	data "external" "vector_validate" {
48	  count = local.vector_enabled && !local.external_secrets_enabled ? 1 : 0
49
50	  program = ["Error: vector can only be set if external-secrets is enabled as well"]
51	}
52
53	# ---------------------------------------------------------------------------------------------------------------------
54	# REQUIRED PARAMETERS
55	# These variables must be set.
56	# ---------------------------------------------------------------------------------------------------------------------
57	variable "vector_chart_version" {
58	  description = "Version of vector helm chart"
59	  type        = string
60	  default     = ""
61	  validation {
62	    condition     = var.vector_chart_version == "" && local.vector_enabled ? false : true
63	    error_message = "vector_chart_version must be set when vector is enabled"
64	  }
65	}
66
67	variable "vector_image_tag" {
68	  description = "Vector image tag"
69	  type        = string
70	  default     = ""
71	  validation {
72	    condition     = var.vector_image_tag == "" && local.vector_enabled ? false : true
73	    error_message = "vector_image_tag must be set when vector is enabled"
74	  }
75	}
76
77	variable "vector_datadog_site" {
78	  description = "Datadog site for vector sinks (e.g. datadoghq.com)"
79	  type        = string
80	  default     = "datadoghq.com"
81	  validation {
82	    condition     = var.vector_datadog_site == "" && local.vector_enabled ? false : true
83	    error_message = "vector_datadog_site must be set when vector is enabled"
84	  }
85	}
86
87	variable "vector_datadog_api_key_vault_path" {
88	  description = "Vault path for the Datadog API key (consumed by ExternalSecret in vector ns)"
89	  type        = string
90	  default     = "secret/data/saas/infrastructure/datadog-api-key"
91	  validation {
92	    condition     = var.vector_datadog_api_key_vault_path == "" && local.vector_enabled ? false : true
93	    error_message = "vector_datadog_api_key_vault_path must be set when vector is enabled"
94	  }
95	}
96
97	variable "vector_external_secrets_cluster_store_name" {
98	  description = "Name of the ClusterSecretStore to use for vector ExternalSecret resources. Pass the name created by the external-secrets module (typically 'vault-cluster-store'). Required when vector is enabled."
99	  type        = string
100	  default     = "vault"
101	  validation {
102	    condition     = var.vector_external_secrets_cluster_store_name == "" && local.vector_enabled ? false : true
103	    error_message = "vector_external_secrets_cluster_store_name must be set when vector is enabled"
104	  }
105	}
106
107	# ---------------------------------------------------------------------------------------------------------------------
108	# OPTIONAL PARAMETERS
109	# These variables are configurable, but have reasonable defaults set.
110	# ---------------------------------------------------------------------------------------------------------------------
111	variable "vector_enabled_cluster_types" {
112	  description = "Types of clusters Vector should be enabled on by default"
113	  type        = map(string)
114	  default = {
115	    "mgmt"  = true
116	    "cps"   = true
117	    "saas"  = true
118	    "infra" = false
119	    "dev"   = false
120	  }
121	}
122
123	variable "vector_enabled" {
124	  description = "Override for explicitly enabling or disabling Vector"
125	  type        = bool
126	  default     = null
127	}
128
129	variable "vector_image" {
130	  description = "Image for Vector"
131	  type        = string
132	  default     = "registry.ic-ops.net/cache-docker-hub/timberio/vector"
133	}
134
135	variable "vector_replicas" {
136	  description = "Number of Vector aggregator replicas"
137	  type        = number
138	  default     = 3
139	}
140
141	variable "vector_audit_drop_patterns" {
142	  description = "Regex patterns; matching log message bodies are dropped"
143	  type        = list(string)
144	  default = [
145	    "Audit\\s-\\s.+",
146	    "\"level\":\"audit\"",
147	  ]
148	}
149
150	variable "vector_email_redaction_enabled" {
151	  description = "Toggle the email-redact VRL transform across all signals"
152	  type        = bool
153	  default     = true
154	}
155
156	variable "vector_email_redaction_replacement" {
157	  description = "String that replaces matched email addresses"
158	  type        = string
159	  default     = "[REDACTED-EMAIL]"
160	}
161
162	variable "vector_additional_config" {
163	  description = "Alternate helm chart customConfig (overwrites entire helm config)"
164	  type        = any
165	  default     = null
166	}
167


Now I need to look at consumer README and check the consumer plan-time guard's usability.


Bash

grep -n "vector_dd_agent_logs_enabled\|vector_validate\|DD Agent" /Users/steve.briggs/projects/IAC/terraform/iac-library/aws/compute/eks-cluster-applications/README.md | head -30

Find related lines in consumer README


54:| `vector_dd_agent_logs_enabled` | Override for routing DD Agent logs through Vector. When null, inherits from Vector's enabled state. Set to false to keep DD Agent shipping logs directly to DD SaaS even when Vector is deployed. | `bool` | `null` | no |
57:### DD Agent → Vector log routing (OPW)
59:When `vector_dd_agent_logs_enabled` is true (defaults to following `vector_enabled`), the Datadog Agent ships container logs to Vector via the OPW HTTP intake on `vector.vector.svc.cluster.local:8282`. Logs traverse two filtering layers before reaching Datadog SaaS:
64:Metrics and APM (traces) are unaffected and continue their existing OTel-based path through Vector. To roll back logs to direct DD shipping for a single cluster while keeping Vector traces, set `vector_dd_agent_logs_enabled = false` on that cluster — Vector continues serving OTel traffic, agent re-rolls back to direct logs.
393:|  [vector\_dd\_agent\_logs\_enabled](#input\_vector\_dd\_agent\_logs\_enabled) | Override for routing DD Agent logs through Vector. When null (default), inherits from local.vector\_enabled (i.e., on wherever Vector is on). Set to false to keep DD Agent shipping logs directly to DD SaaS even when Vector is deployed. | `bool` | `null` | no |


grep -n "vector_enabled\|vector_email\|vector_chart\|input_vector" /Users/steve.briggs/projects/IAC/terraform/iac-library/aws/compute/eks-cluster-applications/README.md | head -40

Look at vector-related rows in the README


7:When `vector_enabled = true`:
15:When `vector_enabled = false` (or unset and the cluster type defaults to disabled):
25:The five required variables must be set together with `vector_enabled = true`. The remaining variables have defaults that are suitable for most clusters.
28:vector_enabled                             = true
29:vector_chart_version                       = "0.36.0"
42:| `vector_chart_version` | Vector helm chart version pin | `string` | `""` | yes |
47:| `vector_enabled` | Override for explicitly enabling or disabling Vector | `bool` | `null` | no |
48:| `vector_enabled_cluster_types` | Per-cluster-type defaults (`mgmt/cps/saas=true`, `infra/dev=false`) | `map(bool)` | see below | no |
52:| `vector_email_redaction_enabled` | Toggle the email-redact VRL transform across all three signals | `bool` | `true` | no |
53:| `vector_email_redaction_replacement` | Replacement string for redacted email addresses | `string` | `[REDACTED-EMAIL]` | no |
59:When `vector_dd_agent_logs_enabled` is true (defaults to following `vector_enabled`), the Datadog Agent ships container logs to Vector via the OPW HTTP intake on `vector.vector.svc.cluster.local:8282`. Logs traverse two filtering layers before reaching Datadog SaaS:
389:|  [vector\_additional\_config](#input\_vector\_additional\_config) | Alternate helm chart customConfig for Vector (overwrites entire helm config, does not merge) | `any` | `null` | no |
390:|  [vector\_audit\_drop\_patterns](#input\_vector\_audit\_drop\_patterns) | Regex patterns; log message bodies matching any pattern are dropped before Datadog | `list(string)` |
[
  "Audit\\s-\\s.+",
  "\"level\":\"audit\""
]
 | no |
391:|  [vector\_chart\_version](#input\_vector\_chart\_version) | Version of Vector helm chart | `string` | `""` | no |
392:|  [vector\_datadog\_api\_key\_vault\_path](#input\_vector\_datadog\_api\_key\_vault\_path) | Vault path for the Datadog API key consumed by Vector's ExternalSecret | `string` | `""` | no |
393:|  [vector\_dd\_agent\_logs\_enabled](#input\_vector\_dd\_agent\_logs\_enabled) | Override for routing DD Agent logs through Vector. When null (default), inherits from local.vector\_enabled (i.e., on wherever Vector is on). Set to false to keep DD Agent shipping logs directly to DD SaaS even when Vector is deployed. | `bool` | `null` | no |
394:|  [vector\_datadog\_site](#input\_vector\_datadog\_site) | Datadog site for Vector sinks (e.g. `datadoghq.com`) | `string` | `""` | no |
395:|  [vector\_email\_redaction\_enabled](#input\_vector\_email\_redaction\_enabled) | Toggle the email-redact VRL transform across logs, traces, and metrics | `bool` | `true` | no |
396:|  [vector\_email\_redaction\_replacement](#input\_vector\_email\_redaction\_replacement) | String that replaces matched email addresses | `string` | `"[REDACTED-EMAIL]"` | no |
397:|  [vector\_enabled](#input\_vector\_enabled) | Override for explicitly enabling or disabling Vector | `bool` | `null` | no |
398:|  [vector\_enabled\_cluster\_types](#input\_vector\_enabled\_cluster\_types) | Types of clusters Vector should be enabled on by default | `map(string)` |
{
  "cps": true,
  "dev": false,
  "infra": false,
  "mgmt": true,
  "saas": true
}
 | no |
399:|  [vector\_external\_secrets\_cluster\_store\_name](#input\_vector\_external\_secrets\_cluster\_store\_name) | Name of the ClusterSecretStore (created by the external-secrets module) used by Vector's ExternalSecret. Required when Vector is enabled. | `string` | `""` | no |
400:|  [vector\_image](#input\_vector\_image) | Image for the Vector aggregator | `string` | `"registry.ic-ops.net/cache-docker-hub/timberio/vector"` | no |
401:|  [vector\_image\_tag](#input\_vector\_image\_tag) | Vector image tag | `string` | `""` | no |
402:|  [vector\_replicas](#input\_vector\_replicas) | Number of Vector aggregator replicas | `number` | `3` | no |


Count vector_dd_agent_logs_enabled references in consumer README


40	| Variable | Description | Type | Default | Required when enabled |
41	|---|---|---|---|---|
42	| `vector_chart_version` | Vector helm chart version pin | `string` | `""` | yes |
43	| `vector_image_tag` | Vector image tag | `string` | `""` | yes |
44	| `vector_datadog_site` | Datadog site for Vector sinks (e.g. `datadoghq.com`) | `string` | `""` | yes |
45	| `vector_datadog_api_key_vault_path` | Vault path for the Datadog API key consumed by Vector's ExternalSecret | `string` | `""` | yes |
46	| `vector_external_secrets_cluster_store_name` | Name of the ClusterSecretStore created by the external-secrets module | `string` | `""` | yes |
47	| `vector_enabled` | Override for explicitly enabling or disabling Vector | `bool` | `null` | no |
48	| `vector_enabled_cluster_types` | Per-cluster-type defaults (`mgmt/cps/saas=true`, `infra/dev=false`) | `map(bool)` | see below | no |
49	| `vector_image` | Vector container image (registry/path) | `string` | `registry.ic-ops.net/cache-docker-hub/timberio/vector` | no |
50	| `vector_replicas` | Number of Vector aggregator replicas | `number` | `3` | no |
51	| `vector_audit_drop_patterns` | Regex patterns; matching log message bodies are dropped before Datadog | `list(string)` | Immuta audit patterns | no |
52	| `vector_email_redaction_enabled` | Toggle the email-redact VRL transform across all three signals | `bool` | `true` | no |
53	| `vector_email_redaction_replacement` | Replacement string for redacted email addresses | `string` | `[REDACTED-EMAIL]` | no |
54	| `vector_dd_agent_logs_enabled` | Override for routing DD Agent logs through Vector. When null, inherits from Vector's enabled state. Set to false to keep DD Agent shipping logs directly to DD SaaS even when Vector is deployed. | `bool` | `null` | no |
55	| `vector_additional_config` | Escape-hatch: overwrites the entire Vector `customConfig` helm value | `any` | `null` | no |
56
57	### DD Agent → Vector log routing (OPW)
58
59	When `vector_dd_agent_logs_enabled` is true (defaults to following `vector_enabled`), the Datadog Agent ships container logs to Vector via the OPW HTTP intake on `vector.vector.svc.cluster.local:8282`. Logs traverse two filtering layers before reaching Datadog SaaS:
60
61	1. **Agent-side** — `DD_LOGS_CONFIG_PROCESSING_RULES` (see `datadog-agent/processing-rules/{env}/rules.json`) drops audit lines before transmission. Authoritative for audit drops.
62	2. **Vector** — `drop_audit` regex filter (defense-in-depth, same patterns) + `redact_emails_logs` substitutes email addresses with `[REDACTED-EMAIL]`.
63
64	Metrics and APM (traces) are unaffected and continue their existing OTel-based path through Vector. To roll back logs to direct DD shipping for a single cluster while keeping Vector traces, set `vector_dd_agent_logs_enabled = false` on that cluster — Vector continues serving OTel traffic, agent re-rolls back to direct logs.
65
66	This requires `datadog-agent` module v1.2.0+ and `vector` module v1.1.0+.
67
68	---
69
70	## Requirements
71
72	| Name | Version |
73	|------|---------|
74	|  [terraform](#requirement\_terraform) | >= 1.9.0 |
75	|  [aws](#requirement\_aws) | ~> 6.0 |
76
77	## Providers
78
79	| Name | Version |
80	|------|---------|
81	|  [aws](#provider\_aws) | ~> 6.0 |
82	|  [aws.super\_certificate\_zone](#provider\_aws.super\_certificate\_zone) | ~> 6.0 |
83	|  [external](#provider\_external) | n/a |
84	|  [kubernetes.local-cluster](#provider\_kubernetes.local-cluster) | n/a |
85	|  [random](#provider\_random) | n/a |
86	|  [vault](#provider\_vault) | n/a |
87
88	## Modules
89
90	| Name | Source | Version |
91	|------|--------|---------|
92	|  [addon-netpol](#module\_addon-netpol) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/security/network-policy.git | v1.1.0 |
93	|  [application\_deployment](#module\_application\_deployment) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/compute/application-deployment.git | v2.1.0 |
94	|  [argocd](#module\_argocd) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/compute/argocd.git | v1.4.1 |
95	|  [aws\_load\_balancer\_controller](#module\_aws\_load\_balancer\_controller) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/networking/aws-load-balancer-controller.git | v1.0.1 |
96	|  [aws\_load\_balancer\_controller\_sa](#module\_aws\_load\_balancer\_controller\_sa) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/aws/security/kubernetes-iam-serviceaccount.git | v7.0.0 |
97	|  [cert\_manager](#module\_cert\_manager) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/security/cert-manager.git | v1.4.0 |
98	|  [cert\_manager\_sa](#module\_cert\_manager\_sa) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/aws/security/kubernetes-iam-serviceaccount.git | v7.0.0 |
99	|  [cert\_manager\_super\_account](#module\_cert\_manager\_super\_account) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/aws/security/cross-account-iam-role.git | v3.0.0 |
100	|  [cilium](#module\_cilium) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/networking/cilium.git | v5.3.0 |
101	|  [cluster\_autoscaler](#module\_cluster\_autoscaler) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/compute/cluster-autoscaler.git | v3.0.0 |
102	|  [cluster\_autoscaler\_sa](#module\_cluster\_autoscaler\_sa) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/aws/security/kubernetes-iam-serviceaccount.git | v7.0.0 |
103	|  [cluster\_issuers](#module\_cluster\_issuers) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/security/cluster-issuers.git | v4.0.0 |
104	|  [cnpg\_operator\_deployment](#module\_cnpg\_operator\_deployment) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/compute/cnpg-operator.git | v1.0.1 |
105	|  [coredns](#module\_coredns) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/networking/coredns.git | v1.2.2 |
106	|  [datadog\_agent](#module\_datadog\_agent) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/monitoring/datadog-agent | v1.1.0 |
107	|  [ebs\_csi\_sa](#module\_ebs\_csi\_sa) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/aws/security/kubernetes-iam-serviceaccount.git | v7.0.0 |
108	|  [external\_dns\_private](#module\_external\_dns\_private) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/networking/external-dns.git | v1.3.2 |
109	|  [external\_dns\_private\_sa](#module\_external\_dns\_private\_sa) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/aws/security/kubernetes-iam-serviceaccount.git | v7.0.0 |
110	|  [external\_dns\_public](#module\_external\_dns\_public) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/networking/external-dns.git | v1.3.2 |
111	|  [external\_dns\_public\_sa](#module\_external\_dns\_public\_sa) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/aws/security/kubernetes-iam-serviceaccount.git | v7.0.0 |
112	|  [external\_networking](#module\_external\_networking) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/networking/external-networking.git | v5.1.0 |
113	|  [external\_secrets](#module\_external\_secrets) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/security/external-secrets.git | v1.2.0 |
114	|  [fluxcd](#module\_fluxcd) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/compute/fluxcd.git | v1.2.0 |


The auto-generated module table on line 106 shows `datadog_agent` at v1.1.0 — let me see if it was bumped to v1.2.0 or v1.1.2 → v1.2.0. The plan/spec says current was v1.1.2, but the README shows v1.1.0. Let me check more carefully and look at the old SHAs.


grep -n "module_datadog_agent\|module_vector " /Users/steve.briggs/projects/IAC/terraform/iac-library/aws/compute/eks-cluster-applications/README.md | head -10

Find module version refs in README


106:|  [datadog\_agent](#module\_datadog\_agent) | git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/monitoring/datadog-agent | v1.1.0 |


The auto-generated module table shows `datadog_agent` at `v1.1.0` (not the current v1.2.0 in the .tf file) and there's no row for `module_vector`. This auto-generated section is stale. Let me check if there's any vector module reference and look at git history to see what the prior version was.


grep -n "vector " /Users/steve.briggs/projects/IAC/terraform/iac-library/aws/compute/eks-cluster-applications/README.md | head -20

Check for vector module entries in README


10:apps → otel-lb → otel-collector → vector → Datadog


git log --oneline -5 datadog_agent.tf 2>/dev/null

Recent commits for datadog_agent.tf


9f456c5 fix: adding flux netpols for nonprod and fixing datadog sampling
11855c8 fix: dropping DD agent sampling percentage back to 15
9b0155d Update Sysdig with logging and replica settings, linting
adf833f Change now-obfuscator options to be a map instead of a list
5d7db3e Edit datadog_agent.tf


git diff HEAD -- datadog_agent.tf vector.tf 2>/dev/null | head -150

Diff the two module files vs HEAD


diff --git a/datadog_agent.tf b/datadog_agent.tf
index e64ff94..100cbc3 100644
--- a/datadog_agent.tf
+++ b/datadog_agent.tf
@@ -11,7 +11,7 @@ locals {

 # Datadog Agent
 module "datadog_agent" {
-  source = "git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/monitoring/datadog-agent?ref=v1.1.2"
+  source = "git@gitlab.infrastructure.immuta.io:infrastructure/terraform/internal-modules/kubernetes/monitoring/datadog-agent?ref=v1.2.0"
   count  = local.datadog_agent_enabled ? 1 : 0

   providers = {
@@ -31,6 +31,8 @@ module "datadog_agent" {
   now_obfusicator_options        = var.datadog_now_obfusicator_options
   tags                           = var.tags
   env                            = var.env
+
+  logs_intake_url = local.vector_dd_agent_logs_enabled ? module.vector[0].service_endpoint_dd_agent : null
 }

 # ---------------------------------------------------------------------------------------------------------------------
@@ -115,3 +117,28 @@ variable "datadog_agent_enabled" {
   type        = bool
   default     = null
 }
+
+# ---------------------------------------------------------------------------------------------------------------------
+# DD AGENT → VECTOR LOGS ROUTING (OPW)
+# ---------------------------------------------------------------------------------------------------------------------
+locals {
+  vector_dd_agent_logs_enabled = (
+    var.vector_dd_agent_logs_enabled != null
+    ? var.vector_dd_agent_logs_enabled
+    : local.vector_enabled
+  )
+}
+
+variable "vector_dd_agent_logs_enabled" {
+  description = "Override for routing DD Agent logs through Vector. When null (default), inherits from local.vector_enabled (i.e., on wherever Vector is on). Set to false to keep DD Agent shipping logs directly to DD SaaS even when Vector is deployed."
+  type        = bool
+  default     = null
+}
+
+# Plan-time guard: vector_dd_agent_logs_enabled cannot be true if vector itself is off.
+# Mirrors the existing data "external" "vector_validate" pattern in vector.tf.
+data "external" "vector_dd_agent_logs_validate" {
+  count = local.vector_dd_agent_logs_enabled && !local.vector_enabled ? 1 : 0
+
+  program = ["Error: vector_dd_agent_logs_enabled requires vector_enabled"]
+}


git -C /Users/steve.briggs/projects/IAC/terraform/iac-library/aws/compute/eks-cluster-applications diff HEAD -- vector.tf | head -80

Diff vector.tf in consumer


git -C /Users/steve.briggs/projects/IAC/terraform/internal-modules/kubernetes/monitoring/datadog-agent log --oneline -10 2>/dev/null

DD agent module recent commits


git -C /Users/steve.briggs/projects/IAC/terraform/internal-modules/kubernetes/monitoring/datadog-agent status 2>/dev/null

DD agent module git status


(Bash completed with no output)


92c56b0 Merge branch 'datadog-agent-values' into 'master'
4cd9416 Fix missing containerd socket values
acb8239 fix: updating correct values
693771f Merge branch 'haris/fix-sampler-rate' into 'master'
c54c7b6 fix: dropping sampling percentage back to 15
408c6c8 Merge branch 'ccunning/move-now-obfuscator-map' into 'master'
fed0dd2 Change now-obfuscator options to be a map instead of a list
bfa915a Edit variables.tf
39d37d7 datadog
3649d97 Merge branch 'updates' into 'master'


On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   .idea/terraform.xml
	modified:   README.md
	modified:   main.tf
	modified:   values.yaml.tftpl
	modified:   variables.tf

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	AGENTS.md
	CLAUDE.MD
	MEMORIES.md
	tests/

no changes added to commit (use "git add" and/or "git commit -a")


git -C /Users/steve.briggs/projects/IAC/terraform/iac-library/aws/compute/eks-cluster-applications diff HEAD -- vector.tf 2>&1

Diff vector.tf in consumer (retry)


git -C /Users/steve.briggs/projects/IAC/terraform/internal-modules/kubernetes/monitoring/datadog-agent diff HEAD -- variables.tf main.tf values.yaml.tftpl 2>&1 | head -200

Diff DD agent module changes


diff --git a/main.tf b/main.tf
index 709ab67..3a3f8d2 100644
--- a/main.tf
+++ b/main.tf
@@ -53,7 +53,7 @@ resource "kubernetes_namespace" "this" {

 resource "kubernetes_config_map" "datadog_processing_rules" {
   metadata {
-    name = "rules-config"
+    name      = "rules-config"
     namespace = var.create_namespace ? kubernetes_namespace.this[0].metadata[0].name : var.namespace
   }
   data = {
@@ -186,7 +186,7 @@ resource "kubernetes_manifest" "external-secret" {
             property = "rds-instance"
           }
         },
-      ] : [], var.enable_nowrds_monitoring ? [
+        ] : [], var.enable_nowrds_monitoring ? [
         {
           secretKey = "NOWRDS_HOST"
           remoteRef = {
@@ -231,7 +231,7 @@ resource "kubernetes_manifest" "external-secret" {
 # DEPLOY A HELM CHART
 # ---------------------------------------------------------------------------------------------------------------------
 resource "helm_release" "this" {
-  name      = var.name
+  name = var.name
   #namespace = kubernetes_namespace.this.metadata[0].name
   namespace = var.create_namespace ? kubernetes_namespace.this[0].metadata[0].name : var.namespace
   wait      = false # Set to true if you want Terraform to wait for resources to be ready
@@ -241,27 +241,29 @@ resource "helm_release" "this" {
   version    = var.chart_version
   values = [
     templatefile("${path.module}/values.yaml.tftpl", {
-      cluster_name   = var.cluster_name
-      env            = var.env == "staging" ? "nonprod" : var.env
-      global_segment = var.global_segment
-      image_tag      = var.image_tag
-      watch_namespace          = var.create_namespace ? kubernetes_namespace.this[0].metadata[0].name : var.namespace
-      tags                     = concat(["global_segment:${var.global_segment}"], var.datadog_tags)
+      cluster_name               = var.cluster_name
+      env                        = var.env == "staging" ? "nonprod" : var.env
+      global_segment             = var.global_segment
+      image_tag                  = var.image_tag
+      watch_namespace            = var.create_namespace ? kubernetes_namespace.this[0].metadata[0].name : var.namespace
+      tags                       = concat(["global_segment:${var.global_segment}"], var.datadog_tags)
       agents_priority_class_name = var.agents_priority_class_name

       apm_enabled = var.apm_enabled

-      container_exclude = var.container_exclude
+      container_exclude      = var.container_exclude
       container_exclude_logs = var.container_exclude_logs

-      enable_mrds_monitoring   = var.enable_mrds_monitoring
-      enable_nowrds_monitoring = var.enable_nowrds_monitoring
+      enable_mrds_monitoring         = var.enable_mrds_monitoring
+      enable_nowrds_monitoring       = var.enable_nowrds_monitoring
       enable_now_obfusicator_options = var.enable_now_obfusicator_options
-      now_obfusicator_options = var.now_obfusicator_options
+      now_obfusicator_options        = var.now_obfusicator_options

-      datadog_api_key_secret_name = var.datadog_api_key_secret_name
+      datadog_api_key_secret_name     = var.datadog_api_key_secret_name
       cluster_agent_token_secret_name = var.cluster_agent_token_secret_name
-      log_level = var.log_level
+      log_level                       = var.log_level
+
+      logs_intake_url = var.logs_intake_url
     })
   ]

diff --git a/values.yaml.tftpl b/values.yaml.tftpl
index a378aa6..5be2cad 100644
--- a/values.yaml.tftpl
+++ b/values.yaml.tftpl
@@ -79,6 +79,12 @@ datadog:
       value: true
     - name: DD_APM_PROBABILISTIC_SAMPLER_SAMPLING_PERCENTAGE
       value: 15
+%{ if logs_intake_url != null ~}
+    - name: DD_OBSERVABILITY_PIPELINES_WORKER_LOGS_ENABLED
+      value: "true"
+    - name: DD_OBSERVABILITY_PIPELINES_WORKER_LOGS_URL
+      value: "${logs_intake_url}"
+%{ endif ~}
 clusterAgent:
   env:
     - name: DD_ENV
diff --git a/variables.tf b/variables.tf
index 338ec7f..430e203 100644
--- a/variables.tf
+++ b/variables.tf
@@ -22,8 +22,8 @@ variable "chart_version" {
 }

 variable "env" {
-    description = "The environment that resources are deployed to. i.e. nonprod, prod"
-    type        = string
+  description = "The environment that resources are deployed to. i.e. nonprod, prod"
+  type        = string
 }

 variable "global_segment" {
@@ -32,8 +32,8 @@ variable "global_segment" {
 }

 variable "image_tag" {
-    description = "The tag of the Datadog agent image to use."
-    type        = string
+  description = "The tag of the Datadog agent image to use."
+  type        = string
 }

 variable "cluster_name" {
@@ -42,23 +42,23 @@ variable "cluster_name" {
 }

 variable "datadog_tags" {
-    description = "A map of global tags to apply to all metrics, traces, and logs."
-    type        = list(string)
-    default     = []
+  description = "A map of global tags to apply to all metrics, traces, and logs."
+  type        = list(string)
+  default     = []
 }

 variable "enable_now_obfusicator_options" {
-    description = "Whether to enable the NOWRDS obfuscator options in the Datadog agent configuration."
-    type        = bool
-    default     = false
+  description = "Whether to enable the NOWRDS obfuscator options in the Datadog agent configuration."
+  type        = bool
+  default     = false
 }

 variable "now_obfusicator_options" {
-    description = "A map of options to configure the NOWRDS obfuscator in the Datadog agent configuration."
-    type        = map(string)
-    default     = {
-      replace_digits = "true"
-    }
+  description = "A map of options to configure the NOWRDS obfuscator in the Datadog agent configuration."
+  type        = map(string)
+  default = {
+    replace_digits = "true"
+  }
 }

 variable "datadog_api_key_secret_name" {
@@ -74,27 +74,27 @@ variable "cluster_agent_token_secret_name" {
 }

 variable "log_level" {
-    description = "The log level for the Datadog agent."
-    type        = string
-    default     = "ERROR"
+  description = "The log level for the Datadog agent."
+  type        = string
+  default     = "ERROR"
 }

 variable "apm_enabled" {
-    description = "Whether to enable APM for the Datadog agent."
-    type        = bool
-    default     = true
+  description = "Whether to enable APM for the Datadog agent."
+  type        = bool
+  default     = true
 }

 variable "container_exclude" {
-    description = "A list of container names to exclude from monitoring."
-    type        = string
-    default     = "kube_namespace:datadog name:enterprise"
+  description = "A list of container names to exclude from monitoring."
+  type        = string
+  default     = "kube_namespace:datadog name:enterprise"
 }

 variable "container_exclude_logs" {
-    description = "A list of container names to exclude from log collection."
-    type        = string
-    default     = "image:registry.k8s.io/autoscaling/cluster-autoscaler image:registry.ic-ops.net/cache-k8s-registry/autoscaling/cluster-autoscaler"
+  description = "A list of container names to exclude from log collection."
+  type        = string
+  default     = "image:registry.k8s.io/autoscaling/cluster-autoscaler image:registry.ic-ops.net/cache-k8s-registry/autoscaling/cluster-autoscaler"
 }

 variable "tags" {
@@ -120,3 +120,14 @@ variable "enable_mrds_monitoring" {
   type        = bool
   default     = false
 }
+
+variable "logs_intake_url" {
+  description = "OPW-compatible logs intake URL. When set, the agent ships logs to this endpoint via DD_OBSERVABILITY_PIPELINES_WORKER_LOGS_URL. For cluster-internal Vector use module.vector.service_endpoint_dd_agent. When null (default), logs ship direct to Datadog SaaS."
+  type        = string
+  default     = null


Important finding: the DD Agent module changes include a LOT of incidental whitespace re-formatting done by `terraform fmt`. The implementer ran `terraform fmt` and it touched many lines. That's fine — but it's worth noting. Let me see the vector.tf consumer change.


git diff HEAD -- vector.tf 2>&1 | head -50

Diff consumer vector.tf


Consumer git status


 M README.md
 M datadog_agent.tf
 M otel_collector.tf
 M sysdig_agent.tf
?? .claude/
?? .terraform.lock.hcl
?? vector.tf


The git status reported in the user's environment block was slightly different — `vector.tf` is shown here as untracked (`??`). Let me check that.


git log --all --oneline -- vector.tf 2>&1 | head -5

Check git history for vector.tf in consumer


Check if vector.tf is tracked


So `vector.tf` is genuinely a new file in the consumer (untracked), even though the prompt described it as "Modify". Let me re-check now that I have the full picture. Let me verify the critical concerns:


Cross-reference consumer wiring


/Users/steve.briggs/projects/IAC/terraform/iac-library/aws/compute/eks-cluster-applications/datadog_agent.tf:35:  logs_intake_url = local.vector_dd_agent_logs_enabled ? module.vector[0].service_endpoint_dd_agent : null
/Users/steve.briggs/projects/IAC/terraform/iac-library/aws/compute/eks-cluster-applications/datadog_agent.tf:125:  vector_dd_agent_logs_enabled = (
/Users/steve.briggs/projects/IAC/terraform/iac-library/aws/compute/eks-cluster-applications/datadog_agent.tf:126:    var.vector_dd_agent_logs_enabled != null
/Users/steve.briggs/projects/IAC/terraform/iac-library/aws/compute/eks-cluster-applications/datadog_agent.tf:127:    ? var.vector_dd_agent_logs_enabled
/Users/steve.briggs/projects/IAC/terraform/iac-library/aws/compute/eks-cluster-applications/datadog_agent.tf:132:variable "vector_dd_agent_logs_enabled" {
/Users/steve.briggs/projects/IAC/terraform/iac-library/aws/compute/eks-cluster-applications/datadog_agent.tf:138:# Plan-time guard: vector_dd_agent_logs_enabled cannot be true if vector itself is off.
/Users/steve.briggs/projects/IAC/terraform/iac-library/aws/compute/eks-cluster-applications/datadog_agent.tf:141:  count = local.vector_dd_agent_logs_enabled && !local.vector_enabled ? 1 : 0
/Users/steve.briggs/projects/IAC/terraform/iac-library/aws/compute/eks-cluster-applications/datadog_agent.tf:143:  program = ["Error: vector_dd_agent_logs_enabled requires vector_enabled"]


Cross-reference port 8282 across all files


/Users/steve.briggs/projects/IAC/terraform/internal-modules/kubernetes/monitoring/vector/netpol.tf:14:        # Port 8282 is the OPW-compatible Datadog Agent intake (datadog_agent source).
/Users/steve.briggs/projects/IAC/terraform/internal-modules/kubernetes/monitoring/vector/netpol.tf:20:            { port = "8282" },
/Users/steve.briggs/projects/IAC/terraform/internal-modules/kubernetes/monitoring/vector/templates/values.yaml.tpl:16:      port: 8282
/Users/steve.briggs/projects/IAC/terraform/internal-modules/kubernetes/monitoring/vector/templates/values.yaml.tpl:71:      address: 0.0.0.0:8282
/Users/steve.briggs/projects/IAC/terraform/internal-modules/kubernetes/monitoring/vector/outputs.tf:18:  value       = "http://vector.${var.namespace}.svc.cluster.local:8282"
/Users/steve.briggs/projects/IAC/terraform/internal-modules/kubernetes/monitoring/vector/README.md:28:- Datadog Agent intake on port 8282 (from DD Agent, OPW protocol)
/Users/steve.briggs/projects/IAC/terraform/internal-modules/kubernetes/monitoring/vector/README.md:73:| `service_endpoint_dd_agent` | OPW-compatible Datadog Agent intake endpoint (`http://vector.<namespace>.svc.cluster.local:8282`) |
/Users/steve.briggs/projects/IAC/terraform/internal-modules/kubernetes/monitoring/datadog-agent/README.md:79:  # e.g. "http://vector.vector.svc.cluster.local:8282"
/Users/steve.briggs/projects/IAC/terraform/iac-library/aws/compute/eks-cluster-applications/README.md:59:When `vector_dd_agent_logs_enabled` is true (defaults to following `vector_enabled`), the Datadog Agent ships container logs to Vector via the OPW HTTP intake on `vector.vector.svc.cluster.local:8282`. Logs traverse two filtering layers before reaching Datadog SaaS:


Port 8282 is consistent across all 9 references. Now let me check the data "external" guard pattern. The plan says it mirrors `data "external" "vector_validate"`, but I want to inspect whether the `program = ["Error: ..."]` pattern would actually fail at plan time (i.e., does this command resolve to a binary or does it fail by command-not-found?).


grep -B1 -A5 "data \"external\" \"vector_validate\"" /Users/steve.briggs/projects/IAC/terraform/iac-library/aws/compute/eks-cluster-applications/vector.tf

Check the existing vector_validate pattern


data "external" "vector_validate" {
  count = local.vector_enabled && !local.external_secrets_enabled ? 1 : 0

  program = ["Error: vector can only be set if external-secrets is enabled as well"]
}


Good - the new guard mirrors the existing pattern exactly. Both use `data "external"` with a `program` that's a non-existent command — when count > 0, terraform tries to run it, fails with command-not-found, and the user sees the error string in the message. That's the established convention.

Let me check for whitespace `~` strip directives and check for one missing thing - does the `vector_dd_agent_logs_enabled` local reference precede the `local.vector_enabled` reference correctly (HCL doesn't care about order, but mental verification):


grep -n "logs_intake_url\|%{ if logs_intake" /Users/steve.briggs/projects/IAC/terraform/internal-modules/kubernetes/monitoring/datadog-agent/values.yaml.tftpl

Check the if directive for whitespace stripping


82:%{ if logs_intake_url != null ~}
86:      value: "${logs_intake_url}"


terraform fmt -check datadog_agent.tf vector.tf README.md 2>&1; echo "exit: $?"

Check fmt status on consumer files we touched


cd /Users/steve.briggs/projects/IAC/terraform/internal-modules/kubernetes/monitoring/vector && terraform fmt -check -recursive 2>&1; echo "exit: $?"

Check fmt status on Vector module


cd /Users/steve.briggs/projects/IAC/terraform/internal-modules/kubernetes/monitoring/datadog-agent && terraform fmt -check -recursive 2>&1; echo "exit: $?"

Check fmt status on DD Agent module


╷
│ Error: Only .tf and .tfvars files can be processed with terraform fmt
│
│
╵

exit: 2


exit: 0


netpols.tf
exit: 3


terraform fmt -check datadog_agent.tf vector.tf 2>&1; echo "exit: $?"

Check fmt status on consumer .tf files only


terraform fmt -check -recursive 2>&1; echo "exit: $?"

Check fmt status across whole consumer


otel_collector.tf
sysdig_agent.tf
exit: 3


Confirmed: pre-existing fmt drift in `otel_collector.tf` and `sysdig_agent.tf` (which we didn't touch); our touched files are clean. The DD Agent module's `netpols.tf` also has pre-existing drift (wasn't touched by this change).

Now let me verify the `%{ if ~}` strip-newline behavior was actually verified by re-running the render harness, and confirm tests still pass:


cd /Users/steve.briggs/projects/IAC/terraform/internal-modules/kubernetes/monitoring/datadog-agent && ls -la tests/render/run.sh && [ -x tests/render/run.sh ] && echo "executable" || echo "not executable"

Check the render runner is executable


.rwxr-xr-x steve.briggs staff 2.0 KB Wed May  6 12:55:25 2026 tests/render/run.sh
executable


ls -la /Users/steve.briggs/projects/IAC/terraform/internal-modules/kubernetes/monitoring/vector/tests/vrl/redact_emails_dd_agent_logs/run.sh && [ -x /Users/steve.briggs/projects/IAC/terraform/internal-modules/kubernetes/monitoring/vector/tests/vrl/redact_emails_dd_agent_logs/run.sh ] && echo "executable" || echo "not executable"

Check VRL test runner is executable


.rwxr-xr-x steve.briggs staff 299 B Wed May  6 12:21:25 2026 /Users/steve.briggs/projects/IAC/terraform/internal-modules/kubernetes/monitoring/vector/tests/vrl/redact_emails_dd_agent_logs/run.sh
executable


Diff direct expected vs last-run actual


error: conflicting output format options.
usage: diff [-aBbdilpTtw] [-c | -e | -f | -n | -q | -u] [--ignore-case]
            [--no-ignore-case] [--normal] [--strip-trailing-cr] [--tabsize]
            [-I pattern] [-F pattern] [-L label] file1 file2
       diff [-aBbdilpTtw] [-I pattern] [-L label] [--ignore-case]
            [--no-ignore-case] [--normal] [--strip-trailing-cr] [--tabsize]
            [-F pattern] -C number file1 file2
       diff [-aBbdiltw] [-I pattern] [--ignore-case] [--no-ignore-case]
            [--normal] [--strip-trailing-cr] [--tabsize] -D string file1 file2
       diff [-aBbdilpTtw] [-I pattern] [-L label] [--ignore-case]
            [--no-ignore-case] [--normal] [--tabsize] [--strip-trailing-cr]
            [-F pattern] -U number file1 file2
       diff [-aBbdilNPprsTtw] [-c | -e | -f | -n | -q | -u] [--ignore-case]
            [--no-ignore-case] [--normal] [--tabsize] [-I pattern] [-L label]
            [-F pattern] [-S name] [-X file] [-x pattern] dir1 dir2
       diff [-aBbditwW] [--expand-tabs] [--ignore-all-blanks]
            [--ignore-blank-lines] [--ignore-case] [--minimal]
            [--no-ignore-file-name-case] [--strip-trailing-cr]
            [--suppress-common-lines] [--tabsize] [--text] [--width]
            -y | --side-by-side file1 file2


Diff via-vector expected vs last-run actual


error: conflicting output format options.
usage: diff [-aBbdilpTtw] [-c | -e | -f | -n | -q | -u] [--ignore-case]
            [--no-ignore-case] [--normal] [--strip-trailing-cr] [--tabsize]
            [-I pattern] [-F pattern] [-L label] file1 file2
       diff [-aBbdilpTtw] [-I pattern] [-L label] [--ignore-case]
            [--no-ignore-case] [--normal] [--strip-trailing-cr] [--tabsize]
            [-F pattern] -C number file1 file2
       diff [-aBbdiltw] [-I pattern] [--ignore-case] [--no-ignore-case]
            [--normal] [--strip-trailing-cr] [--tabsize] -D string file1 file2
       diff [-aBbdilpTtw] [-I pattern] [-L label] [--ignore-case]


macOS BSD diff is fussy. Let me re-run with separate flag:


Diff expected-direct vs actual


Diff expected-via-vector vs actual


Both renders are byte-identical to the locked baseline.

Let me sanity-check the count guards and make sure the consumer's ternary is safe even if `module.vector` count is 0:


Verify vector module count gate


15:  count  = local.vector_enabled ? 1 : 0
48:  count = local.vector_enabled && !local.external_secrets_enabled ? 1 : 0


Tracing the safety of `local.vector_dd_agent_logs_enabled ? module.vector[0].service_endpoint_dd_agent : null`:

- If `var.vector_dd_agent_logs_enabled` is unset/null → `local.vector_dd_agent_logs_enabled = local.vector_enabled`. So `local.vector_dd_agent_logs_enabled` is true iff `local.vector_enabled` is true → `module.vector[0]` exists. Safe.
- If `var.vector_dd_agent_logs_enabled = false` → ternary returns null without dereferencing. Safe.
- If `var.vector_dd_agent_logs_enabled = true` AND `local.vector_enabled = false` → `local.vector_dd_agent_logs_enabled = true`, so the ternary tries `module.vector[0].service_endpoint_dd_agent`, which would error with "Index out of range". HOWEVER, the `data "external" "vector_dd_agent_logs_validate"` guard activates in this case and fails plan first.

But Terraform evaluation order is non-deterministic between independent resources. The `data "external"` and the `module.datadog_agent` argument expression both reference the same condition. If terraform evaluates the module call's expression first, you'd hit "Index out of range" rather than the helpful guard message. Let me check terraform semantics more carefully.

Actually — terraform graph traversal: the `data "external"` count is `local.vector_dd_agent_logs_enabled && !local.vector_enabled`. When this is true (count=1), the data source attempts to execute the bogus program at plan time. Plan/apply order: terraform evaluates the count for both, then processes both as graph nodes. Since each has its own diagnostic, both will be raised. So the user sees both errors potentially. The "Index out of range" might actually fire earlier as a config validation error before the data resource runs.

This isn't great but it mirrors the existing `vector_validate` pattern in vector.tf — so it's at least consistent.

Now let me look at one more concern — does the `examples/basic` for the vector module successfully validate?


List examples/basic in vector module


drwxr-xr-x steve.briggs staff  96 B Tue May  5 13:46:52 2026 ..
.rw-r--r-- steve.briggs staff 307 B Tue May  5 13:47:15 2026 main.tf
.rw-r--r-- steve.briggs staff 207 B Tue May  5 13:47:16 2026 README.md
.rw-r--r-- steve.briggs staff 350 B Tue May  5 15:27:41 2026 versions.tf
drwxr-xr-x steve.briggs staff 192 B Wed May  6 12:30:09 2026 .
drwxr-xr-x steve.briggs staff  96 B Wed May  6 12:30:09 2026 .terraform


cd /Users/steve.briggs/projects/IAC/terraform/internal-modules/kubernetes/monitoring/vector/examples/basic && terraform init -backend=false 2>&1 | tail -5 && terraform validate 2>&1

Validate vector example


Error: Exit code 1
│ update this version constraint. Version constraints are normally set for
│ good reason, so updating the constraint may lead to other errors or
│ unexpected behavior.
╵

╷
│ Error: Missing required provider
│
│ This configuration requires provider registry.terraform.io/hashicorp/helm,
│ but that provider isn't available. You may be able to install it
│ automatically by running:
│   terraform init
╵
╷
│ Error: Missing required provider
│
│ This configuration requires provider
│ registry.terraform.io/hashicorp/kubernetes, but that provider isn't
│ available. You may be able to install it automatically by running:
│   terraform init
╵
╷
│ Error: Unsupported Terraform Core version
│
│   on versions.tf line 2, in terraform:
│    2:   required_version = ">= 1.9.0"
│
│ This configuration does not support Terraform version 1.3.10. To proceed,
│ either choose another supported Terraform version or update this version
│ constraint. Version constraints are normally set for good reason, so
│ updating the constraint may lead to other errors or unexpected behavior.
╵
╷
│ Error: Unsupported Terraform Core version
│
│   on ../../versions.tf line 2, in terraform:
│    2:   required_version = ">= 1.9.0"
│
│ Module module.vector (from ../..) does not support Terraform version
│ 1.3.10. To proceed, either choose another supported Terraform version or
│ update this version constraint. Version constraints are normally set for
│ good reason, so updating the constraint may lead to other errors or
│ unexpected behavior.
╵


Local terraform is 1.3.10 but the example requires 1.9.0+. CI uses 1.9 image. This validates differently in CI (where the version + providers are right). Confirming this is environment-related, not a code issue.

Let me check the variables.tf in the vector module to verify there's no missing var the spec implied (the spec says "no new module variable"):


1	# ---------------------------------------------------------------------------------------------------------------------
2	# REQUIRED PARAMETERS
3	# These variables must be set.
4	# ---------------------------------------------------------------------------------------------------------------------
5
6	variable "chart_version" {
7	  description = "Vector helm chart version pin"
8	  type        = string
9	}
10
11	variable "image_tag_version" {
12	  description = "Vector image tag"
13	  type        = string
14	}
15
16	# ---------------------------------------------------------------------------------------------------------------------
17	# OPTIONAL PARAMETERS
18	# These variables are configurable, but have reasonable defaults set.
19	# ---------------------------------------------------------------------------------------------------------------------
20	variable "datadog_site" {
21	  description = "Datadog site (e.g. datadoghq.com, us3.datadoghq.com)"
22	  type        = string
23	  default     = "datadoghq.com"
24	}
25
26	variable "image" {
27	  description = "Vector image source"
28	  type        = string
29	  default     = "registry.ic-ops.net/cache-docker-hub/timberio/vector"
30	}
31
32	variable "replicas" {
33	  description = "Number of Vector aggregator replicas"
34	  type        = number
35	  default     = 3
36	}
37
38	variable "audit_drop_patterns" {
39	  description = "Regex patterns; matching log message bodies are dropped"
40	  type        = list(string)
41	  default = [
42	    "Audit\\s-\\s.+",
43	    "\"level\":\"audit\"",
44	  ]
45	}
46
47	variable "datadog_api_key_vault_path" {
48	  description = "Vault path for the Datadog API key, consumed by ExternalSecret"
49	  type        = string
50	  default     = "secret/data/saas/infrastructure/datadog-api-key"
51	}
52
53	variable "external_secrets_cluster_store_name" {
54	  description = "Name of the ClusterSecretStore created by external-secrets module"
55	  type        = string
56	  default     = "vault"
57	}
58
59	variable "email_redaction_enabled" {
60	  description = "Toggle the email-redact VRL transform across all signals"
61	  type        = bool
62	  default     = true
63	}
64
65	variable "email_redaction_replacement" {
66	  description = "String that replaces matched email addresses"
67	  type        = string
68	  default     = "[REDACTED-EMAIL]"
69	}
70
71	variable "additional_config" {
72	  description = "Escape hatch — overwrites helm customConfig wholesale (mirrors otel_collector_additional_config semantics)"
73	  type        = any
74	  default     = null
75	}
76
77	variable "resources_requests" {
78	  description = "Pod resource requests"
79	  type        = map(string)
80	  default = {
81	    cpu    = "200m"
82	    memory = "256Mi"
83	  }
84	}
85
86	variable "resources_limits" {
87	  description = "Pod resource limits"
88	  type        = map(string)
89	  default = {
90	    cpu    = "2"
91	    memory = "1Gi"
92	  }
93	}
94
95	variable "namespace" {
96	  description = "Kubernetes namespace for Vector"
97	  type        = string
98	  default     = "vector"
99	}
100
101	variable "service_monitor_enabled" {
102	  description = "Create a ServiceMonitor for Vector self-metrics. Requires kube-prometheus-stack CRDs in cluster."
103	  type        = bool
104	  default     = false
105	}
106