Provision Mac developer environments with Terraform using the dedicated provisioning script.
Prerequisites
For fully automated (passwordless) provisioning:
- SSH access to the target Mac
-
Passwordless sudo (optional) - configure via:
bash # On target Mac, add to /etc/sudoers.d/natilius %admin ALL=(ALL) NOPASSWD: /usr/sbin/softwareupdate, /usr/bin/xcode-selectOr let the provisioner create a temporary whitelist withSET_NOPASSWD=true. Or useSKIP_SUDO=trueto skip operations requiring sudo. -
Xcode Command Line Tools - The provisioner attempts automatic installation, but may require manual confirmation on first run.
Quick Start
resource "null_resource" "mac_setup" {
provisioner "remote-exec" {
inline = [
"curl -fsSL https://raw.githubusercontent.com/vincentkoc/natilius/main/scripts/terraform-provision.sh | SET_NOPASSWD=true bash -s devops"
]
}
}
Environment Variables
Control provisioning behavior with environment variables:
| Variable | Default | Description |
|---|---|---|
NATILIUS_BRANCH |
main |
Git branch to install from |
NONINTERACTIVE |
true |
Skip all prompts (auto-set) |
CI |
true |
CI mode (auto-set) |
SKIP_SUDO |
false |
Skip sudo operations |
SET_NOPASSWD |
false |
Create a temporary sudoers whitelist for this run |
SKIP_SYSTEM_UPDATES |
true (when NONINTERACTIVE) |
Skip macOS system updates to avoid volume-owner prompts |
DRY_RUN |
false |
Preview changes without applying |
QUIET_MODE |
false |
Minimal output |
Full Example
variable "mac_host" {
type = string
}
variable "mac_user" {
default = "admin"
}
variable "ssh_key_path" {
type = string
}
variable "profile" {
default = "devops"
}
resource "null_resource" "mac_setup" {
connection {
type = "ssh"
host = var.mac_host
user = var.mac_user
private_key = file(var.ssh_key_path)
timeout = "30m"
}
provisioner "remote-exec" {
inline = [
"curl -fsSL https://raw.githubusercontent.com/vincentkoc/natilius/main/scripts/terraform-provision.sh | SET_NOPASSWD=true bash -s ${var.profile}"
]
}
}
Custom Configuration
Upload a custom .natiliusrc before running setup:
resource "null_resource" "mac_setup" {
connection {
type = "ssh"
host = var.mac_host
user = var.mac_user
private_key = file(var.ssh_key_path)
}
# Upload custom config
provisioner "file" {
source = "configs/${var.team}.natiliusrc"
destination = "/Users/${var.mac_user}/.natiliusrc"
}
# Install and run
provisioner "remote-exec" {
inline = [
"curl -fsSL https://raw.githubusercontent.com/vincentkoc/natilius/main/install.sh | SKIP_RUN=true bash",
"NONINTERACTIVE=true SKIP_SUDO=true ~/.natilius/natilius.sh setup"
]
}
}
Skip Sudo Operations
For environments where sudo isn't available or desirable:
resource "null_resource" "mac_setup" {
provisioner "remote-exec" {
inline = [
"export SKIP_SUDO=true",
"curl -fsSL https://raw.githubusercontent.com/vincentkoc/natilius/main/scripts/terraform-provision.sh | bash -s minimal"
]
}
}
When SKIP_SUDO=true:
- CLI installed to ~/.local/bin instead of /usr/local/bin
- System-wide completions skipped
- Security module operations skipped
- macOS preferences requiring sudo skipped
Multiple Macs
Provision a fleet of Macs with different profiles:
variable "macs" {
type = map(object({
host = string
profile = string
}))
default = {
"dev-1" = { host = "192.168.1.10", profile = "developer" }
"dev-2" = { host = "192.168.1.11", profile = "devops" }
"build" = { host = "192.168.1.12", profile = "minimal" }
}
}
resource "null_resource" "mac_setup" {
for_each = var.macs
connection {
type = "ssh"
host = each.value.host
user = var.mac_user
private_key = file(var.ssh_key_path)
timeout = "30m"
}
provisioner "remote-exec" {
inline = [
"curl -fsSL https://raw.githubusercontent.com/vincentkoc/natilius/main/scripts/terraform-provision.sh | bash -s ${each.value.profile}"
]
}
}
output "provisioned_macs" {
value = [for k, v in var.macs : "${k}: ${v.host} (${v.profile})"]
}
Dry Run Mode
Preview what would be installed without making changes:
resource "null_resource" "mac_preview" {
provisioner "remote-exec" {
inline = [
"export DRY_RUN=true",
"curl -fsSL https://raw.githubusercontent.com/vincentkoc/natilius/main/scripts/terraform-provision.sh | bash -s devops"
]
}
}
Available Profiles
| Profile | Description |
|---|---|
minimal |
Essential tools only (git, brew, basic CLI) |
developer |
Full development environment |
devops |
Kubernetes, Terraform, cloud CLIs |
clawdbot |
AI agent machines (moltbot, Node.js 24, Chrome) |
See Profiles for details, or the Clawdbot Guide for AI agent provisioning.
Troubleshooting
Xcode CLT Installation Hangs
The provisioner attempts silent Xcode CLT installation. If it hangs:
1. SSH to the Mac manually
2. Run xcode-select --install and complete the GUI prompt
3. Re-run Terraform
Homebrew Requires Password
Homebrew installation may prompt for password on first run. Solutions:
- Pre-install Homebrew before Terraform
- Configure passwordless sudo for Homebrew, or use SET_NOPASSWD=true
- Use SKIP_SUDO=true (limited functionality)
macOS System Updates Prompt for Password
System updates require a volume-owner password and cannot be bypassed by sudoers. By default, system updates are skipped in non-interactive runs. To enable:
SKIP_SYSTEM_UPDATES=false
Timeout Issues
Increase the connection timeout for slow networks or large profiles:
connection {
timeout = "60m"
}