linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
Intro
Lessons
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Introduction
  • Lessons
  • How it works
  • Knowledge base
  • Cheat sheet
  • Capstone
  • Interview prep
home/terraform/kb/Providers/tf-utility-providers

kb/providers ── Providers ── intermediate

Utility providers: random, time, null, terraform_data

Providers that do not manage a cloud, they help HCL itself. `random` generates IDs and passwords. `time` handles delays and timestamp marks. `null` is the deprecated "non-resource" for triggers. `terraform_data` is the modern replacement for `null_resource`, built into Terraform. Each one removes a specific limitation of the declarative approach.

view as markdownaka: terraform-utility-providers, terraform-helper-providers

What a utility provider is

Most providers (aws, kubernetes, github) represent an external system: the API of a cloud or a service. A utility provider is not an API, it is language functionality: a random value generator, a timer, a trigger. These providers give you what HCL does not have built in.

All of them are official HashiCorp providers. Versions are stable, and breaking changes are rare.

random

Generates values that are deterministic within the state and random at creation time.

hcl
terraform {
  required_providers {
    random = {
      source  = "hashicorp/random"
      version = "~> 3.6"
    }
  }
}
resource "random_id" "suffix" {
  byte_length = 4
}
resource "aws_s3_bucket" "demo" {
  bucket = "linuxlab-${random_id.suffix.hex}"
}

random_id.suffix.hex is an 8-character hex string (4 bytes * 2). It is unique at creation, then fixed in state. Every later plan keeps the same value.

Variants

ResourceWhat it generatesMain use case
random_idBytes to hex/base64Unique name suffixes
random_stringA string of charactersNames, passwords (not great)
random_passwordA string with a minimum of character classesReal passwords (sensitive)
random_petA word pair like "honest-zebra"Human-readable names for the lab
random_uuidUUID v4Correlation, request IDs
random_integerA number in a rangeRandom port, index
random_shuffleShuffles a listAZ distribution

keepers: regenerate on change

hcl
resource "random_id" "suffix" {
  byte_length = 4
  keepers = {
    bucket_name = var.bucket_name
  }
}

If var.bucket_name changes, random_id is recreated, which gives a new hex value and a new bucket. The rule is "when N changes, generate a new value."

Without keepers, the value never changes without an explicit taint or -replace.

time

Time manipulation in HCL.

hcl
terraform {
  required_providers {
    time = {
      source  = "hashicorp/time"
      version = "~> 0.12"
    }
  }
}

time_static: the moment of creation

hcl
resource "time_static" "created_at" {}
resource "aws_s3_bucket" "demo" {
  bucket = "linuxlab-${formatdate("YYYYMMDD", time_static.created_at.rfc3339)}"
}

Records a timestamp in state on the first apply. After that it does not change. Useful for immutable marks (a bucket name that contains the creation date).

time_sleep: a delay during apply

hcl
resource "aws_iam_role" "app" {
  name = "app"
  # ...
}
resource "time_sleep" "wait_for_iam" {
  depends_on      = [aws_iam_role.app]
  create_duration = "30s"
}
resource "aws_eks_cluster" "demo" {
  depends_on = [time_sleep.wait_for_iam]
  # ...
}

The IAM role is created, but AWS is eventually consistent: until propagation finishes, EKS does not see the role. time_sleep waits 30 seconds between them.

This is a workaround, not a solution. If you can detect readiness through a data source, prefer that. If you cannot, sleep. The option is there.

time_rotating

hcl
resource "time_rotating" "cert" {
  rotation_days = 30
}

The id attribute changes every 30 days. Combined with random_password and keepers it gives you "rotate the password once a month." This is rarely useful, because secret managers (Vault) usually do it better.

null_resource (deprecated)

An old pattern, the "non-resource" from the null provider, used to hang triggers and a provisioner on something:

hcl
resource "null_resource" "bootstrap" {
  triggers = {
    version = var.script_version
  }
  provisioner "local-exec" {
    command = "./bootstrap.sh"
  }
}

It still works, but it is not used in new code. It has been replaced by terraform_data.

terraform_data (since TF 1.4+)

Built into Terraform, so you do not need a separate provider.

hcl
resource "terraform_data" "bootstrap" {
  input = var.script_version
  triggers_replace = [var.script_version]
  provisioner "local-exec" {
    command = "./bootstrap.sh"
  }
}

Advantages:

  • It does not require required_providers.null, because it is built in.
  • It can store a value (input): it becomes available through terraform_data.bootstrap.output.
  • triggers_replace is a list; when any item changes, the resource is recreated.

terraform_data as a "buffer" for a value

hcl
variable "image_tag" {
  type    = string
  default = "latest"
}
resource "terraform_data" "image_version" {
  input = var.image_tag
}
resource "aws_ecs_task_definition" "app" {
  # ...
  container_definitions = jsonencode([{
    name  = "app"
    image = "myrepo/app:${terraform_data.image_version.output}"
  }])
  lifecycle {
    replace_triggered_by = [terraform_data.image_version]
  }
}

When the tag changes, the task definition is force-recreated through replace_triggered_by. This is the "canary" pattern: tie the lifecycle of a heavy resource to a light indicator.

Which provider when

You needUse
A unique name suffixrandom_id
A strong passwordrandom_password
A creation timestamptime_static
To wait for AWS to "catch up"time_sleep
A recreate trigger when X changesterraform_data with triggers_replace
To run local-exec on changeterraform_data with a provisioner
To store a computed value between resourcesterraform_data.input/output

Pitfalls

  • random in LocalStack is the same as in real AWS. This provider does not depend on the cloud. Tests with LocalStack are fully valid for random/time/terraform_data.

  • random_password is NOT more secure than random_string. Both use crypto/rand. But random_password is marked sensitive and is not printed in logs. For secrets, always use random_password so the password does not leak through terraform output or plan logs.

  • A random_* value rotates only through -replace or keepers. A plain apply does not change it. This is a feature, not a bug: otherwise the bucket would get a different name on every apply.

  • time_sleep spends real apply time. 30s on every apply is bad for CI. Use it only when the alternative does not work.

  • null_resource is deprecated, not forbidden. You do not have to migrate old code; for new code, write terraform_data. There is no difference between them in behavior. The difference is in aesthetics and how well they are integrated.

  • A provisioner on terraform_data is a last resort. Idempotency is not guaranteed, errors are hard to handle, and sandboxing is zero. The replacement for local-exec is an external provider data source (see tf-archive-external-http).

  • A random resource lands in state with its value in the clear. This is obvious for random_id, but random_password is also stored in state in the clear; it is sensitive only in output. Protect your state.

§ команды

bash
terraform state show random_id.suffix

What is inside: byte_length, hex, b64_url, b64_std, dec. All forms of one value.

bash
terraform apply -replace=random_id.suffix

Force regeneration. This cascades and recreates everything that depends on it.

bash
terraform plan | grep terraform_data

See triggers_replace at work: what will force a recreate.

§ см. также

  • tf-provider-blockThe provider block: who Terraform will callThe provider block configures the plugin: which AWS region to talk to, which endpoints to use, which credentials to take. One block per provider is usually enough.
  • tf-resource-lifecyclelifecycle: controlling resource behaviorThe lifecycle block configures four behaviors: create_before_destroy (zero-downtime replacement), prevent_destroy (deletion guard), ignore_changes (ignore drift on specific attributes), replace_triggered_by (force replacement on an external signal).
  • tf-tls-providertls provider: keys, self-signed certificates, CSRsThe `tls` provider generates private keys and certificates right inside HCL. `tls_private_key` makes a key pair (RSA/ECDSA/Ed25519). `tls_self_signed_cert` produces a self-signed certificate. `tls_cert_request` builds a CSR you can hand to an external CA for signing. It is handy for test fixtures and SSH keys. For production, reach for a secrets manager, not state.
  • tf-archive-external-httparchive, external, http: outside data in HCLThree providers for pulling data into Terraform from the outside. `archive` packs files into a zip (lambda code, layers). `external` runs any script with JSON I/O. `http` does a GET request to a URL and parses the response. All three are data sources: they read, they do not write. They are useful where declarative HCL falls short.
Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies