Answers: <p>For example, enable <em>backup</em> and <em>register</em> the results. As a last task create a record in the <em>journal</em> (see the declaration of the variables in the details under the line) and keep it in the cache</p>
  <pre class="lang-yaml prettyprint-override"><code>shell&gt; cat roles/test_rollback/tasks/config.yml 
  - name: config_01
    template:
      src: test.ini.j2
      dest: /tmp/test.ini
      backup: true
    register: config_01
  
  - name: Write journal
    set_fact:
      journal: &quot;{{ journal|d({})|combine({timestamp: journal_config_dict}) }}&quot;
      cacheable: true
    vars:
      timestamp: &quot;{{ '%Y-%m-%d_%H_%M_%S'|strftime }}&quot;
  </code></pre>
  <p>If you want to rollback the changes set the <em>rollback_journal_record</em></p>
  <pre class="lang-yaml prettyprint-override"><code>shell&gt; cat roles/test_rollback/rollback/config.yml 
  - copy:
      remote_src: true
      src: &quot;{{ item.value.backup_file }}&quot;
      dest: &quot;{{ item.value.dest }}&quot;
    loop: &quot;{{ journal[rollback_journal_record]|dict2items|selectattr('value.changed') }}&quot;
    loop_control:
      label: &quot;{{ item.key }}&quot;
  </code></pre>
  <p>Let's test it. Given the file on the remote host</p>
  <pre class="lang-ini prettyprint-override"><code>shell&gt; ssh admin@test_11 cat /tmp/test.ini
  test = X
  </code></pre>
  <p>Run the playbook</p>
  <pre class="lang-yaml prettyprint-override"><code>shell&gt; ansible-playbook pb.yml
    ...
  TASK [test_rollback : config_01] *************************************************************
  changed: [test_11]
  
  TASK [test_rollback : Write journal] *********************************************************
  ok: [test_11]
    ...
  </code></pre>
  <p>The file changed</p>
  <pre class="lang-ini prettyprint-override"><code>shell&gt; ssh admin@test_11 cat /tmp/test.ini
  test = A
  </code></pre>
  <p>and the record was added to the journal</p>
  <sup>
  <pre class="lang-yaml prettyprint-override"><code>shell&gt; cat /tmp/ansible_cache.json/ansible_facts_test_11 
  {
      &quot;journal&quot;: {
          &quot;2023-03-08_02_33_48&quot;: {
              &quot;config_01&quot;: {
                  &quot;backup_file&quot;: &quot;/tmp/test.ini.50045.2023-03-08@01:33:48~&quot;,
                  &quot;changed&quot;: true,
                  &quot;checksum&quot;: &quot;6676ed8ea4caa804d3c5f0247c03ece8b7a80b0b&quot;,
                  &quot;dest&quot;: &quot;/tmp/test.ini&quot;,
                  &quot;diff&quot;: [],
                  &quot;failed&quot;: false,
                  &quot;gid&quot;: 0,
                  &quot;group&quot;: &quot;wheel&quot;,
                  &quot;md5sum&quot;: &quot;0740a0962b43fa7bedf3dcd97e96697f&quot;,
                  &quot;mode&quot;: &quot;0644&quot;,
                  &quot;owner&quot;: &quot;root&quot;,
                  &quot;size&quot;: 9,
                  &quot;src&quot;: &quot;/home/admin/.ansible/tmp/ansible-tmp-1678239225.046748-1753646-178984183659153/source&quot;,
                  &quot;state&quot;: &quot;file&quot;,
                  &quot;uid&quot;: 0
              }
          }
      }
  }
  </code></pre>
  </sup>
  <p>See the records in the journal</p>
  <pre class="lang-yaml prettyprint-override"><code>shell&gt;ansible-playbook pb.yml -t info -e debug_journal_records=true
    ...
  TASK [test_rollback : debug] *****************************************************************
  ok: [test_11] =&gt; 
    msg: |-
      Journal records:
      - 2023-03-08_02_33_48
    ...
  </code></pre>
  <p>See the changed file(s) in the record</p>
  <pre class="lang-yaml prettyprint-override"><code>shell&gt; ansible-playbook pb.yml -t info -e debug_journal_record=2023-03-08_02_33_48
    ...
  TASK [test_rollback : debug] *****************************************************************
  ok: [test_11] =&gt; 
    msg: |-
      Journal changes of 2023-03-08_02_33_48:
      config_01:
          backup_file: /tmp/test.ini.50045.2023-03-08@01:33:48~
          changed: true
          checksum: 6676ed8ea4caa804d3c5f0247c03ece8b7a80b0b
          dest: /tmp/test.ini
          diff: []
          failed: false
          gid: 0
          group: wheel
          md5sum: 0740a0962b43fa7bedf3dcd97e96697f
          mode: '0644'
          owner: root
          size: 9
          src: /home/admin/.ansible/tmp/ansible-tmp-1678239225.046748-1753646-178984183659153/source
          state: file
          uid: 0
    ...
  </code></pre>
  <p>Rollback the changed file(s)</p>
  <pre class="lang-yaml prettyprint-override"><code>shell&gt; ansible-playbook pb.yml -t rollback -e rollback_journal_record=2023-03-08_02_33_48
    ...
  TASK [test_rollback : copy] ******************************************************************
  changed: [test_11] =&gt; (item=config_01)
    ...
  </code></pre>
  <p>The file was restored from the backup</p>
  <pre class="lang-yaml prettyprint-override"><code>shell&gt; ssh admin@test_11 cat /tmp/test.ini
  test = X
  </code></pre>
  <hr>
  <sup>
  <p>Example of a complete project for testing</p>
  <pre class="lang-bash prettyprint-override"><code>shell&gt; tree .
  .
  ├── ansible.cfg
  ├── hosts
  ├── pb.yml
  └── roles
      └── test_rollback
          ├── defaults
          │   └── main.yml
          ├── rollback
          │   ├── config.yml
          │   └── main.yml
          ├── tasks
          │   ├── config.yml
          │   ├── info.yml
          │   ├── main.yml
          │   └── rollback.yml
          └── templates
              └── test.ini.j2
  
  6 directories, 11 files
  </code></pre>
  <p>Enable caching</p>
  <pre class="lang-ini prettyprint-override"><code>shell&gt; cat ansible.cfg 
  [defaults]
  gathering = explicit
  collections_path = $HOME/.local/lib/python3.9/site-packages/
  inventory = $PWD/hosts
  roles_path = $PWD/roles
  ansible_managed = {file}
  deprecation_warnings=true
  remote_tmp = ~/.ansible/tmp
  retry_files_enabled = false
  stdout_callback = yaml
  # fact_caching
  fact_caching = jsonfile
  fact_caching_connection = /tmp/ansible_cache.json
  fact_caching_prefix = ansible_facts_
  fact_caching_timeout = 86400
  </code></pre>
  <pre class="lang-ini prettyprint-override"><code>shell&gt; cat hosts 
  test_11
  test_12
  test_13
  </code></pre>
  <pre class="lang-yaml prettyprint-override"><code>shell&gt; cat pb.yml 
  - hosts: test_11
  
    roles:
      - test_rollback
  </code></pre>
  <p>The role</p>
  <pre class="lang-yaml prettyprint-override"><code>shell&gt; cat roles/test_rollback/tasks/main.yml 
  - import_tasks: info.yml
    tags: info
  
  - import_tasks: rollback.yml
    when: rollback_journal_record is defined
    tags: rollback
  
  - import_tasks: config.yml
    tags: config
  </code></pre>
  <pre class="lang-yaml prettyprint-override"><code>shell&gt; cat roles/test_rollback/tasks/info.yml 
  - debug:
      msg: |
        Journal records:
        {{ journal.keys()|list|to_nice_yaml }}
    when: debug_journal_records|d(false)|bool
  
  - debug:
      msg: |
        Journal changes of {{ debug_journal_record }}:
        {{ journal[debug_journal_record]|dict2items|
           selectattr('value.changed')|items2dict|to_nice_yaml }}
    when: debug_journal_record is defined
  </code></pre>
  <pre class="lang-yaml prettyprint-override"><code>shell&gt; cat roles/test_rollback/tasks/rollback.yml 
  - import_tasks: rollback/main.yml
    
  - meta: end_play
  </code></pre>
  <pre class="lang-yaml prettyprint-override"><code>shell&gt; cat roles/test_rollback/tasks/config.yml 
  - name: config_01
    template:
      src: test.ini.j2
      dest: /tmp/test.ini
      backup: true
    register: config_01
  
  - name: Write journal
    set_fact:
      journal: &quot;{{ journal|d({})|combine({timestamp: journal_config_dict}) }}&quot;
      cacheable: true
    vars:
      timestamp: &quot;{{ '%Y-%m-%d_%H_%M_%S'|strftime }}&quot;
  </code></pre>
  <pre class="lang-yaml prettyprint-override"><code>shell&gt; cat roles/test_rollback/rollback/main.yml 
  - debug:
      msg: |
        Rollback {{ rollback_journal_record }}:
        {{ journal[rollback_journal_record]|dict2items|
           selectattr('value.changed')|
           map(attribute='value.dest')|to_nice_yaml }}
    when: debug|d(false)|bool
  
  - import_tasks: rollback/config.yml
  </code></pre>
  <pre class="lang-yaml prettyprint-override"><code>shell&gt; cat roles/test_rollback/rollback/config.yml 
  - copy:
      remote_src: true
      src: &quot;{{ item.value.backup_file }}&quot;
      dest: &quot;{{ item.value.dest }}&quot;
    loop: &quot;{{ journal[rollback_journal_record]|dict2items|selectattr('value.changed') }}&quot;
    loop_control:
      label: &quot;{{ item.key }}&quot;
  </code></pre>
  <pre class="lang-yaml prettyprint-override"><code>shell&gt; cat roles/test_rollback/defaults/main.yml 
  # Journal
  journal_config_varnames: &quot;{{ q('varnames', '^config_*') }}&quot;
  journal_config_vars: &quot;{{ q('vars', *q('varnames', '^config_*')) }}&quot;
  journal_config_dict: &quot;{{ dict(journal_config_varnames|zip(journal_config_vars)) }}&quot;
  </code></pre>
  <pre class="lang-yaml prettyprint-override"><code>shell&gt; cat roles/test_rollback/templates/test.ini.j2 
  test = A
  </code></pre>
  </sup>
  
Answers: CC BY-SA 4.0
Answers: 2023-03-08T01:53:32.137Z
Answers: 2023-03-08T02:07:00.333Z
Answers: 2023-03-08T02:07:00.333Z
Answers: Vladimir Botka
Body: <p>I would like to define rescue or rollback executions within my Ansible structure. I am using this recommended structure:</p>
  <pre><code>roles
    rolename
      files
      tasks
      templates
      handlers
      ...
  </code></pre>
  <p>I know that I can set blocks and within them a rescue section. But this seems a little clumsy to me and I would like to define all the rollback/rescue options within its own folder like</p>
  <pre><code>roles
    rolename
      ...
      tasks
      rollback/rescue
      handlers
      ...
  </code></pre>
  <p>This would make much more sense to me. I am speaking here about rollback, because I would like to be able to rollback a setup that I have created once with this role. I understand that the concept of rescue is a little bit different because it aims to fire when there is an error approached in executing a role. But I am hoping that I could use the concepts together while defining an anti-step to each step that I have defined in the role. And these anti-steps should have their own place and not included in the tasks files.</p>
  
Comments: CC BY-SA 4.0
Comments: 2023-03-07T23:00:27.893Z
Comments: What prevents you from "defining an anti-step to each step" in *rollback* and including them whenever you want?
Comments: Vladimir Botka
Comments: CC BY-SA 4.0
Comments: 2023-03-08T06:58:30.403Z
Comments: Maybe an unpopular opinion, but your rollback should be to a previous state. This means versioning your roles and applying the desired version of the role to the target system, not implementing "anti-tasks" in the role.
Comments: Bruce Becker
Tags: <ansible>
Title: How to define rollback or rescue in ansible folder structure?
UserDisplayName: Vietna
ThreadText: # How to define rollback or rescue in ansible folder structure?
  
  (Asked by: username_0 on 2023-03-07)
  
  I would like to define rescue or rollback executions within my Ansible structure. I am using this recommended structure:
  
  ```
  roles
    rolename
      files
      tasks
      templates
      handlers
      ...
  
  ```
  
  I know that I can set blocks and within them a rescue section. But this seems a little clumsy to me and I would like to define all the rollback/rescue options within its own folder like
  
  ```
  roles
    rolename
      ...
      tasks
      rollback/rescue
      handlers
      ...
  
  ```
  
  This would make much more sense to me. I am speaking here about rollback, because I would like to be able to rollback a setup that I have created once with this role. I understand that the concept of rescue is a little bit different because it aims to fire when there is an error approached in executing a role. But I am hoping that I could use the concepts together while defining an anti-step to each step that I have defined in the role. And these anti-steps should have their own place and not included in the tasks files.
  
  **Tags:** ansible
  
  > **Comments:**
  > **username_1**: What prevents you from "defining an anti-step to each step" in *rollback* and including them whenever you want?  
  > **username_2**: Maybe an unpopular opinion, but your rollback should be to a previous state. This means versioning your roles and applying the desired version of the role to the target system, not implementing "anti-tasks" in the role.  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_1 on 2023-03-08. Score: 1
  
  For example, enable *backup* and *register* the results. As a last task create a record in the *journal* (see the declaration of the variables in the details under the line) and keep it in the cache
  
  ```yaml
  shell> cat roles/test_rollback/tasks/config.yml 
  - name: config_01
    template:
      src: test.ini.j2
      dest: /tmp/test.ini
      backup: true
    register: config_01
  
  - name: Write journal
    set_fact:
      journal: "{{ journal|d({})|combine({timestamp: journal_config_dict}) }}"
      cacheable: true
    vars:
      timestamp: "{{ '%Y-%m-%d_%H_%M_%S'|strftime }}"
  
  ```
  
  If you want to rollback the changes set the *rollback\_journal\_record*
  
  ```yaml
  shell> cat roles/test_rollback/rollback/config.yml 
  - copy:
      remote_src: true
      src: "{{ item.value.backup_file }}"
      dest: "{{ item.value.dest }}"
    loop: "{{ journal[rollback_journal_record]|dict2items|selectattr('value.changed') }}"
    loop_control:
      label: "{{ item.key }}"
  
  ```
  
  Let's test it. Given the file on the remote host
  
  ```ini
  shell> ssh admin@test_11 cat /tmp/test.ini
  test = X
  
  ```
  
  Run the playbook
  
  ```yaml
  shell> ansible-playbook pb.yml
    ...
  TASK [test_rollback : config_01] *************************************************************
  changed: [test_11]
  
  TASK [test_rollback : Write journal] *********************************************************
  ok: [test_11]
    ...
  
  ```
  
  The file changed
  
  ```ini
  shell> ssh admin@test_11 cat /tmp/test.ini
  test = A
  
  ```
  
  and the record was added to the journal
  
  ```yaml
  shell> cat /tmp/ansible_cache.json/ansible_facts_test_11 
  {
      "journal": {
          "2023-03-08_02_33_48": {
              "config_01": {
                  "backup_file": "/tmp/test.ini.50045.2023-03-08@01:33:48~",
                  "changed": true,
                  "checksum": "6676ed8ea4caa804d3c5f0247c03ece8b7a80b0b",
                  "dest": "/tmp/test.ini",
                  "diff": [],
                  "failed": false,
                  "gid": 0,
                  "group": "wheel",
                  "md5sum": "0740a0962b43fa7bedf3dcd97e96697f",
                  "mode": "0644",
                  "owner": "root",
                  "size": 9,
                  "src": "/home/admin/.ansible/tmp/ansible-tmp-1678239225.046748-1753646-178984183659153/source",
                  "state": "file",
                  "uid": 0
              }
          }
      }
  }
  
  ```
  
  See the records in the journal
  
  ```yaml
  shell>ansible-playbook pb.yml -t info -e debug_journal_records=true
    ...
  TASK [test_rollback : debug] *****************************************************************
  ok: [test_11] => 
    msg: |-
      Journal records:
      - 2023-03-08_02_33_48
    ...
  
  ```
  
  See the changed file(s) in the record
  
  ```yaml
  shell> ansible-playbook pb.yml -t info -e debug_journal_record=2023-03-08_02_33_48
    ...
  TASK [test_rollback : debug] *****************************************************************
  ok: [test_11] => 
    msg: |-
      Journal changes of 2023-03-08_02_33_48:
      config_01:
          backup_file: /tmp/test.ini.50045.2023-03-08@01:33:48~
          changed: true
          checksum: 6676ed8ea4caa804d3c5f0247c03ece8b7a80b0b
          dest: /tmp/test.ini
          diff: []
          failed: false
          gid: 0
          group: wheel
          md5sum: 0740a0962b43fa7bedf3dcd97e96697f
          mode: '0644'
          owner: root
          size: 9
          src: /home/admin/.ansible/tmp/ansible-tmp-1678239225.046748-1753646-178984183659153/source
          state: file
          uid: 0
    ...
  
  ```
  
  Rollback the changed file(s)
  
  ```yaml
  shell> ansible-playbook pb.yml -t rollback -e rollback_journal_record=2023-03-08_02_33_48
    ...
  TASK [test_rollback : copy] ******************************************************************
  changed: [test_11] => (item=config_01)
    ...
  
  ```
  
  The file was restored from the backup
  
  ```yaml
  shell> ssh admin@test_11 cat /tmp/test.ini
  test = X
  
  ```
  
  ---
  
  Example of a complete project for testing
  
  ```bash
  shell> tree .
  .
  ├── ansible.cfg
  ├── hosts
  ├── pb.yml
  └── roles
      └── test_rollback
          ├── defaults
          │   └── main.yml
          ├── rollback
          │   ├── config.yml
          │   └── main.yml
          ├── tasks
          │   ├── config.yml
          │   ├── info.yml
          │   ├── main.yml
          │   └── rollback.yml
          └── templates
              └── test.ini.j2
  
  6 directories, 11 files
  
  ```
  
  Enable caching
  
  ```ini
  shell> cat ansible.cfg 
  [defaults]
  gathering = explicit
  collections_path = $HOME/.local/lib/python3.9/site-packages/
  inventory = $PWD/hosts
  roles_path = $PWD/roles
  ansible_managed = {file}
  deprecation_warnings=true
  remote_tmp = ~/.ansible/tmp
  retry_files_enabled = false
  stdout_callback = yaml
  # fact_caching
  fact_caching = jsonfile
  fact_caching_connection = /tmp/ansible_cache.json
  fact_caching_prefix = ansible_facts_
  fact_caching_timeout = 86400
  
  ```
  
  ```ini
  shell> cat hosts 
  test_11
  test_12
  test_13
  
  ```
  
  ```yaml
  shell> cat pb.yml 
  - hosts: test_11
  
    roles:
      - test_rollback
  
  ```
  
  The role
  
  ```yaml
  shell> cat roles/test_rollback/tasks/main.yml 
  - import_tasks: info.yml
    tags: info
  
  - import_tasks: rollback.yml
    when: rollback_journal_record is defined
    tags: rollback
  
  - import_tasks: config.yml
    tags: config
  
  ```
  
  ```yaml
  shell> cat roles/test_rollback/tasks/info.yml 
  - debug:
      msg: |
        Journal records:
        {{ journal.keys()|list|to_nice_yaml }}
    when: debug_journal_records|d(false)|bool
  
  - debug:
      msg: |
        Journal changes of {{ debug_journal_record }}:
        {{ journal[debug_journal_record]|dict2items|
           selectattr('value.changed')|items2dict|to_nice_yaml }}
    when: debug_journal_record is defined
  
  ```
  
  ```yaml
  shell> cat roles/test_rollback/tasks/rollback.yml 
  - import_tasks: rollback/main.yml
    
  - meta: end_play
  
  ```
  
  ```yaml
  shell> cat roles/test_rollback/tasks/config.yml 
  - name: config_01
    template:
      src: test.ini.j2
      dest: /tmp/test.ini
      backup: true
    register: config_01
  
  - name: Write journal
    set_fact:
      journal: "{{ journal|d({})|combine({timestamp: journal_config_dict}) }}"
      cacheable: true
    vars:
      timestamp: "{{ '%Y-%m-%d_%H_%M_%S'|strftime }}"
  
  ```
  
  ```yaml
  shell> cat roles/test_rollback/rollback/main.yml 
  - debug:
      msg: |
        Rollback {{ rollback_journal_record }}:
        {{ journal[rollback_journal_record]|dict2items|
           selectattr('value.changed')|
           map(attribute='value.dest')|to_nice_yaml }}
    when: debug|d(false)|bool
  
  - import_tasks: rollback/config.yml
  
  ```
  
  ```yaml
  shell> cat roles/test_rollback/rollback/config.yml 
  - copy:
      remote_src: true
      src: "{{ item.value.backup_file }}"
      dest: "{{ item.value.dest }}"
    loop: "{{ journal[rollback_journal_record]|dict2items|selectattr('value.changed') }}"
    loop_control:
      label: "{{ item.key }}"
  
  ```
  
  ```yaml
  shell> cat roles/test_rollback/defaults/main.yml 
  # Journal
  journal_config_varnames: "{{ q('varnames', '^config_*') }}"
  journal_config_vars: "{{ q('vars', *q('varnames', '^config_*')) }}"
  journal_config_dict: "{{ dict(journal_config_varnames|zip(journal_config_vars)) }}"
  
  ```
  
  ```yaml
  shell> cat roles/test_rollback/templates/test.ini.j2 
  test = A
  
  ```
  
  
  ---
  
  