Skip to content

OK-5484 Add bulk VM lifecycle script for Semaphore REST API#62

Open
relwell wants to merge 1 commit into
mainfrom
OK-5484-bulk-vm-lifecycle
Open

OK-5484 Add bulk VM lifecycle script for Semaphore REST API#62
relwell wants to merge 1 commit into
mainfrom
OK-5484-bulk-vm-lifecycle

Conversation

@relwell

@relwell relwell commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds semaphore/bulk_vm_lifecycle.py, a uv-runnable CLI that drives the Semaphore REST API to provision, configure, manage, and tear down groups of VMs sharing a common name prefix. Subcommands: deploy, list, manage, delete, provision-user, install-citrix. deploy parallelizes one task per generated <prefix>-<random hex> name and tracks them in a local manifest; manage/delete lean on the existing ^prefix regex match in vm.yml/list.yml so one Semaphore task covers the whole group; provision-user and install-citrix read the manifest and fan out per-VM tasks because their playbooks require exact name matches.
  • Adds semaphore/BULK_VM_LIFECYCLE.md with usage guidelines (prereqs, examples for every subcommand, REST flow, common flags, manifest format, operational notes including Citrix-specific prereqs in the Base VM Credentials env and the post-install reboot).
  • Cross-references the new doc from the root README.md and semaphore/README.md.

Test plan

  • uv run semaphore/bulk_vm_lifecycle.py --help and each <subcommand> --help parse cleanly
  • Against a running Semaphore instance, deploy --prefix demo --count 2 --vm-image <url> creates 2 VMs and writes semaphore/.bulk_vms_demo.json
  • list --prefix demo returns only the prefix-matched VMs
  • manage --prefix demo --state stopped then --state running toggles the group
  • provision-user --prefix demo --username u --password p provisions on each manifest VM
  • install-citrix --prefix demo installs the VDA on each manifest VM (requires citrix_installer_url + hostname_suffix set via configure_semaphore.py)
  • delete --prefix demo --yes removes the VMs and clears the manifest

🤖 Generated with Claude Code

Adds semaphore/bulk_vm_lifecycle.py for customers who need to provision,
configure, manage, and tear down groups of VMs sharing a common name
prefix without clicking through each Semaphore template by hand.

Subcommands hit the Semaphore REST API to drive the existing playbook
templates:

- deploy        Bulk-creates N VMs named <prefix>-<random hex>, in
                parallel, and writes a manifest at
                semaphore/.bulk_vms_<prefix>.json.
- list / manage / delete
                Use the underlying playbooks' built-in ^prefix regex
                match so a single Semaphore task covers the whole group.
                'delete' wraps 'manage --state absent' and clears the
                manifest on success.
- provision-user
                Reads the manifest and runs the Provision User to VM
                template per VM in parallel (the playbook uses exact
                vm_name matching).
- install-citrix
                Same pattern for the Install Citrix VDA template;
                installer URL and hostname suffix come from the existing
                Base VM Credentials environment.

Includes semaphore/BULK_VM_LIFECYCLE.md with usage guidelines, and
cross-references the new doc from both the root README and
semaphore/README.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@relwell relwell requested review from a team as code owners June 4, 2026 17:47

@ybenchouaf ybenchouaf left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a product perspective, this sufficiently addresses the need for core bulk VM lifecycle operations for now. Will validate with key customers and revisit the UI-driven flow (and potentially group primitives if required) if necessary.

Note: I'm rebuilding my sandbox environment, so I have not executed this against MSVDI hosts myself.

@spikeburton

Copy link
Copy Markdown
Contributor

Something I am thinking about - do we want this as a separate Python script? This means the user must run the script outside of the Semaphore UI, which defeats the purpose of having the UI. (I know we have a few scripts like this already, but they are mainly for bootstrapping/setup purposes, not common operations)

Should we consider having this as something that can be run via Ansible through the Semaphore UI instead?

It also depends on how commonly this will be used as well. If it's not often used, maybe the script is OK

@relwell

relwell commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

@spikeburton I'm inclined to keep it as a separate script, since I'm not sure it's the key flow we're accommodating for in the Semaphore UI. I'm totally down to graduate this to something we can run in Ansible once we've got a stronger signal. For now, this is a workaround to keep the UI simple while allowing this new flow.

@ybenchouaf

Copy link
Copy Markdown

Something I am thinking about - do we want this as a separate Python script? This means the user must run the script outside of the Semaphore UI, which defeats the purpose of having the UI. (I know we have a few scripts like this already, but they are mainly for bootstrapping/setup purposes, not common operations)

Should we consider having this as something that can be run via Ansible through the Semaphore UI instead?

It also depends on how commonly this will be used as well. If it's not often used, maybe the script is OK

We can assume this will be commonly run, at least during initial setup, for organizations with sizable fleets of hosts they need to deploy VMs on.

While this as a standalone python script is a step in the right direction for an improved UX for mass provisioning, I would love to see a way to execute this from within the UI.

@ybenchouaf

Copy link
Copy Markdown

@spikeburton I'm inclined to keep it as a separate script, since I'm not sure it's the key flow we're accommodating for in the Semaphore UI. I'm totally down to graduate this to something we can run in Ansible once we've got a stronger signal. For now, this is a workaround to keep the UI simple while allowing this new flow.

I'm okay with that approach. Let's get this out the door as it's an incremental, meaningful improvement that we can test with our first customers.

@spikeburton

Copy link
Copy Markdown
Contributor

Following the instructions in the README, the primary flow does not seem to work for me. Any idea what is going on?

Also - when there is an error - should we surface this? From what I can tell, there is no obvious way to troubleshoot when things go wrong

Screenshot 2026-06-24 at 4 12 13 PM

@spikeburton

spikeburton commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Update - found the error from the Semaphore UI. It may be worth mentioning this in the README so folks can troubleshoot as needed.

Does this vm_name variable need to be defined when invoked from the script?

Screenshot 2026-06-24 at 4 15 49 PM


`semaphore/bulk_vm_lifecycle.py` drives the Semaphore UI REST API to manage groups of Orka VMs that share a common name prefix. It is intended for customers who need to spin up, configure, manage, or tear down many VMs at once without clicking through the UI for each one — including day-2 operations such as provisioning a user across the group and installing the Citrix VDA on every VM.

Every VM created by `deploy` is named `<prefix>-<random>` (e.g. `demo-a1b2c3d4`). The script tracks the names it generated in a manifest at `semaphore/.bulk_vms_<prefix>.json`, which later subcommands read to target the same set of VMs.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest we add semaphore/.bulk_vms_*.json to the .gitignore

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants