--- # Update Portainer Edge Agent across homelab hosts # # Usage: # ansible-playbook -i hosts.ini playbooks/update_portainer_agent.yml # ansible-playbook -i hosts.ini playbooks/update_portainer_agent.yml -e "agent_version=2.33.7" # ansible-playbook -i hosts.ini playbooks/update_portainer_agent.yml --limit vish-concord-nuc # # Notes: # - Reads EDGE_ID and EDGE_KEY from the running container — no secrets needed in vars # - Set docker_bin in host_vars to override the docker binary path per host # - For Synology (calypso): docker_bin includes sudo prefix since Ansible become # does not reliably escalate on DSM - name: Update Portainer Edge Agent hosts: portainer_edge_agents gather_facts: false vars: agent_version: "2.33.7" agent_image: "portainer/agent:{{ agent_version }}" container_name: portainer_edge_agent tasks: - name: Check container exists shell: "{{ docker_bin | default('docker') }} inspect {{ container_name }} --format '{{ '{{' }}.Id{{ '}}' }}'" register: container_check changed_when: false failed_when: container_check.rc != 0 - name: Get current image shell: "{{ docker_bin | default('docker') }} inspect {{ container_name }} --format '{{ '{{' }}.Config.Image{{ '}}' }}'" register: current_image changed_when: false - name: Get EDGE environment vars from running container shell: "{{ docker_bin | default('docker') }} inspect {{ container_name }} --format '{{ '{{' }}json .Config.Env{{ '}}' }}'" register: container_env changed_when: false - name: Parse EDGE_ID set_fact: edge_id: "{{ (container_env.stdout | from_json | select('match', 'EDGE_ID=.*') | list | first).split('=', 1)[1] }}" - name: Parse EDGE_KEY set_fact: edge_key: "{{ (container_env.stdout | from_json | select('match', 'EDGE_KEY=.*') | list | first).split('=', 1)[1] }}" - name: Pull new agent image shell: "{{ docker_bin | default('docker') }} pull {{ agent_image }}" register: pull_result changed_when: "'Status: Downloaded newer image' in pull_result.stdout" - name: Skip if already on target version debug: msg: "{{ inventory_hostname }}: already running {{ agent_image }}, skipping recreate" when: current_image.stdout == agent_image and not pull_result.changed - name: Stop old container shell: "{{ docker_bin | default('docker') }} stop {{ container_name }}" when: current_image.stdout != agent_image or pull_result.changed - name: Remove old container shell: "{{ docker_bin | default('docker') }} rm {{ container_name }}" when: current_image.stdout != agent_image or pull_result.changed - name: Start new container shell: > {{ docker_bin | default('docker') }} run -d --name {{ container_name }} --restart always -v /var/run/docker.sock:/var/run/docker.sock -v {{ docker_volumes_path | default('/var/lib/docker/volumes') }}:/var/lib/docker/volumes -v /:/host -v portainer_agent_data:/data -e EDGE=1 -e EDGE_ID={{ edge_id }} -e EDGE_KEY={{ edge_key }} -e EDGE_INSECURE_POLL=1 {{ agent_image }} when: current_image.stdout != agent_image or pull_result.changed - name: Wait for container to be running shell: "{{ docker_bin | default('docker') }} ps --filter 'name={{ container_name }}' --format '{{ '{{' }}.Status{{ '}}' }}'" register: container_status retries: 5 delay: 3 until: "'Up' in container_status.stdout" when: current_image.stdout != agent_image or pull_result.changed - name: Report result debug: msg: "{{ inventory_hostname }}: {{ current_image.stdout }} → {{ agent_image }} | {{ container_status.stdout | default('no change') }}"