Skip to main content

Assignment 1: Write a cloud-init config for a new VM

Goal: Write a valid #cloud-config user-data file that would bootstrap a fresh Linux VM into a usable, reachable machine — the same kind of first-boot IaC the lab uses on every VM.

Where: Do this on your own machine (or the Jumpbox). You'll write a file and validate it; you do not need to launch a real VM. The lab's per-VM cloud-init snippets (built on the golden template) are your reference for what a real one looks like.

Tasks

  1. Create a file named user-data.yaml. Make the very first line #cloud-config.
  2. Set the machine's hostname to intern-web01 and fqdn to intern-web01.example.com.
  3. Add a users entry that creates a user named omniops:
    • in the sudo group,
    • with shell /bin/bash,
    • with one ssh_authorized_keys entry — paste your own public key (an ssh-ed25519 ... or ssh-rsa ... line). Never paste a private key; never paste a password (use <REDACTED> if you must show the shape of one).
  4. Set package_update: true and install at least these packages: htop, curl, and python3 (Python is what a later Ansible run would need on the target).
  5. Use write_files to create /etc/motd with a one-line welcome message naming the host.
  6. Add a runcmd section with at least one command (for example, write the current date into /var/log/firstboot.log).
  7. Validate the syntax. cloud-init ships a validator:
    cloud-init schema --config-file user-data.yaml --annotate
    
    If cloud-init isn't installed, validate that it's at least well-formed YAML, e.g. with python3 -c "import yaml,sys; yaml.safe_load(open('user-data.yaml'))".
  8. In a comment block at the bottom of the file, write 2-3 sentences: which keys run once at first boot only, and what you would use instead for a change you need after the VM is already running.

Deliverable

The single file user-data.yaml, syntactically valid, containing all sections above, committed to your Git repo (Module 4) with a clear commit message.

Acceptance criteria — you're done when:

  • The file's first line is exactly #cloud-config.
  • hostname and fqdn are set as specified.
  • A users entry creates omniops with sudo, bash, and a real public SSH key (no private keys, no plaintext passwords).
  • package_update: true is set and htop, curl, python3 are all in packages.
  • write_files creates /etc/motd with a welcome line naming the host.
  • runcmd has at least one command.
  • The file passes cloud-init schema (or at least parses as valid YAML).
  • A bottom comment explains what runs only at first boot and what to use for day-2 changes.
  • The file is committed to Git with a meaningful message.

Hints

  • Indentation is everything in YAML — use spaces, never tabs. users: items are a list, so each starts with - name: ....
  • ssh_authorized_keys is a list, even if you have only one key.
  • To find your public key: cat ~/.ssh/id_ed25519.pub (the .pub file — the one safe to share).
  • Compare your file to a real lab per-VM snippet if you can open one; the structure should feel familiar.
  • Re-read Chapter 4 sections 3 and 4 for the anatomy of user-data and the "runs once" rule.

blocked for >~30 min after re-reading the lessons? Bring what you've tried to your mentor.