Custom Configuration Management Tool
Build a small configuration management tool in Perl, a mini version of Ansible. You describe the desired state of your servers in YAML playbooks: this file should exist with this content, this package should be installed, this service should be running. The tool connects to each host over SSH, checks the current state, and changes only what's different, so running it twice changes nothing the second time (this property is called idempotency). It works on many hosts in parallel, has a --check mode that shows what would change without changing anything, and prints a clear report. You'll learn SSH automation with Net::OpenSSH, YAML parsing and validation, designing idempotent operations, and running work in parallel with Parallel::ForkManager.
- Estimate
- ~14h
- Steps
- 7
- Completed by
- 0
- Proposed by
- codeseed.app
Net::OpenSSH · YAML · Parallel::ForkManager
Project roadmap
- 01
Set up a test lab and the project
~2hSet up two or three test targets: Docker containers running sshd, or small virtual machines, reachable with an SSH key. Never test on real servers. Create the Perl project with a cpanfile listing Net::OpenSSH, YAML::XS and Parallel::ForkManager, and a bin/minicm script with Getopt::Long options: an inventory file, a playbook file, --check, --parallel N and --verbose. Done when: you can SSH into each test host with a key, and bin/minicm --help works.
- 02
Define the inventory and playbook format
~2hThe inventory YAML lists hosts with an address, user and group. A playbook YAML targets a group and lists tasks, each with a name, a type (file, package or service) and its parameters, such as path, content, mode, owner, package name, and state installed or absent. Validate every task on load and report errors with the task name, before connecting anywhere. Done when: a valid playbook loads, and a broken one lists all its problems.
- 03
Run commands over SSH
~2hWrite a Host class around Net::OpenSSH that connects with a timeout, runs a command and returns stdout, stderr and the exit code, and uploads a file through scp_put. Use sudo when a task needs root. Any connection failure marks the host as unreachable with the error message, instead of crashing the tool. Done when: running hostname on every test host through the Host class works, and an offline host is reported clearly.
- 04
Implement idempotent task types
~3hEach task type has a check step and an apply step. file compares the content's SHA-256 and the mode and owner on the host with the desired ones. package queries the package manager (dpkg-query or rpm -q) before installing or removing. service asks systemctl is-active and is-enabled. apply runs only when check finds a difference, and returns ok, changed or failed. Done when: running a playbook twice reports changes the first time and none the second.
- 05
Add check mode
~1hWith --check, run only the check step for every task and report what would change (for example, file /etc/motd: content differs), without modifying anything on the host. Done when: after editing a file by hand on a test host, --check reports that exact difference and leaves the file untouched.
- 06
Run hosts in parallel
~2hUse Parallel::ForkManager to process up to --parallel hosts at the same time, with each child working on one host and sending its results back to the parent through the run_on_finish data structure. Tasks within one host always run in order. Done when: a playbook runs on all test hosts in parallel, and the results of every host are collected.
- 07
Report results and test
~2hPrint a report per host and task (ok, changed, failed, skipped) and a final recap like Ansible's: counts per host. Exit with code 2 if any task failed and 0 otherwise. Write tests with Test::More for playbook validation and the check logic, using a fake Host object instead of real SSH. Done when: prove -l t/ passes, and a real run prints a correct recap.
Ready to build this?
Get a GitHub repo and start building. Your AI reviewer checks each step as you go.
Tech stack