data protection.md

Manage Data Protection

Define custom detection types, allow lists, and enforcement policies as code, with a dry-run-first rollout

This guide manages your organization's Data Control Center configuration in Terraform: the org-level switch, a detection engine (Protection Profile), a custom detection type, allow-list entries that suppress false positives, and an enforcement policy rolled out safely with dry run. The whole flow is self-contained — no IDs need to be copied from the app.

**Estimated time**: 20–30 minutes. Complete [Getting Started](/terraform/getting-started) first. For the concepts, see the [Data Control Center overview](/how-tos/data-control-center/overview).

Before You Begin

Step 1: Adopt the organization config

Data protection has one configuration object per organization, provisioned by the platform. The barndoor_dlp_org_config resource adopts that singleton rather than creating anything:

resource "barndoor_dlp_org_config" "this" {
  enabled = true

# Observe-only mode: every policy records findings without acting.
  global_dry_run = false
}

Reference: barndoor_dlp_org_config

Because it's a singleton, `terraform destroy` doesn't delete anything — it resets both settings to the platform defaults (`enabled = true`, `global_dry_run = false`) and drops the resource from state.

Step 2: Define a detection engine (Protection Profile)

A detection engine — a Protection Profile in the app — binds a detection provider to the data types it scans for. Enforcement policies reference engines, so at least one must exist. Manage it in Terraform, or look up one that was created in the portal:

```hcl theme={null} resource "barndoor_dlp_detection_engine" "secrets" { name = "Credential secrets" provider_type = "builtin_secrets_regex"

enabled_detection_types = [ "DETECTION_TYPE_AWS_ACCESS_KEY_ID", "DETECTION_TYPE_GITHUB_PAT", ] } ```

```hcl theme={null} data "barndoor_dlp_detection_engine" "secrets" { # Names are unique per provider_type; set provider_type (or use id) # when one profile spans several engines. name = "Credential secrets" provider_type = "builtin_secrets_regex" } ```

References: barndoor_dlp_detection_engine resource, data source

Engines are unique per (`name`, `provider_type`) — the same name across several provider types renders as one merged profile in the app. Deleting an engine that is the only one on an enforcement policy fails with an error naming the referencing policy.

Step 3: Define a custom detection type

Custom detection types extend the built-in detectors with organization-specific patterns — project codenames, internal ticket formats, customer ID shapes:

resource "barndoor_dlp_custom_detection_type" "codenames" {
  name        = "Project codenames"
  description = "Internal project codenames that must not leave the organization"

patterns = [
    { pattern = "(?i)project\s+aurora", pattern_type = "PATTERN_TYPE_REGEX" },
    { pattern = "AURORA-CLASSIFIED", pattern_type = "PATTERN_TYPE_LITERAL" },
  ]

# Both default to *_MEDIUM when omitted.
  default_severity   = "DETECTION_SEVERITY_HIGH"
  default_confidence = "DETECTION_CONFIDENCE_HIGH"
}

Reference: barndoor_dlp_custom_detection_type

Patterns are evaluated in order. Regex patterns use Rust regex syntax and are validated by the API — a pattern that doesn't compile is rejected at apply time, not silently ignored.

The platform assigns the type's wire name (DETECTION_TYPE_CUSTOM_…), exposed as the detection_type attribute — that's the value other data protection resources reference.

Step 4: Suppress false positives with allow-list entries

Allow-list entries stop known-safe values from being reported (and acted on) as findings:

# Scoped to specific detection types — including the custom one from Step 3.
resource "barndoor_dlp_allow_list_entry" "codename_docs_site" {
  pattern         = "aurora.docs.example.com"
  pattern_type    = "PATTERN_TYPE_LITERAL"
  detection_types = [barndoor_dlp_custom_detection_type.codenames.detection_type]
  reason          = "Public documentation host, not a leak"
}

# Omit detection_types to suppress matches for every detection type.
resource "barndoor_dlp_allow_list_entry" "support_mailbox" {
  pattern         = "support@example.com"
  pattern_type    = "PATTERN_TYPE_LITERAL"
  detection_types = ["DETECTION_TYPE_EMAIL"]
  reason          = "Shared support mailbox, not PII"
}

Reference: barndoor_dlp_allow_list_entry

The platform has no update operation for allow-list entries, so changing **any** attribute replaces the entry (delete + create). Plans will show this as a replacement — that's expected.

Step 5: Enforce with a policy — dry run first

Enforcement policies decide what happens when a detection engine reports a finding. A policy targets exactly one lane: MCP traffic (tool inputs/outputs) or model-provider traffic (prompts/responses).

Start in dry run — the policy evaluates and records findings in activity, but takes no action:

resource "barndoor_dlp_enforcement_policy" "block_secrets_in_prompts" {
  name          = "Block secrets in prompts"
  action        = "POLICY_ACTION_BLOCK"
  provider_ids  = ["openai"]
  runtime_stage = "RUNTIME_STAGE_PROMPT"

# Roll out observing first; flip to false to enforce.
  dry_run = true

detection_engine_ids = [barndoor_dlp_detection_engine.secrets.id]
}

And an MCP-lane example — tokenize PII in tool traffic for one group:

resource "barndoor_dlp_enforcement_policy" "tokenize_mcp_pii" {
  name   = "Tokenize PII on MCP traffic"
  action = "POLICY_ACTION_TOKENIZE"

mcp_targets = [
    { mcp_server_id = "*", direction = "BOTH" },
  ]

principals = [
    { principal_type = "GROUP", principal_id = "engineering" },
  ]

dry_run = true

detection_engine_ids = [barndoor_dlp_detection_engine.secrets.id]
}

Reference: barndoor_dlp_enforcement_policy

**The two lanes don't mix.** `MCP_SERVER` policies use `mcp_targets` with the `RUNTIME_STAGE_TOOL_INPUT` / `RUNTIME_STAGE_TOOL_OUTPUT` stages and may not set `provider_ids` or `model_alias`; `MODEL_PROVIDER` policies are the reverse, with `RUNTIME_STAGE_PROMPT` / `RUNTIME_STAGE_RESPONSE`. The API rejects mixed shapes. It also rejects (with a 422) any detection engine that doesn't support the policy's `action`.

Step 6: Flip to enforcement

Once dry-run findings in the activity view look right — expected matches, no false positives that an allow-list entry should cover — enforce by changing one line and re-applying:

  dry_run = false

This is the code-review moment the dry-run pattern exists for: the diff that turns on enforcement is one visible line in a pull request.

There are two dry-run levers, and they compose: `global_dry_run` on the org config forces *every* policy to observe-only (useful during initial rollout of the whole system), while per-policy `dry_run` stages an individual policy. Enforcement happens only when both are off.

Complete example

```hcl theme={null} resource "barndoor_dlp_org_config" "this" { enabled = true global_dry_run = false }

resource "barndoor_dlp_detection_engine" "secrets" { name = "Credential secrets" provider_type = "builtin_secrets_regex"

enabled_detection_types = [ "DETECTION_TYPE_AWS_ACCESS_KEY_ID", "DETECTION_TYPE_GITHUB_PAT", ] }

resource "barndoor_dlp_custom_detection_type" "codenames" { name = "Project codenames" description = "Internal project codenames that must not leave the organization"

default_severity = "DETECTION_SEVERITY_HIGH" default_confidence = "DETECTION_CONFIDENCE_HIGH" }

resource "barndoor_dlp_allow_list_entry" "codename_docs_site" { pattern = "aurora.docs.example.com" pattern_type = "PATTERN_TYPE_LITERAL" detection_types = [barndoor_dlp_custom_detection_type.codenames.detection_type] reason = "Public documentation host, not a leak" }

resource "barndoor_dlp_enforcement_policy" "block_secrets_in_prompts" { name = "Block secrets in prompts" action = "POLICY_ACTION_BLOCK" provider_ids = ["openai"] runtime_stage = "RUNTIME_STAGE_PROMPT" dry_run = true

detection_engine_ids = [barndoor_dlp_detection_engine.secrets.id] }

</Expandable>