Gardience Documentation gardience.com →
Docs / Clients

Terraform

Terraform builds the machine; Gardience keeps it patched, hardened and audited from its first boot. The Gardience provider for Terraform (and OpenTofu) manages:

Resource or data sourceWhat it does
gardience_enrollmentOpens an enrollment and gives first-boot scripts (linux_script, windows_script) for user_data. Destroying it closes the enrollment.
gardience_bootstrap_script (data source)A first-boot script for one machine: its name in Gardience and extra groups.
gardience_machineWaits until a machine has joined, and removes it from the fleet when it is destroyed.
gardience_node_groupA custom node group.
gardience_stateA saved state, checked by the configuration keeper before it is published.
gardience_event_ruleAn event rule: restart a stopped service, warn about a filling disk, or apply a state to each new machine.
gardience_runbookA runbook: one Salt function with a form. Saving never runs it.
gardience_change_windowA change window: changes to a node group wait until it is open.
gardience_scheduleA schedule that runs a runbook or workflow on set days. It starts turned off: an admin turns it on in the console.
gardience_machine (data source)Whether a machine has joined, its OS and whether it is compliant.

Everything the provider changes is recorded in the audit log under the key's owner, with "via API key NAME".

Before you start

  1. An admin creates a configure API key: Settings > API keys, scope Configure (Terraform, admins). It manages configuration only and never runs anything on the fleet. See API keys and the REST API.
  2. New machines must reach the Gardience server on the Salt ports (4505 and 4506, or the ports set at install), and download the Salt agent from github.com and packages.broadcom.com. For offline sites, bake the agent into the image and keep the script for the rest.
  3. Give Terraform the server's address and the key, for example as environment variables:
export GARDIENCE_URL=https://gardience.example.com
export GARDIENCE_API_KEY=gdn_...        # from your secrets manager

If the server uses a certificate from a private certificate authority, set ca_cert_file (or GARDIENCE_CA_CERT_FILE) to that authority's PEM file.

Example: web servers that configure themselves

terraform {
  required_providers {
    gardience = { source = "gardience/gardience" }
  }
}

provider "gardience" {}

# machines built with this enrollment join on their own and land in the "web" group
resource "gardience_enrollment" "web" {
  label  = "web tier (terraform)"
  hours  = 2160          # 90 days; Terraform opens a new one when it expires
  groups = ["web"]
}

# how a web server should be
resource "gardience_state" "web_base" {
  name    = "web_base"
  content = <<-YAML
    nginx:
      pkg.installed: []
      service.running:
        - enable: True
        - require:
          - pkg: nginx
  YAML
}

# the moment a new machine in "web" joins, apply web_base to it
resource "gardience_event_rule" "web_join" {
  id    = "web-join"
  kind  = "join"
  scope = "group"
  group = "web"
  state = gardience_state.web_base.name
  depends_on = [gardience_enrollment.web]
}

# keep nginx running everywhere it is watched
resource "gardience_event_rule" "nginx" {
  id      = "keep-nginx"
  kind    = "service"
  os      = "linux"
  service = "nginx"
}

data "gardience_bootstrap_script" "web" {
  count         = 3
  enrollment_id = gardience_enrollment.web.id
  os            = "linux"
  node          = "web-${count.index + 1}"
}

resource "aws_instance" "web" {
  count         = 3
  ami           = var.ami
  instance_type = "t3.small"
  user_data     = data.gardience_bootstrap_script.web[count.index].content
  tags          = { Name = "web-${count.index + 1}" }
}

# wait until each machine has joined; on destroy, remove it from the fleet
resource "gardience_machine" "web" {
  count      = 3
  name       = "web-${count.index + 1}"
  depends_on = [aws_instance.web]
}

On terraform apply, each instance boots, installs the pinned and hash-checked Salt agent, joins Gardience without anyone approving it, lands in the web group, and gets web_base within seconds of joining. From then on it is in every scan, patch run, baseline and report like any other machine. On terraform destroy, the machines are removed from the fleet.

For Windows, use os = "windows" and wrap the script for your platform (on AWS: user_data = "<powershell>${data.gardience_bootstrap_script.win.content}</powershell>").

Bring an existing setup under Terraform

Gardience writes its current configuration as Terraform, with an import block for every item, so you can adopt what admins already built in the console without recreating it:

curl -fsS -H "Authorization: Bearer $GARDIENCE_API_KEY" "$GARDIENCE_URL/gardience/api/v1/export" > gardience.tf
terraform plan   # shows each item to import, and no changes
terraform apply  # adopts them

It covers node groups, saved states, event rules, custom runbooks, change windows, and schedules. It never writes secrets, API keys, enrollments, users or roles, or states kept in Git (those stay in Git). Each export is in the audit log.

Good to know

Without Terraform

The same API works from any tool. See API keys and the REST API for the endpoints, and Enrollment tokens for first-boot scripts from the console.

Gardience by Bouziden Management LLC.