Skip to content

Terraform and OpenTofu

The Updawg provider manages an organization’s configuration from Terraform or OpenTofu: groups, policies, enrollment tokens, notification channels and rules. It is published as pez-solutions/updawg, and its full reference (every argument of every resource and data source) is on the Terraform Registry.

It needs an API token, so the Business or Enterprise plan.

terraform {
required_providers {
updawg = {
source = "pez-solutions/updawg"
version = "~> 0.1"
}
}
}
provider "updawg" {
org = "acme" # or UPDAWG_ORG
# The token comes from UPDAWG_API_TOKEN. Keep it out of .tf files.
}
Setting Environment Default
api_token UPDAWG_API_TOKEN none: required
org UPDAWG_ORG none: here or per block
api_url UPDAWG_API_URL https://api.updawg.net

The token acts as the person who issued it, within its scopes. Give it read, plus groups, policy, enrollment and integrations for the resources you manage. A token belongs to one organization, so managing several means one aliased provider block, and one token, for each.

Resource Scope
updawg_group groups
updawg_policy policy
updawg_enrollment_token enrollment
updawg_notification_channel integrations
updawg_notification_rule integrations

Data sources, read-only, with read: updawg_organization, updawg_hosts, updawg_group and updawg_policy.

resource "updawg_group" "web" {
name = "web"
label_selector = { tier = "web" }
}
resource "updawg_policy" "nightly" {
yaml = file("${path.module}/policies/nightly.yaml")
}

A policy’s YAML is sent to the validator during plan, so a document that doesn’t compile fails the plan with its line and column. An edit made in the portal shows up as a change to yaml, and applying puts your text back.

An enrollment token is shown once, when it is made, and Updawg keeps only a hash of it. So Terraform state is the only place the provider can keep the value to pass on, to cloud-init user data for example. The token’s value is in your state file.

We chose this over the alternatives on purpose:

  • Write-only attributes are for values you send in, like a password. The token is a value that comes back from the API, so there is nothing to make write-only.
  • An ephemeral resource would never be stored, but it would issue a new token on every plan, and it can only be passed to other ephemeral or write-only arguments. Instance user data and metadata are ordinary arguments, so the token would land in state anyway, just in a different resource.

So treat state as a secret: keep it in an encrypted backend with access control. Limit what a leaked value is worth, too:

resource "updawg_enrollment_token" "web" {
name = "web tier"
labels = { tier = "web" }
max_uses = 20
expires_at = "2027-01-01T00:00:00Z"
}
resource "aws_instance" "web" {
# ...
user_data = <<-EOT
#cloud-config
runcmd:
- curl -fsSL https://get.updawg.net | UPDAWG_TOKEN=${updawg_enrollment_token.web.token} sh
EOT
}

The API can’t change a token, so changing any of its arguments issues a new one and revokes the old. Hosts already enrolled are unaffected. A token you revoke in the portal is planned for re-issue.

There is no updawg_api_token resource. An API token can’t issue tokens, so a provider authenticated by one couldn’t create them.

A channel’s configuration (a webhook URL, a Slack webhook) is stored encrypted and never returned. Pass it as config_wo with a config_wo_version and it is never written to state (Terraform or OpenTofu 1.11 or later); raise the version to send a new one. config = jsonencode({...}) works on older versions, but the value is then kept in state.

resource "updawg_notification_channel" "ops" {
name = "ops"
kind = "slack"
config_wo = jsonencode({ webhook_url = var.slack_webhook })
config_wo_version = 1
}

Every resource imports with org/id, or a bare id for the provider’s organization:

Terminal window
terraform import updawg_group.web acme/grp_…

An imported enrollment token has no value, since the API showed it only once.