| name | hermes-workspace-ansible |
| description | Use when creating, modifying, testing, reviewing, or cleaning up Ansible automation in a workspace that contains an ansible/ directory. Keep all Ansible work contained there, record Python and collection dependencies, use the authorized SSH identity safely, and offer consent-based archival of completed project files. |
| version | 2.0.2 |
| author | Hermes Agent |
| license | MIT |
| platforms | ["linux"] |
| metadata | {"hermes":{"tags":["ansible","workspace","ssh","cleanup","collaboration"],"related_skills":["hermes-workspace-manager","hermes-workspace-git"],"related_skill_classifications":{"hermes-workspace-manager":"bundled","hermes-workspace-git":"bundled"}}} |
Hermes Ansible Workspace
Overview
Act as a considerate team member in workspaces that provide an ansible/ directory. Keep Ansible source, dependencies, generated artifacts, and task-specific scratch work inside that directory while work is active. Preserve existing conventions, do not disturb unrelated automation, and make completed work easy to review and archive. This skill is the specialized Ansible placement layer for hermes-workspace-manager: resolve <workspace>/ansible itself or one established project/subtree beneath it as the topic scope, according to the existing layout, and do not create a duplicate generic topic folder.
Use <workspace> for the active workspace supplied by Hermes. The active paths are:
- Ansible workspace:
<workspace>/ansible/
- Closed-project archive:
<workspace>/ansible_archive/
Never place Ansible project files elsewhere in the workspace merely for convenience.
When to Use
Use this skill for:
- Ansible playbooks, roles, inventories, variable files, templates, plugins, and configuration
- Ansible collections developed or installed for workspace work
- Python dependencies needed by Ansible modules, filters, inventory plugins, or helper scripts
- Connectivity and privilege-escalation checks against managed systems
- Pull requests or completed tasks involving files under
ansible/
- Cleanup or archival of completed Ansible work
Do not use it to move unrelated workspace content into ansible/, or to archive active/shared automation without explicit user agreement. A repository contribution that merely packages or documents this skill is not itself Ansible automation: keep every non-Ansible-native Git checkout under <workspace>/git/<repository-name>/ and archive it under <workspace>/git_archive/<repository-name>/ according to hermes-workspace-git. Classify by the repository's primary purpose, not by the current task or one Ansible-related file.
1. Inspect Before Working
- Read workspace instructions such as
<workspace>/AGENTS.md and inspect the existing ansible/ layout, configuration, inventories, dependency files, and local conventions.
- Load
hermes-workspace-manager when available and classify the task as Ansible-native work.
- Resolve
<workspace>/ansible/ or the smallest unambiguous existing project/subtree beneath it as the topic scope. Shared flat layouts may require the Ansible root; do not invent a project directory or create <workspace>/<topic-name> for the same work.
- Check version-control status before editing. Treat uncommitted or untracked files as potentially owned by another team member.
- Identify the smallest task-specific set of files. Reuse existing directories and naming conventions rather than creating parallel structures.
- Keep secrets out of playbooks, inventories, logs, requirement files, and commits. Use the project's established vault or secret-management mechanism.
This step is complete only when the applicable project instructions and pre-existing changes are known and every planned file has a destination under ansible/.
2. Keep All Ansible Work Under ansible/
Place artifacts according to the existing layout, normally:
ansible/
├── ansible.cfg
├── collections/
│ └── requirements.yml
├── filter_plugins/
├── inventory/
├── library/
├── playbooks/
├── requirements.txt
├── roles/
├── templates/
└── tests/
The current repository layout takes precedence over this example. Put temporary inventories, rendered test output, helper scripts, downloaded roles or collections, retry files, caches, and other task-specific artifacts inside an appropriate ignored location under ansible/; remove disposable artifacts before completion.
Do not write task artifacts to the workspace root, /tmp, or an unrelated repository directory when an ansible/-local location can serve the same purpose. If a tool inherently uses a system temporary directory, ensure it leaves no task-owned residue there.
3. Record Dependencies Reproducibly
Python modules
If Ansible work requires installing a Python package, add a direct, reproducible dependency entry to:
<workspace>/ansible/requirements.txt
Create the file if it does not exist. Preserve the file's existing version and hash conventions. Do not add packages that were merely inspected or are already supplied by the documented base environment unless the automation truly depends on them.
Install from the requirement file rather than leaving the environment as the only record:
python3 -m pip install -r ansible/requirements.txt
Prefer a project virtual environment located under ansible/ when one is needed, and keep it ignored by version control.
Ansible collections
If work requires an Ansible collection, record it in:
<workspace>/ansible/collections/requirements.yml
Preserve valid YAML and existing source/version conventions. Install from that manifest:
ansible-galaxy collection install -r ansible/collections/requirements.yml
If a role dependency is needed, use the repository's established role requirements file rather than silently treating it as a collection. Do not hand-edit downloaded collection contents in place; modify source maintained under ansible/ or pin the required upstream version.
Dependency work is complete only when a clean environment can discover every newly required direct dependency from the committed requirement files.
4. Connect to Managed Systems Safely
Default identity and privilege behavior
Use the agent's authorized private SSH key and connect as root by default. If the user specifies another remote account, use that account exactly as directed:
- Non-root account with sudo: set the requested remote user and use Ansible privilege escalation only where required (
become: true).
- Non-root account without sudo: do not assume or attempt root privileges; constrain tasks to that account's permissions.
- Root account: do not add unnecessary
become settings.
Treat authentication and privilege escalation as separate concerns. Never expose, copy, print, commit, or transmit the private key. Do not disable SSH host-key checking as a shortcut. Ask before accepting a changed host key.
Verify access
Use a non-interactive connectivity check before running changes, followed by an Ansible ping or check-mode operation appropriate to the inventory. Avoid commands that may prompt indefinitely.
If access succeeds, verify the actual remote user and required privilege path before making changes. If access fails, distinguish DNS/routing, host-key, authentication, account, and sudo failures from the command output rather than calling every failure a key problem.
If the target does not authorize the agent's key
Give the user the matching public key and state which remote account needs access. Use an existing .pub file that corresponds to the selected private key, or derive only the public key with ssh-keygen -y -f <private-key-path>. Never display the private key.
Provide installation help if needed. The user can run this on the target console or through an already authorized administrator. It works for both root and named accounts:
USER='CHANGE_ME'
KEY='PASTE_THE_PUBLIC_KEY_HERE'
id -u "$USER" >/dev/null 2>&1 || { printf 'Unknown user: %s\n' "$USER" >&2; exit 1; }
HOME_DIR="$(getent passwd "$USER" | cut -d: -f6)"
GROUP="$(id -gn "$USER")"
AUTHORIZED_KEYS="$HOME_DIR/.ssh/authorized_keys"
install -d -m 700 -o "$USER" -g "$GROUP" "$HOME_DIR/.ssh"
touch "$AUTHORIZED_KEYS"
chown "$USER:$GROUP" "$AUTHORIZED_KEYS"
chmod 600 "$AUTHORIZED_KEYS"
grep -qxF "$KEY" "$AUTHORIZED_KEYS" ||
printf '%s\n' "$KEY" >> "$AUTHORIZED_KEYS"
Resolve the account's primary group instead of assuming it has the same name as the user. The final append preserves the existing file and adds the key only when an identical line is absent.
Tell the user to verify the account name, home directory, SSH server configuration, and any centralized access-management policy before changing authorized_keys. After they grant access, retry the non-interactive connectivity check. Do not claim target verification until it succeeds.
5. Validate the Automation
Run checks from the workspace using the repository's configuration and documented commands. At minimum, where applicable:
- Parse YAML and requirement manifests.
- Run
ansible-playbook --syntax-check against changed playbooks with the intended inventory.
- Run linting if the project has
ansible-lint or another established checker.
- Exercise check mode when modules and target behavior support it.
- Run the narrow live path only with authorization, then verify the resulting system state.
- Re-run idempotent automation and confirm that the second run reports no unexpected changes.
State explicitly when check mode, idempotency, or live-system verification is unavailable. Validation is complete only when the changed files and the intended target behavior have been checked at a level appropriate to their impact.
6. Preserve Reusable Inventory Before Cleanup
Before deleting, replacing, or archiving inventory sources during cleanup, identify systems that Hermes successfully accessed, confirm which targets are intended to persist after cleanup, and consolidate only those systems' non-secret connection metadata into the active shared inventory when the workspace uses one. Explicitly exclude disposable labs, temporary VMs, short-lived test instances, decommissioned hosts, and provider-recyclable addresses. If persistence is uncertain, do not retain the target; ask or leave its inventory with the task-specific artifacts.
Preserve only verified facts needed for future access, such as:
- Inventory hostname or a sanitized alias appropriate to the repository's visibility
ansible_host when it differs from the inventory hostname
- Non-default SSH port
- Verified remote account
- Verified privilege model, including whether
become is available or intentionally unavailable
- Required Python interpreter when it was confirmed on the target
Do not copy passwords, private keys, vault values, tokens, raw SSH configuration, internal notes, or other credentials into inventory. Never infer that a host is reachable, that a key remains authorized, or that sudo works merely because an old inventory entry exists. Do not accept or replace SSH host keys automatically; preserve strict host-key checking and require normal fingerprint verification on the next connection when trust is absent or stale.
Merge retained hosts into the established inventory structure instead of creating duplicate inventory files. Preserve unrelated groups, variables, aliases, and comments. For public repositories, replace real infrastructure details with documented placeholders; keep real operational inventory only in an approved private or local workspace.
After consolidation, run ansible-inventory --graph without --vars using the workspace configuration and confirm that every retained host appears in the intended groups. Do not use ansible-inventory --list for this validation: it can expand host and group variables, including values obtained from dynamic inventory or an available vault, into captured output. Do not add --vars, print expanded inventory, or retain validation output in logs. Inventory retention records connection knowledge only; it is not proof of current connectivity and does not authorize a managed-target connection or change.
7. Clean Up and Archive Completed Work
Before completion
Remove disposable files created during the task from within ansible/, including retry files, caches, temporary inventories, rendered scratch output, and test-only downloads. Do not remove pre-existing or user-owned files. Keep files required to reproduce, operate, review, reconnect to previously accessed systems, or roll back the automation.
When a pull request is created
Do not archive immediately. Ask the user whether cleanup and archival should happen now, and offer a concrete list of what would be cleaned up or moved. For example:
- Disposable files to delete: exact paths and why they are no longer needed
- Completed project files to archive: exact source and proposed archive destination
- Files to retain: shared requirements, configuration, inventories, or reusable roles still needed by active work
A useful prompt is:
The pull request is created. Should I clean up the completed Ansible work now? I would remove <disposable paths> and move <completed paths> to <workspace>/ansible_archive/<relative paths>. I would retain <shared or active paths>.
Do not perform the archival until the user agrees.
Archive procedure after approval
- Recheck version-control status and confirm the offered source paths have not changed ownership or purpose.
- Create
<workspace>/ansible_archive/ and preserve each archived file's path relative to ansible/.
- Move only the approved files; do not copy them and leave duplicate active files behind.
- If the destination name already exists, append a local timestamp in
YYYYMMDD_HHMM form to the filename. Never overwrite an existing archive.
Example:
Source: ansible/playbooks/upgrade_os.yml
Destination: ansible_archive/playbooks/upgrade_os.yml
Conflict: ansible_archive/playbooks/upgrade_os.yml.20260720_1227
Generate the suffix at move time with date +%Y%m%d_%H%M; do not guess it. Create destination parent directories before moving. Requirement manifests and shared configuration should move only when they are exclusively part of the approved closed project; otherwise remove only obsolete project-specific entries after checking that active automation does not use them.
- Inspect
ansible/ and ansible_archive/ after the move, and show the resulting version-control status or file list. Confirm every approved path was moved and no unapproved file was changed.
Cleanup is complete only when disposable task-owned residue is gone, approved closed-project files are safely archived without overwrites, and remaining active automation is intact.
Common Pitfalls
- Working outside
ansible/. Move active Ansible artifacts into the established tree before continuing and remove accidental duplicates.
- Installing dependencies without recording them. Add Python packages and collections to their requirement manifests, then install from those files.
- Archiving automatically at PR creation. Offer exact cleanup actions and wait for user agreement.
- Overwriting an archive conflict. Append the real
YYYYMMDD_HHMM timestamp to the destination filename.
- Archiving shared files wholesale. Retain shared configuration and manifests unless the user approves moving them and no active automation depends on them.
- Assuming sudo. Follow the user's selected account and privilege model; verify it before execution.
- Treating every SSH failure as missing authorization. Diagnose network, host-key, account, and sudo errors first.
- Leaking credentials. Public keys may be shared for authorization; private keys and secrets must never be exposed.
- Discarding verified access metadata or retaining dead targets during cleanup. Consolidate non-secret, verified connection facts only for systems confirmed to persist; exclude disposable or provider-recyclable targets, validate with
ansible-inventory --graph without variables, and keep host-key trust separate.
- Claiming success after syntax check alone. Verify target behavior and idempotency when access and risk permit.
Verification Checklist