No description
  • Go 99.8%
  • HCL 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-06-11 10:49:05 -04:00
.github Publish ubuntu v0.1.84 2026-05-22 15:09:25 +00:00
.release Publish ubuntu v0.1.84 2026-06-03 19:27:45 +00:00
build [COMPLIANCE] Add/Update Copyright Headers 2026-05-25 03:41:37 +00:00
docs Publish ubuntu v0.1.85 2026-06-05 18:20:55 +00:00
executor [COMPLIANCE] Add/Update Copyright Headers 2026-05-25 03:41:37 +00:00
generated/providers/ubuntu/runtimeassets Publish ubuntu v0.1.84 2026-06-03 19:27:45 +00:00
guest [COMPLIANCE] Add/Update Copyright Headers 2026-05-25 03:41:37 +00:00
providers Publish ubuntu v0.1.85 2026-06-05 20:52:38 +00:00
shared/hostrpc [COMPLIANCE] Add/Update Copyright Headers 2026-05-25 03:41:37 +00:00
version Publish ubuntu v0.1.85 2026-06-05 18:20:55 +00:00
CODEOWNERS Publish ubuntu v0.1.84 2026-06-03 19:27:45 +00:00
CONTRIBUTING.md Publish ubuntu v0.1.82 2026-05-19 01:09:01 +00:00
go.mod Publish ubuntu v0.1.85 2026-06-05 20:52:38 +00:00
go.sum Publish ubuntu v0.1.84 2026-05-22 16:50:43 +00:00
LICENSE Publish ubuntu v0.1.82 2026-05-18 19:24:07 +00:00
main.go Publish ubuntu v0.1.84 2026-06-03 19:27:45 +00:00
main_test.go Publish ubuntu v0.1.84 2026-06-03 19:27:45 +00:00
README.md Publish ubuntu v0.1.84 2026-05-21 22:35:54 +00:00
terraform-registry-manifest.json Publish ubuntu v0.1.84 2026-05-21 22:35:54 +00:00

Terraform Ubuntu Provider

This repository contains the ubuntu provider.

Warning

This provider is in alpha and is not officially supported.

Issues, pull requests, behavior changes, docs generation changes, and release logic changes all belong upstream.

What This Provider Does

The Ubuntu provider lets Terraform manage operating-system state on a remote Ubuntu host over SSH. You can declare the host configuration you want Terraform to maintain and then plan and apply those changes the same way you do with the rest of your infrastructure.

It is primarily aimed at systems configuration on the host itself: packages, service configuration, files, trust material, scheduled jobs, users, firewall rules, and other long-lived operating-system settings.

Typical use cases include:

  • installing and removing packages
  • writing or reconciling files on the host
  • enabling and restarting services
  • managing users, groups, cron, certificates, firewall rules, and other host-level configuration

Architecture and Design

The Ubuntu provider is built on top of the Terraform Plugin Framework and uses SSH to connect to the target host(s). It executes commands and manages files to enforce the desired state declared in Terraform configurations.

It pushes a small, machine-compiled binary to the target host, which then executes operations necessary to enforce the desired state. The executor is removed as soon as the operations are complete. No host tools or agents are required beyond SSH.

The provider can manage many host connections in parallel, and it is designed to be resilient to transient SSH failures and host reboots.

It provides post-quantum safety capabilities, even if the underlying SSH connection cannot. See provider configuration for details.

Simple Examples

Example 1: Connect to a host and install a package

terraform {
  required_providers {
    ubuntu = {
      source  = "hashicorp/ubuntu"
      version = "~> 0.1"
    }
  }
}

provider "ubuntu" {
  ssh {
    user        = "terraform"
    private_key = var.ssh_private_key_pem
  }

  default_target {
    target = var.host_address
    port   = 22
  }
}

resource "ubuntu_package" "nginx" {
  name         = "nginx"
  update_cache = true
}

If you use Vault's SSH secrets engine, let Vault issue a short-lived keypair and signed certificate at runtime, then pass both into the provider as ephemeral values so they stay out of Terraform state and plan files.

provider "vault" {}

ephemeral "vault_generic_endpoint" "runner_ssh" {
  path         = "ssh-client-signer/issue/terraform"
  write_fields = ["private_key", "signed_key"]
}

provider "ubuntu" {
  ssh {
    user        = "terraform"
    private_key = ephemeral.vault_generic_endpoint.runner_ssh.write_data["private_key"]
    certificate = ephemeral.vault_generic_endpoint.runner_ssh.write_data["signed_key"]
  }
}

Example 2: Configure nginx as a routing service

resource "ubuntu_user" "nginx_runtime" {
  name        = "edge-router"
  system      = true
  home        = "/nonexistent"
  create_home = false
  shell       = "/usr/sbin/nologin"
  comment     = "nginx routing service account"
}

resource "ubuntu_file" "nginx_main" {
  path    = "/etc/nginx/nginx.conf"
  content = templatefile("${path.module}/templates/nginx.conf.tftpl", {
    runtime_user = ubuntu_user.nginx_runtime.name
  })
  owner   = "root"
  group   = "root"
  mode    = "0644"

  depends_on = [ubuntu_package.nginx]
}

resource "ubuntu_file" "nginx_route" {
  path    = "/etc/nginx/conf.d/internal-api.conf"
  content = <<-EOT
    server {
      listen 80;
      server_name _;

      location /api/ {
        proxy_pass         http://10.0.2.15:8080;
        proxy_http_version 1.1;
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
      }
    }
  EOT
  owner   = "root"
  group   = "root"
  mode    = "0644"
}

resource "ubuntu_systemd_unit" "nginx" {
  name             = ubuntu_package.nginx.name
  enabled          = true
  state            = "running"
  reload_on_change = true
  reload_triggers = [
    ubuntu_file.nginx_main.digest,
    ubuntu_file.nginx_route.digest,
  ]
}

Template file at templates/nginx.conf.tftpl:

user ${runtime_user};
worker_processes auto;
pid /run/nginx.pid;

events {
  worker_connections 1024;
}

http {
  include /etc/nginx/mime.types;
  default_type application/octet-stream;
  sendfile on;
  keepalive_timeout 65;
  include /etc/nginx/conf.d/*.conf;
}

Example 3: Reboot a host on demand

action "ubuntu_restart_host" "maintenance_reboot" {
  config {
    name            = "maintenance_reboot"
    reason          = "Apply maintenance changes that require a reboot"
    timeout_seconds = 600
    settle_seconds  = 15
  }
}

Invoke it as a one-shot action when you actually want the reboot:

terraform apply -invoke='action.ubuntu_restart_host.maintenance_reboot'

Terraform plans and runs only that action when -invoke is set, which keeps reboots separate from the normal converge loop.

Start Here

  • Read docs/index.md for the provider docs.
  • Use the examples above as a starting point for your first configuration.

Build

go build .

Install

terraform {
  required_providers {
    ubuntu = {
      source  = "hashicorp/ubuntu"
      version = "~> 0.1"
    }
  }
}

Contents

  • Provider documentation: docs/