Updating the Ansible Code
The YAML editor holds the tasks the plugin runs. Plugin YAML in Rogue Arena is standard Ansible with a few load-bearing rules that differ from how you’d write a standalone playbook. Read this page carefully. The differences are small but strict, and most first-build failures come from missing one of them.
Rule 1: Task List Only, No Playbook Header
Section titled “Rule 1: Task List Only, No Playbook Header”This is the biggest difference between a Rogue Arena plugin and a normal Ansible playbook. Your YAML is a flat task list. At runtime the platform wraps it in a play (it sets hosts, vars, and the tasks: collection). You don’t write the wrapper.
Correct (start directly with - name: tasks):
- name: Create staging dir ansible.windows.win_file: path: "C:\\PluginSetup" state: directory
- name: Stage installer from vault ansible.windows.win_copy: src: my-installer.exe dest: "C:\\PluginSetup\\my-installer.exe"Wrong (do not include playbook structure):
---- hosts: all tasks: - name: Create staging dir ...Leave out the --- at the top, the - hosts: line, and the outer tasks: key. If you paste a snippet from the public Ansible docs that includes a playbook header, remove the header before you save.
Everything runs inline. You cannot use import_tasks or include_tasks, or reference separate YAML files. The whole plugin is one continuous task list.
Rule 2: Stage From the Vault Before You Install
Section titled “Rule 2: Stage From the Vault Before You Install”Non-apt resources (.exe, .msi, .deb, Docker images, git tarballs, ISOs, etc.) live in the plugin vault. You upload them once during development, and they’re pulled onto the target VM at runtime. You manage the vault in the Resources panel.
- name: Stage VLC installer from plugin vault ansible.windows.win_copy: src: vlc-3.0.20-win64.exe dest: "C:\\PluginSetup\\vlc-3.0.20-win64.exe"For copies on the target VM later in the play, such as mirroring a config to a second path, moving a staged file into place, or duplicating a file within the machine, use a shell-level copy:
- Windows:
Copy-Iteminsideansible.windows.win_powershellorwin_shell - Linux:
cp/mvinsideansible.builtin.shell
To move files between two VMs, run a shell command on the source VM (robocopy, scp, or Copy-Item over a UNC path).
Apt is the runtime exception. Rogue Arena ships an apt mirror inside every Architect scenario, with most popular repositories already set up. On Linux machines, apt install what you need without adding repositories first:
- name: Install Docker apt: pkg: - docker-ce - docker-ce-cli - containerd.io state: present update_cache: yesUse the vault for anything the mirror doesn’t have. Downloading from the public internet at runtime (wget, curl, Invoke-WebRequest to the open internet) is a failure state. Fetch those files into the vault during development.
Rule 3: Write Text Files Inline Via content:
Section titled “Rule 3: Write Text Files Inline Via content:”Scripts, configs, and other text files don’t need to go in the vault. Use win_copy / copy with a content: block to write them directly onto the target:
- name: Write PowerShell setup script ansible.windows.win_copy: content: | $ErrorActionPreference = 'Stop' Write-Host "Running setup" # ... dest: 'C:\PluginSetup\setup.ps1'- name: Write bash setup script ansible.builtin.copy: content: | #!/bin/bash set -e echo "Running setup" dest: /tmp/plugin-setup/setup.sh mode: '0755'Keep text in the YAML, and save the vault for binaries.
Rule 4: Use the Right Module Collection
Section titled “Rule 4: Use the Right Module Collection”Windows modules come from two collections, and using the wrong namespace fails the build:
ansible.windows.*: core Windows modules (bundled with Ansible)community.windows.*: extended Windows modules (community-maintained)
Common mistakes:
| Wrong | Right |
|---|---|
ansible.windows.win_lineinfile | community.windows.win_lineinfile |
ansible.windows.win_firewall_rule | community.windows.win_firewall_rule |
ansible.windows.win_unzip | community.windows.win_unzip |
Always use the fully qualified collection name (FQCN). The platform mapping at a glance:
| Windows | Linux |
|---|---|
ansible.windows.win_copy | ansible.builtin.copy |
ansible.windows.win_file | ansible.builtin.file |
ansible.windows.win_stat | ansible.builtin.stat |
ansible.windows.win_shell | ansible.builtin.shell |
ansible.windows.win_powershell | n/a (use shell) |
ansible.windows.win_reboot | ansible.builtin.reboot |
ansible.windows.win_service | ansible.builtin.service |
Rule 5: Windows Paths Use Doubled Backslashes
Section titled “Rule 5: Windows Paths Use Doubled Backslashes”In YAML double-quoted strings, a single backslash starts an escape sequence (\t = tab, \n = newline, \V = parse error). The house style is double-quoted Windows paths with \\:
- name: Check file ansible.windows.win_stat: path: "C:\\Program Files\\MyApp\\app.exe"Exception: inside script: | blocks, content passes raw to PowerShell. Use normal single-backslash paths:
- name: PowerShell script ansible.windows.win_powershell: script: | $path = "C:\scripts\file.exe" Copy-Item -Path $path -Destination "C:\temp\"Rule 6: Default to No Privilege Escalation
Section titled “Rule 6: Default to No Privilege Escalation”By default, plugins run as SYSTEM on Windows and root on Linux. Don’t use become, ansible_become, or runas unless you need to run in a specific user’s context.
You do NOT need become for (this list is long, so learn it well):
- Writing files to system paths (
C:\,C:\temp,/etc/,/opt/, etc.) - Modifying
HKLM(HKEY_LOCAL_MACHINE) registry - Installing software system-wide
- Managing services
- Creating directories anywhere on the system
- Active Directory operations from a domain-joined machine (SYSTEM has domain access)
You DO need become/runas for (narrow cases):
- Modifying user-specific registry hives (
HKCU, or HKEY_CURRENT_USER) - Touching user profile folders that SYSTEM can’t read
- Running processes that must appear in a user’s session context
- AD operations that require a specific user identity (Domain Admin, etc.)
Example: an HKCU registry edit (the rare case where runas is needed):
- name: Change user wallpaper ansible.windows.win_powershell: script: | Set-ItemProperty -Path "HKCU:\Control Panel\Desktop" -Name Wallpaper -Value "C:\wallpaper.jpg" RUNDLL32.EXE user32.dll,UpdatePerUserSystemParameters vars: ansible_become: true ansible_become_method: runas ansible_become_user: "{{ username }}" ansible_become_password: "{{ user_password }}"Example: an AD operation that needs a Domain Admin token:
- name: Run as Domain Admin ansible.windows.win_powershell: script: | Import-Module ActiveDirectory # ... AD operations vars: ansible_become: true ansible_become_method: runas ansible_become_user: "Administrator@{{ DomainNameFQDN }}" ansible_become_password: "{{ domain_admin_password }}"Rule 7: Normalize CSV Inputs
Section titled “Rule 7: Normalize CSV Inputs”CSV parameters arrive as raw strings that often include a BOM and Windows carriage returns. Strip both first:
- name: Normalize CSV ansible.builtin.set_fact: my_csv_clean: "{{ my_csv_param | default('') | regex_replace('^\\ufeff','') | regex_replace('\\r','') | trim }}"
- name: Parse CSV to list of objects ansible.builtin.set_fact: my_records: "{{ my_csv_clean | community.general.from_csv }}"For complex processing (bulk AD creation, etc.), write the cleaned CSV to a file and parse it with PowerShell Import-Csv.
Rule 8: Always Validate at the End
Section titled “Rule 8: Always Validate at the End”A green build doesn’t prove the install worked. Add checks after every install or configuration step:
- name: Check application installed ansible.windows.win_stat: path: "C:\\Program Files\\MyApp\\app.exe" register: app_check
- name: Fail if not found ansible.builtin.fail: msg: "Installation failed — application not found" when: not app_check.stat.existsService checks, port polls, and retry-until-condition loops follow the same idea. A plugin that finishes silently and leaves the box half-configured is the worst kind of bug.
Rule 9: Idempotency With changed_when
Section titled “Rule 9: Idempotency With changed_when”Track whether each operation changed anything. PowerShell scripts output JSON with a changed flag, and Ansible reads it through changed_when:
- name: Configure setting (idempotent) ansible.windows.win_powershell: script: | $changed = $false if ($currentValue -ne $desiredValue) { Set-Something -Value $desiredValue $changed = $true } [pscustomobject]@{ changed = $changed } | ConvertTo-Json register: config_result changed_when: config_result.output is search('"changed"\s*:\s*true')Re-running a finished plugin should be a no-op. If the second run reports changes, you have a bug.
Complete Example: Simple App Install
Section titled “Complete Example: Simple App Install”This example puts the standard pattern together: stage from the vault, install, clean up, and validate. (Some plugins also start with a set_fact block that keeps version numbers and staging paths in one place. It’s optional but common.)
- name: Create staging folder ansible.windows.win_file: path: "C:\\PluginSetup" state: directory
- name: Stage VLC installer from plugin vault ansible.windows.win_copy: src: vlc-3.0.20-win64.exe dest: "C:\\PluginSetup\\vlc-3.0.20-win64.exe"
- name: Install VLC silently ansible.windows.win_powershell: script: | C:\PluginSetup\vlc-3.0.20-win64.exe /L=1033 /S
- name: Pause to let VLC setup finish ansible.builtin.pause: minutes: 4
- name: Remove staging folder ansible.windows.win_file: path: "C:\\PluginSetup" state: absent
- name: Check for VLC executable ansible.windows.win_stat: path: "C:\\Program Files\\VideoLAN\\VLC\\vlc.exe" register: vlc_check
- name: Fail if VLC is missing ansible.builtin.fail: msg: "Validation failed — VLC executable not found" when: not vlc_check.stat.existsEvery plugin worth shipping follows this shape.
When in Doubt, Clone and Read
Section titled “When in Doubt, Clone and Read”The Plugin Browser shows the YAML of every plugin you can see. When you’re stuck, pick a published plugin that does something similar, clone it, and read its task list. After three or four reference plugins, you’ll know the patterns.
Or Hand It to Your Agent
Section titled “Or Hand It to Your Agent”If you’d rather not write the YAML by hand, the Agent MCP plugin includes two skills built for plugin work:
/rogue-plugin-brainstorm: start a new plugin from scratch/rogue-plugin-develop: iterate on an existing plugin
Both skills follow every rule on this page, and the YAML they produce goes straight into the Plugin Dev editor.
The skills help most with hard plugins: multi-machine server/client stacks like Elastic (Stack server on one box, Agent on every endpoint) and Splunk (indexer + forwarders), AD forest builds with trust relationships, Wireguard mesh deployments, and anything where half a dozen plays must run in the right order on the right boxes with the right parameters. Your agent handles these end to end. It researches install flows, splits the work across plugins, sets up the parent/child relationships, puts vault files where they belong, validates the offline install, and iterates against live deploy logs when something breaks. A plugin that takes a weekend to write by hand comes together in a single afternoon.