Skip to content

microsoftexpert/tf-mod-aws-datazone

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🟧 AWS DataZone Terraform Module

Secure-by-default Amazon DataZone data-governance/catalog fabric — the domain keystone plus projects, environment profiles, environments, environment-blueprint configuration, business glossaries/terms, custom form types, custom asset types, and user profiles, from a single composite call. Only the domain is taggable; every child collection cross-validates its key references at plan time. Built for the AWS provider v6.x.

Terraform aws module type resources


🧩 Overview

  • 🏛️ Provisions an aws_datazone_domain keystone plus the full governance/catalog fabric under it: projects, environment profiles, environments, environment-blueprint configurations, glossaries, glossary terms, custom form types, custom asset types, and user profiles — one composite call, one working, access-governed data-catalog domain.
  • 🔐 Optional customer-managed KMS key (kms_key_identifier) for domain metadata/reporting-data encryption — null defaults to an AWS-owned key; supply a CMK from tf-mod-aws-kms for any domain cataloging PII-adjacent metadata.
  • 🪪 IAM Identity Center integration is first-class (single_sign_on) — user_assignment defaults to "MANUAL" (secure baseline): every data-portal user is an explicit, reviewable assignment, never an automatic grant to the whole Identity Center population.
  • 🛑 Domain and project deletion checks default to on (skip_deletion_check = false) — DataZone refuses to delete a non-empty domain/project unless you explicitly opt into cascade deletion.
  • 🏷️ Only the domain is taggable. Confirmed against the live v6.54.0 schema: none of the nine child resources (project, environment, environment_profile, environment_blueprint_configuration, glossary, glossary_term, form_type, asset_type, user_profile) expose a tags/tags_all argument — the mirror image of tf-mod-aws-organizations, where the parent is untaggable and the children are.
  • 🔗 Every cross-collection key reference is validated at plan timeproject_key, glossary_key, environment_profile_key typos surface as a clear terraform plan error, never an opaque provider 400.
  • 🧬 Two resources have no synthetic id at allaws_datazone_form_type and aws_datazone_asset_type are identified by (domain, name[, revision]); this module surfaces name/revision outputs instead of fabricating an id that doesn't exist.

💡 Why it matters: DataZone is the access-grant and discovery layer for our data estate — it doesn't move data, it governs who can find and request it. Getting the domain's encryption, SSO, and deletion-safety defaults right here is what keeps a catalog of PII-adjacent metadata from becoming its own exposure surface.


❤️ Support this project

If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:

Whether it's a star, a professional connection, or a coffee, every gesture helps keep these modules actively maintained and continually improving. Thank you for being part of the community!


🗺️ Where this fits in the family

tf-mod-aws-datazone sits downstream of the identity/encryption foundation modules and upstream of the actual data-plane modules it catalogs. It does not create IAM roles, KMS keys, an Identity Center instance, or the underlying Glue/Redshift/Lake Formation resources — those are sibling modules it consumes by ARN/id or is consumed by.

flowchart LR
 iamrole["tf-mod-aws-iam-role<br/>domain_execution_role / service_role"]
 kms["tf-mod-aws-kms<br/>kms_key_identifier"]
 idc["IAM Identity Center<br/>(account opt-in)"]
 dz["tf-mod-aws-datazone"]
 glue["tf-mod-aws-glue"]
 redshift["tf-mod-aws-redshift"]
 lf["tf-mod-aws-lakeformation"]
 s3["tf-mod-aws-s3-bucket"]

 iamrole -->|"arn"| dz
 kms -->|"arn"| dz
 idc -->|"single_sign_on.type = IAM_IDC"| dz
 dz -->|"environment_provisioned_resources"| glue
 dz -->|"environment_provisioned_resources"| redshift
 dz -->|"glossary_term_ids (tag-based access)"| lf
 dz -.->|"catalogs metadata about"| s3

 style dz fill:#FF9900,color:#fff,stroke:#cc7a00,stroke-width:2px
Loading

🧬 What this module builds

flowchart TB
 subgraph DZMOD["tf-mod-aws-datazone"]
 dom["aws_datazone_domain.this<br/>(keystone)"]
 proj["aws_datazone_project.this<br/>for_each projects"]
 envprof["aws_datazone_environment_profile.this<br/>for_each environment_profiles"]
 env["aws_datazone_environment.this<br/>for_each environments"]
 ebc["aws_datazone_environment_blueprint_configuration.this<br/>for_each environment_blueprint_configurations"]
 glos["aws_datazone_glossary.this<br/>for_each glossaries"]
 term["aws_datazone_glossary_term.this<br/>for_each glossary_terms"]
 form["aws_datazone_form_type.this<br/>for_each form_types"]
 asset["aws_datazone_asset_type.this<br/>for_each asset_types"]
 user["aws_datazone_user_profile.this<br/>for_each user_profiles"]

 dom -->|"id"| proj
 dom -->|"id"| ebc
 dom -->|"id"| user
 proj -->|"id"| envprof
 proj -->|"id"| glos
 proj -->|"id"| form
 proj -->|"id"| asset
 envprof -->|"id"| env
 proj -->|"id"| env
 glos -->|"id"| term
 form -.->|"name / revision"| asset
 end

 style dom fill:#FF9900,color:#fff,stroke:#cc7a00,stroke-width:2px
Loading
Resource Role Cardinality Taggable Has id?
aws_datazone_domain.this Keystone — domain identity, encryption, SSO 1 ✅ (+ arn)
aws_datazone_project.this Project namespace per projects entry
aws_datazone_environment_profile.this Reusable environment template per environment_profiles entry
aws_datazone_environment.this Deployed environment per environments entry
aws_datazone_environment_blueprint_configuration.this Regional blueprint enablement per environment_blueprint_configurations entry ❌ (no attributes beyond arguments)
aws_datazone_glossary.this Business glossary per glossaries entry
aws_datazone_glossary_term.this Glossary term per glossary_terms entry
aws_datazone_form_type.this Custom metadata schema per form_types entry ❌ (composite name+revision identity)
aws_datazone_asset_type.this Custom asset type per asset_types entry ❌ (composite name identity)
aws_datazone_user_profile.this Activated data-portal principal per user_profiles entry

ℹ️ Only aws_datazone_domain exposes a tags argument in the v6.54.0 provider schema — the module deliberately does not attempt to tag any of the nine child resources.


✅ Provider / Versions

Requirement Version
Terraform >= 1.12.0
hashicorp/aws >= 6.0, < 7.0

No provider {} block is declared inside the module — the caller's configured provider (credentials, default_tags) is inherited. Credentials resolve through the standard AWS chain (env → SSO/shared credentials → assume_role → instance profile / IRSA → OIDC) at the root/pipeline level; there are no credential variables. DataZone resources are Regional (no region variable declared — see Architecture Notes for the environment cross-account/cross-region nuance, which is a plain string-argument concern, not a provider-alias one).


🔑 Required IAM Permissions

Action Required for Notes
datazone:CreateDomain, datazone:GetDomain, datazone:UpdateDomain, datazone:DeleteDomain, datazone:ListDomains Domain lifecycle
datazone:CreateProject, datazone:GetProject, datazone:UpdateProject, datazone:DeleteProject, datazone:ListProjects Project lifecycle
datazone:CreateEnvironmentProfile, datazone:GetEnvironmentProfile, datazone:UpdateEnvironmentProfile, datazone:DeleteEnvironmentProfile, datazone:ListEnvironmentProfiles Environment-profile lifecycle
datazone:CreateEnvironment, datazone:GetEnvironment, datazone:UpdateEnvironment, datazone:DeleteEnvironment, datazone:ListEnvironments Environment lifecycle Provisioning is asynchronous — see AWS Prerequisites
datazone:PutEnvironmentBlueprintConfiguration, datazone:GetEnvironmentBlueprintConfiguration, datazone:DeleteEnvironmentBlueprintConfiguration, datazone:ListEnvironmentBlueprintConfigurations, datazone:ListEnvironmentBlueprints, datazone:GetEnvironmentBlueprint Blueprint enablement + blueprint-id lookups The last two are used by the caller's aws_datazone_environment_blueprint data-source lookup, out of scope for this module
datazone:CreateGlossary, datazone:GetGlossary, datazone:UpdateGlossary, datazone:DeleteGlossary Glossary lifecycle
datazone:CreateGlossaryTerm, datazone:GetGlossaryTerm, datazone:UpdateGlossaryTerm, datazone:DeleteGlossaryTerm Glossary-term lifecycle
datazone:CreateFormType, datazone:GetFormType, datazone:UpdateFormType, datazone:DeleteFormType Form-type lifecycle DeleteFormType only succeeds once status = DISABLED — see Troubleshooting
datazone:CreateAssetType, datazone:GetAssetType, datazone:DeleteAssetType Asset-type lifecycle
datazone:CreateUserProfile, datazone:GetUserProfile, datazone:UpdateUserProfile User-profile lifecycle No DeleteUserProfile action exists — see Architecture Notes
datazone:TagResource, datazone:UntagResource, datazone:ListTagsForResource Tagging the domain The only taggable resource in this module
iam:PassRole Passing domain_execution_role, service_role, manage_access_role_arn, provisioning_role_arn to DataZone Scope the resource to only these specific role ARNs
sso:* (scope down per your Identity Center delegation model) single_sign_on enablement AWS's own example policy grants broad sso:* — narrow this for production
kms:CreateGrant, kms:DescribeKey kms_key_identifier (CMK) The CMK's own key policy must separately allow the DataZone service principal

No DataZone service-linked role is auto-created by this module's actions; enabling an AWS-managed blueprint provisions a CloudFormation stack that creates its own resources in the target account/Region.


📋 AWS Prerequisites

  • domain_execution_role must exist first, with a trust policy allowing sts:AssumeRole/sts:TagSession (and sts:SetContext for V2) for datazone.amazonaws.com, plus cloudformation.amazonaws.com for CloudFormation-provisioned blueprints.
  • domain_version = "V2" also requires service_role — a second IAM role with its own AWS-managed policy attachment (DataZone V2 shares SageMaker Unified Studio's domain model).
  • IAM Identity Center must be enabled account/organization-wide before single_sign_on.type = "IAM_IDC" succeeds — an account-level opt-in outside this module's (and this catalog's Phase 1) scope.
  • Environment blueprints are AWS-managed, not created here. Resolve environment_blueprint_identifier via the aws_datazone_environment_blueprint data source before referencing it.
  • Region model: domain/project/glossary/metadata-type resources are Regional with no us-east-1 coupling; environment_profiles/environments provision into a specific target aws_account_id/aws_account_region as plain string arguments — DataZone's native cross-account data-mesh model, not a Terraform provider-alias concern.
  • Service quotas (soft limits): domains per account/Region, projects per domain, environments per project, glossaries per project — consult DataZone's Service Quotas page before large rollouts.

📁 Module Structure

tf-mod-aws-datazone/
├── providers.tf # terraform{} + required_providers (aws >= 6.0, < 7.0); no provider block
├── variables.tf # domain config, 9 child-collection maps, tags, timeouts
├── main.tf # aws_datazone_domain.this + 9 for_each child resources
├── outputs.tf # id + arn (domain), portal_url, child reference maps, tags_all
├── README.md # this file
└── SCOPE.md # in/out-of-scope, Consumes/Emits, IAM, prerequisites, gotchas

⚙️ Quick Start

module "iam_domain_execution_role" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-iam-role?ref=v1.0.0"

  name = "datazone-domain-execution-role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect    = "Allow"
      Action    = ["sts:AssumeRole", "sts:TagSession"]
      Principal = { Service = "datazone.amazonaws.com" }
    }]
  })
}

module "datazone" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-datazone?ref=v1.0.0"

  name                  = "casey-data-catalog"
  domain_execution_role = module.iam_domain_execution_role.arn

  projects = {
    lending_analytics = {
      name        = "lending-analytics"
      description = "Aggregated, de-identified lending analytics project."
    }
  }

  tags = {
    Environment = "prod"
    Owner       = "data-governance"
  }
}

🔌 Cross-Module Contract

Consumes

Input Type Source module
domain_execution_role string (IAM role ARN) tf-mod-aws-iam-role
service_role string (IAM role ARN, V2 only) tf-mod-aws-iam-role
kms_key_identifier string (KMS key ARN) tf-mod-aws-kms
environment_blueprint_configurations[*].manage_access_role_arn / provisioning_role_arn string (IAM role ARN) tf-mod-aws-iam-role
environment_profiles[*].environment_blueprint_identifier string (blueprint id) aws_datazone_environment_blueprint data source (caller-resolved)
user_profiles[*].user_identifier string (IAM ARN or Identity Center id) tf-mod-aws-iam-role, tf-mod-aws-iam-user, IAM Identity Center

Emits

Output Description Consumed by
id Domain id Every child resource's domain_identifier
arn Domain ARN IAM policies scoping datazone:* to this domain
portal_url Data-portal URL Human documentation / SSO bookmarking
root_domain_unit_id Root domain-unit id Future domain-unit modules
project_ids / project_statuses Project maps keyed by caller key Internal wiring; tf-mod-aws-lakeformation grants; audit
environment_profile_ids Profile map Internal environments wiring
environment_ids / environment_provisioned_resources Environment maps tf-mod-aws-glue/tf-mod-aws-redshift/tf-mod-aws-lakeformation
glossary_ids / glossary_term_ids Glossary maps projects[*].glossary_terms wiring; tag-based access control
form_type_names / form_type_revisions Form-type composite identity asset_types[*].forms_input wiring
asset_type_names / asset_type_revisions Asset-type composite identity Catalog automation
user_profile_ids / user_profile_types User-profile maps Access review / audit
tags_all Domain's merged tags — the only taggable resource Governance/audit

📚 Example Library

1 · Minimal domain + project
module "datazone" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-datazone?ref=v1.0.0"

  name                  = "casey-catalog"
  domain_execution_role = module.iam_domain_execution_role.arn

  projects = {
    core = { name = "core-catalog" }
  }
}
2 · `tags` merging with provider `default_tags`
provider "aws" {
  default_tags {
    tags = {
      ManagedBy  = "terraform"
      CostCenter = "data-platform"
    }
  }
}

module "datazone" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-datazone?ref=v1.0.0"

  name                  = "casey-catalog"
  domain_execution_role = module.iam_domain_execution_role.arn

  # Resource tags win on key conflict with default_tags; tags_all reflects the merge.
  tags = {
    Environment = "prod"
    CostCenter  = "data-governance" # overrides the provider default_tags value
  }
}
3 · Customer-managed KMS key (CMK) for domain encryption
module "kms" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-kms?ref=v1.0.0"
  name   = "datazone-catalog-cmk"
}

module "datazone" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-datazone?ref=v1.0.0"

  name                  = "casey-catalog"
  domain_execution_role = module.iam_domain_execution_role.arn
  kms_key_identifier    = module.kms.arn # PII-adjacent metadata: always supply a CMK
}
4 · Secure-by-default opt-out: skipping the domain deletion check
module "datazone_sandbox" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-datazone?ref=v1.0.0"

  name                  = "casey-catalog-sandbox"
  domain_execution_role = module.iam_domain_execution_role.arn

  # Opt-out — only for a throwaway sandbox domain being torn down wholesale.
  # Production domains should leave this false and empty out children first.
  skip_deletion_check = true

  projects = {
    scratch = {
      name                = "scratch"
      skip_deletion_check = true
    }
  }
}
5 · IAM Identity Center single sign-on
module "datazone" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-datazone?ref=v1.0.0"

  name                  = "casey-catalog"
  domain_execution_role = module.iam_domain_execution_role.arn

  single_sign_on = {
    type            = "IAM_IDC"
    user_assignment = "MANUAL" # explicit, reviewable per-user assignment (the default)
  }
}
6 · Environment profile + environment for a Data Lake blueprint
data "aws_datazone_environment_blueprint" "data_lake" {
  domain_id = module.datazone.id
  name      = "DefaultDataLake"
  managed   = true
}

module "datazone" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-datazone?ref=v1.0.0"

  name                  = "casey-catalog"
  domain_execution_role = module.iam_domain_execution_role.arn

  projects = {
    lending = { name = "lending" }
  }

  environment_blueprint_configurations = {
    data_lake = {
      environment_blueprint_identifier = data.aws_datazone_environment_blueprint.data_lake.id
      enabled_regions                  = ["us-east-1"]
      manage_access_role_arn           = module.iam_manage_access_role.arn
      provisioning_role_arn            = module.iam_provisioning_role.arn
    }
  }

  environment_profiles = {
    lending_data_lake = {
      name                             = "lending-data-lake-profile"
      project_key                      = "lending"
      aws_account_region               = "us-east-1"
      environment_blueprint_identifier = data.aws_datazone_environment_blueprint.data_lake.id
    }
  }

  environments = {
    lending_prod = {
      name                    = "lending-prod-environment"
      project_key             = "lending"
      environment_profile_key = "lending_data_lake"
    }
  }
}
7 · Glossary with related terms
module "datazone" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-datazone?ref=v1.0.0"

  name                  = "casey-catalog"
  domain_execution_role = module.iam_domain_execution_role.arn

  projects = {
    lending = { name = "lending" }
  }

  glossaries = {
    lending_terms = {
      name        = "Lending Glossary"
      project_key = "lending"
    }
  }

  glossary_terms = {
    borrower = {
      name              = "Borrower"
      glossary_key      = "lending_terms"
      short_description = "A member entity that has received or applied for credit."
    }
    npi = {
      name              = "PII"
      glossary_key      = "lending_terms"
      short_description = "Nonpublic personal information under privacy-regulation."
      term_relations = {
        classifies = [] # populated with asset-classifying term ids as the glossary matures
      }
    }
  }
}
8 · Custom form type with a Smithy model
module "datazone" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-datazone?ref=v1.0.0"

  name                  = "casey-catalog"
  domain_execution_role = module.iam_domain_execution_role.arn

  projects = {
    lending = { name = "lending" }
  }

  form_types = {
    npi_classification = {
      name        = "NpiClassificationFormType"
      project_key = "lending"
      description = "Classifies an asset's PII sensitivity tier."
      model = {
        smithy = <<-EOT
 structure NpiClassificationFormType {
 @required
 sensitivityTier: String
 containsSsn: Boolean
 }
 EOT
      }
    }
  }
}
9 · Custom asset type referencing the form type above
module "datazone" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-datazone?ref=v1.0.0"

  name                  = "casey-catalog"
  domain_execution_role = module.iam_domain_execution_role.arn

  projects = {
    lending = { name = "lending" }
  }

  form_types = {
    npi_classification = {
      name        = "NpiClassificationFormType"
      project_key = "lending"
      model       = { smithy = "structure NpiClassificationFormType { @required sensitivityTier: String }" }
    }
  }

  asset_types = {
    loan_file = {
      name        = "LoanFile"
      project_key = "lending"
      description = "A governed loan-file data asset."
      forms_input = [
        {
          map_block_key   = "npi_classification"
          type_identifier = "NpiClassificationFormType"
          type_revision   = "1"
          required        = true
        }
      ]
    }
  }
}
10 · Activating IAM and SSO user profiles
module "datazone" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-datazone?ref=v1.0.0"

  name                  = "casey-catalog"
  domain_execution_role = module.iam_domain_execution_role.arn

  user_profiles = {
    data_steward = {
      user_identifier = module.iam_data_steward_role.arn
      user_type       = "IAM_USER"
    }
    analyst_sso = {
      user_identifier = "11111111-2222-3333-4444-555555555555" # IAM Identity Center user id
      user_type       = "SSO_USER"
    }
  }
}
11 · `for_each` pattern — many projects from a single caller-side map
locals {
  business_units = toset(["lending", "deposits", "risk"])
}

module "datazone" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-datazone?ref=v1.0.0"

  name                  = "casey-catalog"
  domain_execution_role = module.iam_domain_execution_role.arn

  projects = {
    for bu in local.business_units : bu => {
      name        = bu
      description = "Catalog project for the ${bu} business unit."
    }
  }
}
12 · V2 domain requiring both execution and service roles
module "datazone_v2" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-datazone?ref=v1.0.0"

  name                  = "casey-catalog-v2"
  domain_version        = "V2"
  domain_execution_role = module.iam_domain_execution_role.arn
  service_role          = module.iam_domain_service_role.arn
}
13 · `import` block — bringing an existing domain under management
import {
  to = module.datazone.aws_datazone_domain.this
  id = "dzd_54nakfrg9k6sri"
}

module "datazone" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-datazone?ref=v1.0.0"

  name                  = "casey-catalog" # match the existing domain's name exactly
  domain_execution_role = module.iam_domain_execution_role.arn
}
14 · End-to-end composition — domain, IAM, KMS, project, blueprint, glossary, and the data-plane siblings it catalogs
module "iam_domain_execution_role" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-iam-role?ref=v1.0.0"

  name = "datazone-domain-execution-role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect    = "Allow"
        Action    = ["sts:AssumeRole", "sts:TagSession"]
        Principal = { Service = "datazone.amazonaws.com" }
      },
      {
        Effect    = "Allow"
        Action    = "sts:AssumeRole"
        Principal = { Service = "cloudformation.amazonaws.com" }
      }
    ]
  })
}

module "kms" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-kms?ref=v1.0.0"
  name   = "datazone-catalog-cmk"
}

module "glue" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-glue?ref=v1.0.0"
  #... database/crawler backing the lending data lake...
}

module "lakeformation" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-lakeformation?ref=v1.0.0"
  #... least-privilege grants scoped to module.datazone.project_ids["lending"]...
}

module "datazone" {
  source = "git::https://github.com/microsoftexpert/tf-mod-aws-datazone?ref=v1.0.0"

  name                  = "casey-catalog"
  domain_execution_role = module.iam_domain_execution_role.arn
  kms_key_identifier    = module.kms.arn

  single_sign_on = {
    type = "IAM_IDC"
  }

  projects = {
    lending = { name = "lending" }
  }

  glossaries = {
    lending_terms = {
      name        = "Lending Glossary"
      project_key = "lending"
    }
  }

  glossary_terms = {
    npi = {
      name              = "PII"
      glossary_key      = "lending_terms"
      short_description = "Nonpublic personal information under privacy-regulation."
    }
  }

  tags = {
    Environment = "prod"
    Owner       = "data-governance"
  }
}

📥 Inputs

ℹ️ High-level grouping:

  • Domain identity: name, domain_execution_role
  • Domain configuration: description, domain_version, kms_key_identifier, service_role, single_sign_on, skip_deletion_check
  • Child collections (for_each maps): projects, environment_profiles, environments, environment_blueprint_configurations, glossaries, glossary_terms, form_types, asset_types, user_profiles
  • Universal: tags, timeouts (domain-level only — see Architecture Notes for which child collections carry their own per-entry timeouts)

🧾 Outputs

  • Primary: id, arn (domain only — no child resource exposes an ARN)
  • Domain: portal_url, root_domain_unit_id, domain_version
  • Projects: project_ids, project_statuses
  • Environment profiles: environment_profile_ids
  • Environments: environment_ids, environment_provisioned_resources
  • Environment blueprint configurations: environment_blueprint_configuration_blueprint_ids (reference only — this resource has no computed id/arn)
  • Glossaries / terms: glossary_ids, glossary_term_ids
  • Form types: form_type_names, form_type_revisions (composite identity — no synthetic id)
  • Asset types: asset_type_names, asset_type_revisions (composite identity — no synthetic id)
  • User profiles: user_profile_ids, user_profile_types
  • Tags: tags_all (domain only)

ℹ️ No outputs are marked sensitive — this module emits no secrets.


🧠 Architecture Notes

  • ARN / ID formats: Domain iddzd_xxxxxxxxxxxxx; arnarn:aws:datazone:<region>:<account_id>:domain/<id>. No child resource in this module exposes an ARN.
  • Only the domain is taggable — confirmed against the live v6.54.0 schema. var.tags flows exclusively to aws_datazone_domain.this; tags_all reflects the domain only.
  • aws_datazone_form_type and aws_datazone_asset_type have no id attribute — their identity is the composite (domain_identifier, name, revision) and (domain_identifier, name) respectively. Reference them downstream by name/revision.
  • aws_datazone_environment_blueprint_configuration exports no additional attributes at all — the (domain_id, environment_blueprint_id) pair supplied by the caller is the resource's complete identity.
  • environment.user_parameters is FORCE-NEW — changing it destroys and recreates the environment (and whatever data-plane resources it provisioned). Treat as a planned-downtime change.
  • domain_version has no supported downgrade/upgrade path in the DataZone API — treat as effectively FORCE-NEW even though the schema doesn't flag it.
  • aws_datazone_user_profile has no delete API — its timeouts expose only create/update; removing a user_profiles entry deactivates the profile rather than truly deleting it.
  • Destroy ordering: a Domain (or Project) with skip_deletion_check = false (the default) refuses to delete while it still owns child entities — Terraform's own dependency graph destroys children first in a normal terraform destroy, but a targeted destroy or console-created drift can still surface a ConflictException.
  • No us-east-1 global-resource requirement — unlike CloudFront/ACM/WAFv2, DataZone has no global-service coupling; environment_profiles/environments target a specific account/Region via plain string arguments, not a provider alias.

🧱 Design Principles

Hardened default Behavior Opt-out / control
Domain encryption kms_key_identifier = null (AWS-owned key) Supply a CMK ARN from tf-mod-aws-kms — recommended for any PII-adjacent domain
SSO user assignment single_sign_on.user_assignment = "MANUAL" when SSO is configured — every user is an explicit, reviewable assignment Set user_assignment = "AUTOMATIC" — not recommended; document as a compliance exception
Domain/project deletion safety skip_deletion_check = false — refuses to delete a non-empty domain/project skip_deletion_check = true — cascades deletion; irreversible, use only with a documented decommissioning plan
No implicit user activation user_profiles defaults to {} — no principal is activated for the data portal unless explicitly named N/A — opt-in by design
Form/asset type lifecycle status = "ENABLED" by default, matching normal operating mode Set status = "DISABLED" during authoring/review; understand the one-way console step required before Terraform can delete an ENABLED form type

Other principles: one composite owns the full governance fabric so callers get a working, access-governed domain from a single call; every child collection is a map(object(...)) keyed by a caller-supplied stable string (never count), with cross-collection key references validated at the variable level; IAM roles, KMS keys, the Identity Center instance, and the underlying Glue/Redshift/Lake Formation data-plane resources are deliberately left to their dedicated sibling modules rather than duplicated here.


🚀 Runbook

terraform init -backend=false
terraform validate
terraform fmt -check
terraform plan # requires valid AWS credentials (profile / SSO / OIDC) + region
terraform apply
terraform output

⚠️ Always pin the module source with ?ref=v1.0.0, never a branch.


🧪 Testing

  • terraform init -backend=false && terraform validate — schema + reference integrity; the project_key/glossary_key/environment_profile_key cross-field validations surface a bad key reference here, not mid-apply.
  • terraform fmt -check — formatting.
  • terraform plan against a non-production account — confirm the domain's single_sign_on/kms_key_identifier posture matches intent before the first apply.
  • After apply, confirm project_ids/glossary_ids/environment_ids are populated as expected and that environment_provisioned_resources reflects the blueprint's actual output.
  • Exercise a throwaway sandbox project's skip_deletion_check = true path before relying on it for a real decommission.

💬 Example Output

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Outputs:

id = "dzd_54nakfrg9k6sri"
arn = "arn:aws:datazone:us-east-1:123456789012:domain/dzd_54nakfrg9k6sri"
portal_url = "https://dzd_54nakfrg9k6sri.datazone.us-east-1.on.aws"
root_domain_unit_id = "4z53za2s8g6zwr"
project_ids = { "lending" = "8h9j2k3l4m5n" }
glossary_ids = {}
tags_all = { "Environment" = "prod", "Owner" = "data-governance" }

🔍 Troubleshooting

  • ValidationException on domain create citing service_role: domain_version = "V2" requires service_role to be set — this module's own validation {} block should catch this at plan time; if you bypassed it via -target, supply the role and re-apply.
  • ConflictException deleting the domain or a project: the domain/project still owns child entities. Either destroy children first (the normal Terraform dependency order) or set skip_deletion_check = true deliberately, understanding it cascades.
  • terraform destroy on a form type hangs or errors: the form type's status is ENABLED — DataZone will not let Terraform delete an enabled form type. Set status = "DISABLED" in a prior apply, confirm it took effect, then remove the map entry.
  • Perpetual diff / unexpected replace on an environment after a user_parameters change: this is documented, correct behavior — user_parameters is FORCE-NEW. Plan the change as a deliberate recreation, not a routine update.
  • user_profiles entry "won't delete": there is no DeleteUserProfile API — removing the map entry deactivates the profile instead. This is expected DataZone behavior, not a module bug.
  • Tag drift warnings on the domain: a key collision between provider default_tags and this module's tags — resource tags win; tags_all shows the merged result. No other resource in this module can drift on tags since none are taggable.
  • Credential-chain failures (NoCredentialProviders / ExpiredToken): no valid credentials resolved. Set AWS_PROFILE, refresh SSO, or confirm the OIDC role assumption targets the correct account/Region.
  • AccessDeniedException on datazone:* despite a permissive-looking policy: check iam:PassRole scoping on domain_execution_role/service_role/manage_access_role_arn/provisioning_role_arn — DataZone needs to pass these roles through, and a missing PassRole grant surfaces as a DataZone-side access denial, not an IAM one.
  • Blueprint identifier lookups return nothing: confirm the aws_datazone_environment_blueprint data source is queried with managed = true for AWS-managed blueprints (Data Lake, etc.) and the correct domain_id — blueprint ids are domain-scoped.

🔗 Related Docs

  • Terraform Registry — hashicorp/aws: aws_datazone_domain, aws_datazone_project, aws_datazone_environment, aws_datazone_environment_profile, aws_datazone_environment_blueprint_configuration, aws_datazone_glossary, aws_datazone_glossary_term, aws_datazone_form_type, aws_datazone_asset_type, aws_datazone_user_profile
  • AWS — Amazon DataZone User Guide (domains, projects, environments, blueprints, business glossaries)
  • AWS — Enabling AWS IAM Identity Center for DataZone
  • AWS — Amazon DataZone quotas
  • tf-mod-aws-iam-role, tf-mod-aws-kms, tf-mod-aws-glue, tf-mod-aws-redshift, tf-mod-aws-lakeformation, tf-mod-aws-s3-bucket (sibling/consuming modules)

🧡 "Infrastructure as Code should be standardized, consistent, and secure."

Packages

 
 
 

Contributors

Languages