Skip to content

microsoftexpert/tf-mod-vsphere-datacenter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

tf-mod-vsphere-datacenter

Terraform Provider Module type Resources Workflow

Standalone module that manages a single VMware vSphere datacenter — the top-level vCenter inventory container that holds hosts, clusters, datastores, networks, and virtual machines.


Overview

Why it matters: The datacenter is the root of the vCenter object graph. Almost every other module in this library — clusters, datastores, port groups, VMs — consumes a datacenter_id (MOID). This module creates that container deterministically, emits its MOID for sibling modules to wire against, and does nothing else: it owns the container, not its contents. Most real configuration lives in the child objects placed inside the datacenter, not on the datacenter itself.

The module wraps exactly one resource — vsphere_datacenter named this — and carries the universal tail (tags + custom_attributes), both of which the provider schema supports on this resource.


❤️ 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!


Family DAG — where this fits

Render the source below via the Mermaid Chart MCP (validated flowchart). This module is highlighted in #607078. tf-mod-vsphere-datacenter is a foundation root — it consumes no datacenter_id; it produces one. Its id / moid flows out to the datacenter_id input of nearly every other module in the library; tags flows in from tf-mod-vsphere-tag.

graph LR
 root(["foundation root — consumes<br/>no datacenter_id (produces one)"])
 tag["tf-mod-vsphere-tag<br/>tags"]
 dc["tf-mod-vsphere-datacenter"]
 cc["tf-mod-vsphere-compute-cluster<br/>datacenter_id"]
 ds["tf-mod-vsphere-nas-datastore /<br/>tf-mod-vsphere-vmfs-datastore<br/>datacenter_id"]
 dvs["tf-mod-vsphere-distributed-virtual-switch<br/>datacenter_id"]
 host["tf-mod-vsphere-host<br/>datacenter_id"]
 fold["tf-mod-vsphere-folder<br/>datacenter_id"]
 vm["tf-mod-vsphere-virtual-machine<br/>datacenter_id"]

 root --> dc
 tag -->|tags| dc
 dc -->|id / moid| cc
 dc -->|id / moid| ds
 dc -->|id / moid| dvs
 dc -->|id / moid| host
 dc -->|id / moid| fold
 dc -->|id / moid| vm

 style dc fill:#607078,color:#fff,stroke:#333,stroke-width:2px
Loading

Resource shape — what this builds

Keystone node in #00A1E0; the universal tail in #607078. This module wraps exactly one resource — vsphere_datacenter.this — with tags and custom_attributes applied to it. name and folder are immutable (force-new).

graph TD
 this["vsphere_datacenter.this<br/>(keystone — one datacenter)<br/>name + folder immutable (force-new)"]
 tail["universal tail<br/>tags (list) + custom_attributes (map)"]

 this --> tail

 style this fill:#00A1E0,color:#fff,stroke:#333,stroke-width:2px
 style tail fill:#607078,color:#fff
Loading

🔑 Required vCenter Privileges

Minimum starting set for the Terraform integration account. Validate with vCenter's Check Privileges feature and expand only as the provider docs require. Do not use the built-in Administrator account.

Privilege (ID) vCenter UI name Held on Reason
Datacenter.Create Datacenter → Create datacenter parent folder (or vCenter root) Create the datacenter object.
Datacenter.Delete Datacenter → Remove datacenter the datacenter Required for terraform destroy and for the destroy half of a force-new replacement (see Architecture / Troubleshooting).
InventoryService.Tagging.AttachTag vSphere Tagging → Assign or Unassign vSphere Tag the datacenter Only when tags is non-empty — attach the supplied vsphere_tag MOIDs.
Global.SetCustomField Global → Set custom attribute the datacenter Only when custom_attributes is non-empty — write attribute values. The attribute definitions must already exist.

Grant Datacenter.Create at the inventory node where the datacenter is created — the parent folder when folder is set, otherwise the vCenter root.


vSphere Prerequisites

  • vCenter Server is required. Datacenters are vCenter inventory objects; they cannot be created against a standalone ESXi host. custom_attributes likewise require vCenter and are unsupported on direct ESXi connections.
  • vCenter version: ≥ 7.0 (compatible with the vmware/vsphere 2.x provider line; 8.0 supported).
  • License: any licensed vCenter Server edition (vCenter Standard or higher). The datacenter object itself requires no special feature license.
  • Terraform: ≥ 1.12.0. Provider: vmware/vsphere ~> 2.0.
  • Preconditions:
  • If folder is set, the target inventory folder must already exist.
  • Any MOIDs passed in tags must reference existing vsphere_tag objects.
  • Any keys in custom_attributes must reference existing custom-attribute definitions (MOIDs), created out of band.
  • Provider/auth is configured by the caller, never by this module. Prefer environment variables (VSPHERE_SERVER, VSPHERE_USER, VSPHERE_PASSWORD); allow_unverified_ssl must be false in production.

Quick Start

Source is pinned to a tag (?ref=v1.0.0) — never a branch. The provider is configured by the caller. No credentials appear in module code. See examples/main.tf for a runnable two-datacenter example.

# Caller's root module — the provider is configured here, never inside the module.
# Authentication is supplied out of band via environment variables:
# VSPHERE_SERVER, VSPHERE_USER, VSPHERE_PASSWORD
provider "vsphere" {
  # allow_unverified_ssl = false # MUST remain false in production
}

# Minimal — datacenter at the vCenter root folder.
module "datacenter_prod" {
  source = "git::https://github.com/microsoftexpert/tf-mod-vsphere-datacenter?ref=v1.0.0"

  name = "dc-prod-01"
}

# With placement + the universal tail. vSphere tags are a LIST of tag MOIDs
# (not key/value); custom_attributes is a map of attribute MOID -> value string.
module "datacenter_research" {
  source = "git::https://github.com/microsoftexpert/tf-mod-vsphere-datacenter?ref=v1.0.0"

  name   = "dc-research-01"
  folder = "/research/" # optional; omit for the root folder

  tags = [module.tf_mod_vsphere_tag.tag_ids["environment"]]
  custom_attributes = {
    (data.vsphere_custom_attribute.cost_center.id) = "AGRI-1042"
  }
}

📚 Example Library

1 · Minimal — datacenter at the vCenter root
module "datacenter_prod" {
  source = "git::https://github.com/microsoftexpert/tf-mod-vsphere-datacenter?ref=v1.0.0"

  name = "dc-prod-01"
}
2 · Placed in an inventory folder
module "datacenter_east" {
  source = "git::https://github.com/microsoftexpert/tf-mod-vsphere-datacenter?ref=v1.0.0"

  name   = "dc-east-01"
  folder = "/regions/east"
}
3 · With environment tags from tf-mod-vsphere-tag
module "datacenter_staging" {
  source = "git::https://github.com/microsoftexpert/tf-mod-vsphere-datacenter?ref=v1.0.0"

  name = "dc-staging-01"
  tags = [
    module.tf_mod_vsphere_tag.tag_ids["environment"], # "staging"
    module.tf_mod_vsphere_tag.tag_ids["managed-by"],  # "terraform"
  ]
}
4 · With custom attributes for cost tracking
module "datacenter_dev" {
  source = "git::https://github.com/microsoftexpert/tf-mod-vsphere-datacenter?ref=v1.0.0"

  name = "dc-dev-01"
  custom_attributes = {
    (data.vsphere_custom_attribute.cost_center.id) = "INFRA-DEV"
    (data.vsphere_custom_attribute.owner.id)       = "platform-team"
  }
}
5 · Full metadata — folder + tags + custom attributes
module "datacenter_prod_full" {
  source = "git::https://github.com/microsoftexpert/tf-mod-vsphere-datacenter?ref=v1.0.0"

  name   = "dc-prod-02"
  folder = "/regions/us-east"

  tags = [
    module.tf_mod_vsphere_tag.tag_ids["environment"],
    module.tf_mod_vsphere_tag.tag_ids["region"],
  ]
  custom_attributes = {
    (data.vsphere_custom_attribute.cost_center.id) = "PROD-OPS"
    (data.vsphere_custom_attribute.owner.id)       = "cloud-platform"
    (data.vsphere_custom_attribute.tier.id)        = "tier-1"
  }
}
6 · Multiple datacenters with for_each (dev / staging / prod)
locals {
  datacenters = {
    dev     = { name = "dc-dev-01", folder = "/lifecycle/dev" }
    staging = { name = "dc-staging-01", folder = "/lifecycle/staging" }
    prod    = { name = "dc-prod-01", folder = "/lifecycle/prod" }
  }
}

module "datacenter" {
  source   = "git::https://github.com/microsoftexpert/tf-mod-vsphere-datacenter?ref=v1.0.0"
  for_each = local.datacenters

  name   = each.value.name
  folder = each.value.folder
  tags   = [module.tf_mod_vsphere_tag.tag_ids[each.key]]
}

output "datacenter_ids" {
  value = { for k, v in module.datacenter : k => v.id }
}
7 · Production datacenter with governance attributes
module "datacenter_prod_us" {
  source = "git::https://github.com/microsoftexpert/tf-mod-vsphere-datacenter?ref=v1.0.0"

  name   = "dc-prod-us-01"
  folder = "/production"

  tags = [
    module.tf_mod_vsphere_tag.tag_ids["environment"], # "production"
    module.tf_mod_vsphere_tag.tag_ids["compliance"],  # "fca-regulated"
    module.tf_mod_vsphere_tag.tag_ids["data-class"],  # "npi"
  ]
  custom_attributes = {
    (data.vsphere_custom_attribute.cost_center.id)   = "-PROD"
    (data.vsphere_custom_attribute.owner.id)         = "cloud-platform"
    (data.vsphere_custom_attribute.approved_by.id)   = "infra-review-board"
    (data.vsphere_custom_attribute.last_reviewed.id) = "2026-06-30"
  }
}
8 · Disaster recovery datacenter
module "datacenter_dr" {
  source = "git::https://github.com/microsoftexpert/tf-mod-vsphere-datacenter?ref=v1.0.0"

  name   = "dc-dr-01"
  folder = "/dr"

  tags = [
    module.tf_mod_vsphere_tag.tag_ids["environment"], # "dr"
    module.tf_mod_vsphere_tag.tag_ids["site-role"],   # "secondary"
  ]
  custom_attributes = {
    (data.vsphere_custom_attribute.dr_pair.id)   = module.datacenter_prod.name
    (data.vsphere_custom_attribute.rpo_hours.id) = "4"
  }
}
9 · Lab / sandbox datacenter (no folder)
module "datacenter_lab" {
  source = "git::https://github.com/microsoftexpert/tf-mod-vsphere-datacenter?ref=v1.0.0"

  name = "dc-lab-01"
  # folder omitted → created at the vCenter root (intentional for lab envs)
  tags = [module.tf_mod_vsphere_tag.tag_ids["environment"]]
}
10 · Nested folder path (two levels deep)
module "datacenter_tenant" {
  source = "git::https://github.com/microsoftexpert/tf-mod-vsphere-datacenter?ref=v1.0.0"

  name   = "dc-tenant-acme-01"
  folder = "/tenants/acme" # parent "/tenants" must already exist
}
11 · Import an existing datacenter into Terraform state
# Step 1 — add the import block and apply once.
import {
  to = module.datacenter_existing.vsphere_datacenter.this
  id = "/dc-legacy-01" # full inventory path from vCenter
}

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

  name = "dc-legacy-01"
  # folder omitted — the existing DC is at the root; add it if it's in a folder
}
# Step 2 — remove the import block after the first successful apply.
12 · Consume the id output in a downstream module
module "datacenter" {
  source = "git::https://github.com/microsoftexpert/tf-mod-vsphere-datacenter?ref=v1.0.0"
  name   = "dc-prod-01"
}

# Wire datacenter id to a folder module (typical dependency chain)
module "vm_folder" {
  source        = "git::https://github.com/microsoftexpert/tf-mod-vsphere-folder?ref=v1.0.0"
  path          = "Production/WebTier"
  type          = "vm"
  datacenter_id = module.datacenter.id # ← from this module
}
13 · Regional pair — primary and DR with paired attribute
module "datacenter_primary" {
  source = "git::https://github.com/microsoftexpert/tf-mod-vsphere-datacenter?ref=v1.0.0"
  name   = "dc-us-east-01"
  folder = "/regions/east"
  custom_attributes = {
    (data.vsphere_custom_attribute.dr_pair.id) = "dc-us-west-01"
  }
}

module "datacenter_dr_west" {
  source = "git::https://github.com/microsoftexpert/tf-mod-vsphere-datacenter?ref=v1.0.0"
  name   = "dc-us-west-01"
  folder = "/regions/west"
  custom_attributes = {
    (data.vsphere_custom_attribute.dr_pair.id) = module.datacenter_primary.name
  }
}
14 · Per-team datacenter with role-based access metadata
module "datacenter_platform" {
  source = "git::https://github.com/microsoftexpert/tf-mod-vsphere-datacenter?ref=v1.0.0"

  name   = "dc-platform-01"
  folder = "/teams/platform"

  tags = [module.tf_mod_vsphere_tag.tag_ids["team"]] # "platform-engineering"
  custom_attributes = {
    (data.vsphere_custom_attribute.admin_group.id)  = "vsphere-platform-admins"
    (data.vsphere_custom_attribute.ticket_queue.id) = "INFRA-PLATFORM"
  }
}
15 · End-to-end: datacenter → folder → resource pool
# Foundation layer: datacenter
module "datacenter" {
  source = "git::https://github.com/microsoftexpert/tf-mod-vsphere-datacenter?ref=v1.0.0"
  name   = "dc-prod-01"
}

# VM inventory folder inside the datacenter
module "prod_vm_folder" {
  source        = "git::https://github.com/microsoftexpert/tf-mod-vsphere-folder?ref=v1.0.0"
  path          = "Production"
  type          = "vm"
  datacenter_id = module.datacenter.id # ← wired from datacenter
}

# Resource pool — caller resolves parent_resource_pool_id from the cluster
module "prod_resource_pool" {
  source                  = "git::https://github.com/microsoftexpert/tf-mod-vsphere-resource-pool?ref=v1.0.0"
  name                    = "rp-prod-workloads"
  parent_resource_pool_id = data.vsphere_compute_cluster.prod.resource_pool_id
}


Inputs

Name Type Required Default Description
name string Yes Datacenter name; must be unique within its parent folder. Immutable — changing it forces destroy/recreate.
folder string No null Inventory folder path relative to the vCenter root (e.g. "/research/" or "parent/child"). null ⇒ created in the root folder. Immutable — changing it forces destroy/recreate.
tags list(string) No [] List of vsphere_tag ID strings (MOIDs) to attach. Not a key/value map. Obtain values from tf-mod-vsphere-tag outputs, e.g. tags = [module.tf_mod_vsphere_tag.tag_ids["environment"]].
custom_attributes map(string) No {} Map of custom-attribute MOID → value string. Requires vCenter (unsupported on direct ESXi). Obtain attribute MOIDs from the vsphere_custom_attribute data source.

Full type signatures (from variables.tf):

variable "name" { type = string } # required
variable "folder" { type = string, default = null } # optional, force-new
variable "tags" { type = list(string), default = [] } # universal tail
variable "custom_attributes" { type = map(string), default = {} } # universal tail

tags and custom_attributes use type = list(string) / map(string) with a plain default, not a top-level optional(...) wrapper — optional is only valid inside an object schema and fails terraform validate at the variable's top level.


Outputs

Name Type Description
id string (MOID) Primary output. Managed object ID of the datacenter. On provider v2.x the resource id is the MOID. Wire this into any sibling module that takes a datacenter_id input.
name string The display name of the datacenter.
moid string (MOID) The MOID exposed explicitly by the provider. Equivalent to id on v2.x; prefer it when you want intent-revealing code.

Design Principles

Every secure default below produces the safe result on an empty call; the caller must add characters to opt out.

Principle Secure / sane default Explicit opt-out Why
No credentials in the module module defines no vsphere_server / user / password / allow_unverified_ssl caller configures the provider "vsphere" block Auth and TLS trust are caller/provider decisions; keeps secrets out of module code and state.
TLS verification on allow_unverified_ssl = false (caller/provider) set true for lab/dev with self-signed certs only Production must verify vCenter certificates.
Empty tag set tags = [] supply vsphere_tag MOIDs Caller owns the tag taxonomy; no surprise attachments.
Empty custom attributes custom_attributes = {} supply MOID → value map No silent metadata writes; requires vCenter.
Explicit placement folder = null ⇒ root folder set folder to a path Placement is intentional, never hidden nesting.
Immutability documented name / folder are force-new A datacenter is a live container; the module surfaces (rather than hides) that renaming or moving it means destroy/recreate.

Runbook

Plan-only. This module system never runs terraform apply — a human reviews the plan and applies after review.

terraform init -backend=false # no remote state needed to validate the module
terraform validate # type-check the schema
terraform fmt -check # confirm canonical formatting
terraform plan # review the proposed changes
# NO terraform apply here — plan-only. A human reviews the plan and runs apply.

Troubleshooting

Symptom Cause Resolution
Plan shows destroy then create after I changed name or folder. Both arguments are immutable — the provider marks them force new resource. Expected. There is no in-place rename/move through this resource. To preserve inventory, rename/move in vCenter and reconcile with terraform import, or accept recreate only on an empty datacenter. Recreating destroys the container and everything Terraform manages beneath it.
Error: custom attributes are unsupported on direct ESXi connections (or datacenter create fails outright). VSPHERE_SERVER points at an ESXi host, not vCenter. Datacenters and custom attributes require vCenter. Point the provider at vCenter Server.
Permission to perform this operation was denied on create. The integration account lacks Datacenter.Create at the target inventory node. Grant Datacenter.Create on the parent folder (or vCenter root). Validate with Check Privileges. Do not fall back to the built-in Administrator account.
Tag attach fails / "tag not found". A value in tags is a tag name, not a MOID, or the tag does not exist, or the account lacks the tagging privilege. Pass real vsphere_tag MOIDs (e.g. from tf-mod-vsphere-tag.tag_ids), ensure they exist, and grant InventoryService.Tagging.AttachTag on the datacenter.
How do I bring an existing datacenter under management? The datacenter already exists in vCenter. Import by full inventory path: terraform import module.<name>.vsphere_datacenter.this /dc-name. After refresh, id/moid resolve to the MOID.

Related Docs


Plan-only module. No terraform apply is ever performed by this module system — a human reviews the plan and applies after review.

Packages

 
 
 

Contributors

Languages