Add cloud-init.sh for provider install-script/user-data fields

IONOS Cloud Server, DigitalOcean, and Hetzner all offer an "install
script"/user-data field that runs as root with no TTY while the image is
still provisioning, so bootstrap.sh's interactive tail can't run there.

cloud-init.sh clones the repo unattended and drops a one-shot
/etc/profile.d hook that launches the normal whiptail setup.sh wizard on
the first interactive login, then removes itself.
This commit is contained in:
Claude
2026-08-10 02:29:02 +00:00
parent 5aef55a059
commit b2b4b6dd19
2 changed files with 84 additions and 0 deletions
+14
View File
@@ -33,6 +33,20 @@ the wizard — the USB can be unplugged once setup starts.
sudo bash bootstrap.sh --pat ghp_xxxxxxxxxxxxxxxxxxxx
```
Use a fine-grained read-only PAT scoped to just this repo (Contents: Read).
**Cloud provider install-script / user-data field (IONOS, DigitalOcean,
Hetzner, ...):** these run as root with no terminal attached while the
image is still being provisioned, so `bootstrap.sh`'s interactive hand-off
doesn't apply yet. Paste `cloud-init.sh` into that field instead:
```
https://raw.githubusercontent.com/outis1one/ubuntu-post-install/main/cloud-init.sh
```
It clones the repo in the background during provisioning and installs a
one-shot login hook. The provider boots Ubuntu 24.04, this runs unattended,
and by the time you SSH in the whiptail service menu is already waiting for
you — same experience as `bootstrap.sh`, just already started. Assumes a
root login (the default for all three providers above); see the comments in
`cloud-init.sh` if you've provisioned a separate sudo user instead.
The PAT is stripped from the stored remote URL after cloning.
## Usage
Executable
+70
View File
@@ -0,0 +1,70 @@
#!/bin/bash
# cloud-init.sh — payload for a cloud provider's "install script" / user-data
# field (IONOS Cloud Server image deploy, DigitalOcean droplet user-data,
# Hetzner Cloud user-data, etc). The provider runs this as root, unattended,
# with no TTY, while the box is still being provisioned — before you have
# ever logged in.
#
# It deliberately does NOT run the interactive wizard itself (there's no
# terminal for whiptail to talk to yet). Instead it does two things:
#
# 1. Clones this repo to /root/ubuntu-post-install (pulls if already there).
# 2. Installs a one-shot /etc/profile.d hook that launches setup.sh —
# the normal whiptail service menu — the first time you actually log
# in over SSH, then deletes itself so it never fires again.
#
# End result: the provider boots Ubuntu 24.04, this runs in the background,
# and by the time you SSH in the checklist menu is sitting there waiting —
# the same experience as running bootstrap.sh by hand, just already started.
#
# Usage: paste this file's raw URL into the provider's install-script /
# user-data field, e.g.
# https://raw.githubusercontent.com/outis1one/ubuntu-post-install/main/cloud-init.sh
#
# Assumes the provider logs you in as root (the default for IONOS Cloud
# Server, DigitalOcean droplets, and Hetzner Cloud server images). If you've
# provisioned a separate non-root sudo user instead, the hook won't reach
# you automatically — SSH in and run:
# sudo bash /root/ubuntu-post-install/setup.sh
set -euo pipefail
if [ "$(id -u)" -ne 0 ]; then
echo "cloud-init.sh must run as root — that's how provider install-script hooks already run it." >&2
exit 1
fi
REPO_URL="https://github.com/outis1one/ubuntu-post-install.git"
DEST="/root/ubuntu-post-install"
MARKER="/root/.ubuntu-post-install-pending"
HOOK="/etc/profile.d/99-ubuntu-post-install.sh"
command -v git >/dev/null 2>&1 || { apt-get update -qq && apt-get install -y git; }
if [ -d "$DEST/.git" ]; then
git -C "$DEST" pull --ff-only || true
else
git clone "$REPO_URL" "$DEST"
fi
touch "$MARKER"
# POSIX sh, not bash — /etc/profile.d/*.sh gets sourced by whatever shell
# the login uses, not necessarily bash.
cat > "$HOOK" << 'EOF'
# Installed by cloud-init.sh — launches the ubuntu-post-install wizard on
# the first interactive login, then removes itself so it never fires again.
MARKER="/root/.ubuntu-post-install-pending"
HOOK="/etc/profile.d/99-ubuntu-post-install.sh"
DEST="/root/ubuntu-post-install"
if [ -f "$MARKER" ] && [ -t 0 ] && [ "$(id -u)" -eq 0 ] && [ -f "$DEST/setup.sh" ]; then
rm -f "$MARKER" "$HOOK"
echo ""
echo "ubuntu-post-install: launching the setup wizard..."
echo ""
bash "$DEST/setup.sh"
fi
EOF
chmod 644 "$HOOK"
echo "cloud-init.sh: repo cloned to $DEST — the setup wizard will launch on first login."