├── README.md
└── playbooks
├── base
├── ansible.cfg
├── dockerguy.yml
├── getdiskname.yml
├── http.j2
├── inventory
├── inventoryans.py
├── mariadb55.retry
├── mariadb55.yml
├── motd.retry
├── motd.yml
├── nomoresecret.yml
├── pascal.py
├── secret.yml
├── un-ansible-loop.yml
├── user-with-items.retry
├── user-with-items.yml
├── variables-jinja.yml
├── variables.yml
├── when-test.retry
└── when-test.yml
├── delegation
├── addhosts.retry
├── addhosts.yml
├── ansible.cfg
├── bigfile.yaml
├── delegate.yml
├── download.retry
├── download.yml
├── inventory
├── reboot.yml
├── templates
│ ├── ansible2.example.com-httpd.conf.j2
│ ├── index.html.j2
│ └── proxy.example.com-httpd.conf.j2
├── waitforme.retry
├── waitforme.yaml
├── web.retry
└── web.yml
├── docker
├── ansible.cfg
└── inventory
├── ec2
├── ansible.cfg
├── ec2.ini
├── ec2.py
├── inventory
│ ├── ec2.ini
│ ├── ec2.py
│ └── inventory
├── list-instances.yaml
├── ubuntu-ami.yaml
└── ubuntu.yaml
├── error_handling
├── ansible.cfg
├── assert.retry
├── assert.yml
├── block.yml
├── inventory
├── rescue.yml
└── uricheck.yml
├── exam-lab
├── ansible.cfg
├── inventory
├── main.yml
└── roles
│ ├── mountweb
│ ├── README.md
│ ├── defaults
│ │ └── main.yml
│ ├── handlers
│ │ └── main.yml
│ ├── meta
│ │ └── main.yml
│ ├── tasks
│ │ └── main.yml
│ ├── templates
│ │ └── http.j2
│ ├── tests
│ │ ├── inventory
│ │ └── test.yml
│ └── vars
│ │ └── main.yml
│ └── web-role
│ ├── README.md
│ ├── defaults
│ └── main.yml
│ ├── handlers
│ └── main.yml
│ ├── meta
│ └── main.yml
│ ├── tasks
│ ├── main.yml
│ ├── webclients.yml
│ └── webservers.yml
│ ├── templates
│ └── httpd.j2
│ ├── tests
│ ├── inventory
│ └── test.yml
│ └── vars
│ └── main.yml
├── ftp
├── ansible.cfg
├── inventory
└── vsftpd.yml
├── includes
├── ansible.cfg
├── inventory.yml
├── site.yml
└── tasks
│ ├── file.yml
│ └── lamp.yml
├── lab
├── ansible.cfg
├── custom.fact
├── lab-copy-facts.yml
├── lab-custom.facts
├── lab-facts
├── lab-inventory
├── lab-playbook.yml
├── lab-tasks
│ ├── file.yml
│ └── lamp.yml
├── lab-vars
│ └── allvars.yml
├── playbook.retry
└── playbook.yml
├── lab10
├── ansible.cfg
├── disks.yml
├── inventory
├── lab10-3.yml
├── main.yml
├── roles
│ ├── mountweb
│ │ ├── README.md
│ │ ├── defaults
│ │ │ └── main.yml
│ │ ├── handlers
│ │ │ └── main.yml
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── tasks
│ │ │ └── main.yml
│ │ ├── templates
│ │ │ └── http.j2
│ │ ├── tests
│ │ │ ├── inventory
│ │ │ └── test.yml
│ │ └── vars
│ │ │ └── main.yml
│ └── web-role
│ │ ├── README.md
│ │ ├── defaults
│ │ └── main.yml
│ │ ├── handlers
│ │ └── main.yml
│ │ ├── meta
│ │ └── main.yml
│ │ ├── tasks
│ │ ├── main.yml
│ │ ├── webclients.yml
│ │ └── webservers.yml
│ │ ├── templates
│ │ └── httpd.j2
│ │ ├── tests
│ │ ├── inventory
│ │ └── test.yml
│ │ └── vars
│ │ └── main.yml
├── templates
│ └── httpd.j2
└── vars
│ └── webservers.yml
├── lab4
├── ansible.cfg
├── inventory
├── tasks
│ ├── redhat.yml
│ └── ubuntu.yml
├── variables.yml
└── vars
│ ├── redhat.yml
│ └── ubuntu.yml
├── loops
├── ansible.cfg
├── conditionalrestart.yml
├── copytxt.retry
├── copytxt.yml
├── errorlog
├── handlers-lab.yml
├── handlers.retry
├── handlers.yml
├── ifsize.yml
├── inventory
├── register.retry
├── register.yml
├── register2.retry
├── register2.yml
├── register3.yml
├── with_nested.yml
└── with_nested_cleanup.yml
├── newvault
├── ansible.cfg
├── createusers.retry
├── createusers.yml
├── inventory
├── vars
│ └── secret.yml
└── vaultpw
├── roles-demo
├── ansible.cfg
├── inventory
├── motd-role.yml
├── nginx-role.yml
└── roles
│ └── motd
│ ├── README.md
│ ├── defaults
│ └── main.yml
│ ├── handlers
│ └── main.yml
│ ├── meta
│ └── main.yml
│ ├── tasks
│ └── main.yml
│ ├── templates
│ └── motd.j2
│ ├── tests
│ ├── inventory
│ └── test.yml
│ └── vars
│ └── main.yml
├── tags
├── ansible.cfg
├── install.yml
├── inventory
└── tag_include.yml
├── vault
├── ansible.cfg
├── inventory
└── vault-pw
└── windows
├── ansible.cfg
├── inventory
└── playbook.yml
/README.md:
--------------------------------------------------------------------------------
1 | # AutomatingWithAnsible
2 |
--------------------------------------------------------------------------------
/playbooks/base/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | remote_user = ansible
3 | host_key_checking = false
4 | inventory = inventory
5 |
6 | [privilege_escalation]
7 | become = True
8 | become_method = sudo
9 | become_user = root
10 | become_ask_pass = False
11 |
12 |
--------------------------------------------------------------------------------
/playbooks/base/dockerguy.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: use role to install docker
3 | hosts: ansible2.example.com
4 |
5 | roles:
6 | - geerlingguy.docker
7 |
--------------------------------------------------------------------------------
/playbooks/base/getdiskname.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: get disk names
3 | hosts: ansible2.example.com
4 | tasks:
5 |
6 | - name: get device name
7 | set_fact:
8 | device_name: "{{ item.key }}"
9 | no_log: True
10 | with_dict: "{{ ansible_devices }}"
11 | when: "item.value.host.startswith('SATA')"
12 |
13 | - name: show all values for selected device name
14 | debug: var=ansible_devices[device_name]
15 |
16 | - name: show only device name
17 | debug: var=device_name
18 |
--------------------------------------------------------------------------------
/playbooks/base/http.j2:
--------------------------------------------------------------------------------
1 | Listen *:80
2 | NameVirtualHost {{ ansible_fqdn }}
3 |
4 |
5 | ServerName {{ ansible_fqdn }}
6 | ServerAdmin {{ system_owner }}
7 | DocumentRoot /var/www/html
8 |
9 |
--------------------------------------------------------------------------------
/playbooks/base/inventory:
--------------------------------------------------------------------------------
1 | ansible1.example.com
2 | ansible2.example.com
3 | ubuntu.example.com
4 |
--------------------------------------------------------------------------------
/playbooks/base/inventoryans.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from subprocess import Popen,PIPE
4 | import sys
5 | import json
6 |
7 | result = {}
8 | result['all'] = {}
9 |
10 | pipe = Popen(['getent', 'hosts'], stdout=PIPE, universal_newlines=True)
11 |
12 | result['all']['hosts'] = []
13 | for line in pipe.stdout.readlines():
14 | s = line.split()
15 |
16 | result['all']['vars'] = {}
17 |
18 | if len(sys.argv) == 2 and sys.argv[1] == '--list':
19 | print(json.dumps(result))
20 | elif len(sys.argv) == 3 and sys.argv[1] == '--host':
21 | print(json.dumps({}))
22 | else:
23 | print("Requires an argument, please use --list or --host ")
24 |
--------------------------------------------------------------------------------
/playbooks/base/mariadb55.retry:
--------------------------------------------------------------------------------
1 | ansible2.example.com
2 |
--------------------------------------------------------------------------------
/playbooks/base/mariadb55.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: use role to install mariadb
3 | hosts: ansible2.example.com
4 |
5 | roles:
6 | - f500.mariadb55
7 |
--------------------------------------------------------------------------------
/playbooks/base/motd.retry:
--------------------------------------------------------------------------------
1 | ansible2.example.com
2 |
--------------------------------------------------------------------------------
/playbooks/base/motd.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: role usage example
3 | hosts: ansible2.example.com
4 |
5 | roles:
6 | - motd
7 |
--------------------------------------------------------------------------------
/playbooks/base/nomoresecret.yml:
--------------------------------------------------------------------------------
1 | hello world
2 | byebye world
3 |
--------------------------------------------------------------------------------
/playbooks/base/pascal.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | from subprocess import Popen,PIPE
4 | import sys
5 |
6 | try:
7 | import json
8 | except ImportError:
9 | import simplejson as json
10 |
11 |
12 |
13 | result = {}
14 |
15 | result['all'] = {}
16 |
17 |
18 |
19 | pipe = Popen(['getent', 'hosts'], stdout=PIPE, universal_newlines=True)
20 |
21 |
22 | result['all']['hosts'] = []
23 |
24 | for line in pipe.stdout.readlines():
25 | s = line.split()
26 | result['all']['hosts']=result['all']['hosts']+s
27 |
28 |
29 | result['all']['vars'] = {}
30 |
31 |
32 | if len(sys.argv) == 2 and sys.argv[1] == '--list':
33 | print(json.dumps(result))
34 |
35 | elif len(sys.argv) == 3 and sys.argv[1] == '--host':
36 | print(json.dumps({}))
37 |
38 | else:
39 | print("Requires an argument, please use --list or --host ")
40 |
--------------------------------------------------------------------------------
/playbooks/base/secret.yml:
--------------------------------------------------------------------------------
1 | $ANSIBLE_VAULT;1.1;AES256
2 | 33616466613334353330636362343339313831393561303362383737636631396363613735383830
3 | 6138323337633166336337333762343132633265613764610a336238653637343135323464313736
4 | 63396230666366633666613363613266626530346563633238666530316637313261623031356438
5 | 3438656234333363340a393237643439666466666534356434323933643566303433356630383130
6 | 39313933356234383862366537323139366463626432316539333939663235666339
7 |
--------------------------------------------------------------------------------
/playbooks/base/un-ansible-loop.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: clean up all
3 | hosts: all
4 | tasks:
5 | - name: remove services
6 | yum:
7 | name: "{{ item }}"
8 | state: absent
9 | with_items:
10 | - httpd
11 | - vsftpf
12 | - name: remove files
13 | file:
14 | path: "{{ item }}"
15 | state: absent
16 | with_items:
17 | - /etc/ansible/facts.d
18 | - /var/www/html/index.html
19 | - /var/ftp/pub/README
20 | - /etc/motd
21 |
--------------------------------------------------------------------------------
/playbooks/base/user-with-items.retry:
--------------------------------------------------------------------------------
1 | ansible2.example.com
2 |
--------------------------------------------------------------------------------
/playbooks/base/user-with-items.yml:
--------------------------------------------------------------------------------
1 | - name: create users
2 | hosts: ansible2.example.com
3 | tasks:
4 | - name: manage users and group membership
5 | user:
6 | name: "{{ item.name }}"
7 | state: present
8 | groups: "{{ item.groups }}"
9 | with_items:
10 | - { name: 'linda', groups: 'students' }
11 | - { name: 'anna', groups: 'profs' }
12 |
--------------------------------------------------------------------------------
/playbooks/base/variables-jinja.yml:
--------------------------------------------------------------------------------
1 | - name: deploy and start Apache
2 | hosts: ansible2.example.com
3 | vars:
4 | apache_package: httpd
5 | firewall_package: firewalld
6 | web_service: httpd
7 | firewall_service: firewalld
8 | rule: http
9 | system_owner: anna@example.com
10 |
11 | tasks:
12 | - name: install and update latest packages
13 | yum:
14 | name:
15 | - "{{ apache_package }}"
16 | - "{{ firewall_package }}"
17 | state: latest
18 |
19 | - name: start and enable {{ firewall_service }}
20 | service:
21 | name: "{{ firewall_service }}"
22 | enabled: true
23 | state: started
24 |
25 | - template:
26 | src: http.j2
27 | dest: /etc/httpd/conf.d
28 | owner: root
29 | group: root
30 | mode: 0644
31 |
32 | - name: create web content
33 | copy:
34 | content: "Welcome at the Ansible managed web server"
35 | dest: /var/www/html/index.html
36 |
37 | - name: start and enable {{ web_service }}
38 | service:
39 | name: "{{ web_service }}"
40 | enabled: true
41 | state: started
42 |
43 | - name: open firewall for {{ rule }}
44 | firewalld:
45 | service: "{{ rule }}"
46 | permanent: true
47 | immediate: true
48 | state: enabled
49 |
50 | - name: verify the web server
51 | hosts: localhost
52 | become: false
53 | tasks:
54 | - name: test that webserver is available
55 | uri:
56 | url: http://ansible2.example.com
57 | status_code: 200
58 |
59 |
--------------------------------------------------------------------------------
/playbooks/base/variables.yml:
--------------------------------------------------------------------------------
1 | - name: deploy and start Apache
2 | hosts: ansible2.example.com
3 | vars:
4 | apache_package: httpd
5 | firewall_package: firewalld
6 | web_service: httpd
7 | firewall_service: firewalld
8 | rule: http
9 |
10 | tasks:
11 | - name: install and update latest packages
12 | yum:
13 | name:
14 | - "{{ apache_package }}"
15 | - "{{ firewall_package }}"
16 | state: latest
17 |
18 | - name: start and enable {{ firewall_service }}
19 | service:
20 | name: "{{ firewall_service }}"
21 | enabled: true
22 | state: started
23 |
24 | - name: create web content
25 | copy:
26 | content: "Welcome at the Ansible managed web server"
27 | dest: /var/www/html/index.html
28 |
29 | - name: start and enable {{ web_service }}
30 | service:
31 | name: "{{ web_service }}"
32 | enabled: true
33 | state: started
34 |
35 | - name: open firewall for {{ rule }}
36 | firewalld:
37 | service: "{{ rule }}"
38 | permanent: true
39 | immediate: true
40 | state: enabled
41 |
42 | - name: verify the web server
43 | hosts: localhost
44 | become: false
45 | tasks:
46 | - name: test that webserver is available
47 | uri:
48 | url: http://ansible2.example.com
49 | status_code: 200
50 |
51 |
--------------------------------------------------------------------------------
/playbooks/base/when-test.retry:
--------------------------------------------------------------------------------
1 | ubuntu.example.com
2 |
--------------------------------------------------------------------------------
/playbooks/base/when-test.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | name: conditional test
4 | tasks:
5 | - name: conditional create user
6 | user:
7 | name: laura
8 | when: ansible_default_ipv4.address == "192.168.4.81"
9 |
--------------------------------------------------------------------------------
/playbooks/delegation/addhosts.retry:
--------------------------------------------------------------------------------
1 | localhost
2 |
--------------------------------------------------------------------------------
/playbooks/delegation/addhosts.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: test add host
3 | hosts: localhost
4 | tasks:
5 | - name: add another host
6 | add_host:
7 | name: proxy
8 | ansible_host: 192.168.4.232
9 | ansible_user: ansible
10 |
11 | - name: show where the command has been running
12 | command: hostname
13 | delegate_to: proxy
14 | register: command1
15 |
16 | - name: show how facts are handled
17 | command: echo "this is on {{ inventory_hostname }}"
18 | delegate_to: proxy
19 | register: output
20 |
21 | - debug:
22 | msg: "{{ command1.stdout }}"
23 | - debug:
24 | msg: "{{ output.stdout }}"
25 |
--------------------------------------------------------------------------------
/playbooks/delegation/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | inventory = inventory
3 | remote_user = ansible
4 | host_key_checking = false
5 |
6 | [privilege_escalation]
7 | become = True
8 | become_method = sudo
9 | become_user = root
10 | become_ask_pass = False
11 |
12 |
--------------------------------------------------------------------------------
/playbooks/delegation/bigfile.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: download a big file
3 | hosts: all
4 | serial: 2
5 | tasks:
6 | - name: start download
7 | get_url:
8 | url: http://192.168.122.1/largefile
9 | async: 1800
10 | poll: 30
11 |
12 |
--------------------------------------------------------------------------------
/playbooks/delegation/delegate.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: understand delegate_to
3 | hosts: ansible2.example.com
4 | tasks:
5 | - name: get process information
6 | command: ps
7 | register: remote_process
8 | changed_when: false
9 |
10 | - name: get localhost processes
11 | command: ps
12 | delegate_to: localhost
13 | register: local_process
14 | changed_when: false
15 |
16 | - name: display information about localhost processes
17 | debug:
18 | msg: "{{ local_process.stdout }}"
19 |
20 | - name: show information about remotehost processes
21 | debug:
22 | msg: "{{ remote_process.stdout }}"
23 |
--------------------------------------------------------------------------------
/playbooks/delegation/download.retry:
--------------------------------------------------------------------------------
1 | ansible2.example.com
2 |
--------------------------------------------------------------------------------
/playbooks/delegation/download.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: download large file
3 | hosts: ansible2.example.com
4 | tasks:
5 | - name: download large file
6 | get_url:
7 | url: https://www.rhatcert.com/pub/labipa-3.2.zip
8 | dest: /tmp/
9 | async: 7200
10 | poll: 0
11 | register: background_download
12 | - name: wait for download completion
13 | async_status:
14 | jid: "{{ background_download.ansible_job_id }}"
15 | register: job_result
16 | until: job_result.finished
17 | retries: 30
18 | delay: 120
19 |
20 |
--------------------------------------------------------------------------------
/playbooks/delegation/inventory:
--------------------------------------------------------------------------------
1 | ansible1.example.com
2 |
3 | [webservers]
4 | ansible2.example.com
5 |
6 | [proxyservers]
7 | proxy.example.com
8 |
--------------------------------------------------------------------------------
/playbooks/delegation/reboot.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: restart a server
3 | hosts: ansible2.example.com
4 | tasks:
5 | - name: restart server
6 | shell: sleep 2 && shutdown -r now "rebooting, please wait"
7 | async: 1
8 | poll: 0
9 | ignore_errors: true
10 |
11 | - name: waiting for server to come back
12 | wait_for:
13 | host: "{{ inventory_hostname }}"
14 | state: started
15 | delay: 30
16 | timeout: 300
17 | port: 22
18 | delegate_to: localhost
19 |
--------------------------------------------------------------------------------
/playbooks/delegation/templates/ansible2.example.com-httpd.conf.j2:
--------------------------------------------------------------------------------
1 | #{{ ansible_managed }}
2 | NameVirtualHost *:80
3 |
4 |
5 | ServerAdmin root@{{ ansible_fqdn }}
6 | DocumentRoot /var/www/html
7 | ServerName {{ ansible_fqdn }}
8 | ErrorLog logs/{{ ansible_fqdn }}-error.log
9 | CustomLog logs/{{ ansible_fqdn }}-access.log common
10 |
11 |
--------------------------------------------------------------------------------
/playbooks/delegation/templates/index.html.j2:
--------------------------------------------------------------------------------
1 | You are connected to {{ ansible_fqdn }}
2 |
--------------------------------------------------------------------------------
/playbooks/delegation/templates/proxy.example.com-httpd.conf.j2:
--------------------------------------------------------------------------------
1 | #{{ ansible_managed }}
2 |
3 | ProxyPass "/external" "http://{{ ansible_hostname}}"
4 | ProxyPassReverse "/external" "http://{{ ansible_hostname }}"
5 |
--------------------------------------------------------------------------------
/playbooks/delegation/waitforme.retry:
--------------------------------------------------------------------------------
1 | ansible1.example.com
2 | ansible2.example.com
3 |
--------------------------------------------------------------------------------
/playbooks/delegation/waitforme.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | tasks:
4 | - name: set waiting limit
5 | command: /bin/sleep 5
6 | async: 10
7 | poll: 5
8 |
--------------------------------------------------------------------------------
/playbooks/delegation/web.retry:
--------------------------------------------------------------------------------
1 | ansible2.example.com
2 | proxy.example.com
3 |
--------------------------------------------------------------------------------
/playbooks/delegation/web.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: install and configure apache
3 | hosts: ansible2.example.com,proxy.example.com
4 | tasks:
5 | - name: install apache
6 | package:
7 | name: httpd
8 | state: installed
9 | - name: start and enable apache
10 | service:
11 | name: httpd
12 | state: started
13 | enabled: yes
14 | - name: install firewalld
15 | package:
16 | name: firewalld
17 | state: installed
18 | - name: start and enable firewalld
19 | service:
20 | name: firewalld
21 | state: started
22 | enabled: yes
23 | - name: configure firewalld
24 | firewalld:
25 | zone: public
26 | service: http
27 | permanent: true
28 | immediate: true
29 | state: enabled
30 | - name: copy webserver template
31 | template:
32 | src: "templates/{{ inventory_hostname }}-httpd.conf.j2"
33 | dest: /etc/httpd/conf.d/ansible.conf
34 | owner: root
35 | group: root
36 | mode: 0644
37 | notify:
38 | - restart httpd
39 |
40 | handlers:
41 | - name: restart httpd
42 | service:
43 | name: httpd
44 | state: restarted
45 |
46 | - name: deploy apache and disable proxy server
47 | hosts: webservers
48 | tasks:
49 | - name: stop apache proxy
50 | service:
51 | name: httpd
52 | state: stopped
53 | delegate_to: "{{ item }}"
54 | with_items: "{{ groups['proxyservers'] }}"
55 | - name: deploy webpages
56 | template:
57 | src: templates/index.html.j2
58 | dest: /var/www/html/index.html
59 | owner: apache
60 | group: apache
61 | mode: 0644
62 | - name: start apache proxy server
63 | service:
64 | name: httpd
65 | state: started
66 | delegate_to: "{{ item }}"
67 | with_items: "{{ groups['proxyservers'] }}"
68 |
--------------------------------------------------------------------------------
/playbooks/docker/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | remote_user = ansible
3 | host_key_checking = false
4 | inventory = inventory
5 |
6 | [privilege_escalation]
7 | become = True
8 | become_method = sudo
9 | become_user = root
10 | become_ask_pass = False
11 |
12 |
--------------------------------------------------------------------------------
/playbooks/docker/inventory:
--------------------------------------------------------------------------------
1 | ansible1.example.com
2 | ansible2.example.com
3 | ubuntu.example.com
4 | fedora.example.com
5 |
--------------------------------------------------------------------------------
/playbooks/ec2/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | inventory = inventory
3 | remote_user = ubuntu
4 | host_key_checking = false
5 |
6 |
7 | [privilege_escalation]
8 | become = True
9 | become_method = sudo
10 | become_user = root
11 | become_ask_pass = False
12 |
13 |
--------------------------------------------------------------------------------
/playbooks/ec2/ec2.ini:
--------------------------------------------------------------------------------
1 | # Ansible EC2 external inventory script settings
2 | #
3 |
4 | [ec2]
5 |
6 | # to talk to a private eucalyptus instance uncomment these lines
7 | # and edit edit eucalyptus_host to be the host name of your cloud controller
8 | #eucalyptus = True
9 | #eucalyptus_host = clc.cloud.domain.org
10 |
11 | # AWS regions to make calls to. Set this to 'all' to make request to all regions
12 | # in AWS and merge the results together. Alternatively, set this to a comma
13 | # separated list of regions. E.g. 'us-east-1,us-west-1,us-west-2' and do not
14 | # provide the 'regions_exclude' option. If this is set to 'auto', AWS_REGION or
15 | # AWS_DEFAULT_REGION environment variable will be read to determine the region.
16 | regions = all
17 | regions_exclude = us-gov-west-1, cn-north-1
18 |
19 | # When generating inventory, Ansible needs to know how to address a server.
20 | # Each EC2 instance has a lot of variables associated with it. Here is the list:
21 | # http://docs.pythonboto.org/en/latest/ref/ec2.html#module-boto.ec2.instance
22 | # Below are 2 variables that are used as the address of a server:
23 | # - destination_variable
24 | # - vpc_destination_variable
25 |
26 | # This is the normal destination variable to use. If you are running Ansible
27 | # from outside EC2, then 'public_dns_name' makes the most sense. If you are
28 | # running Ansible from within EC2, then perhaps you want to use the internal
29 | # address, and should set this to 'private_dns_name'. The key of an EC2 tag
30 | # may optionally be used; however the boto instance variables hold precedence
31 | # in the event of a collision.
32 | destination_variable = public_dns_name
33 |
34 | # This allows you to override the inventory_name with an ec2 variable, instead
35 | # of using the destination_variable above. Addressing (aka ansible_ssh_host)
36 | # will still use destination_variable. Tags should be written as 'tag_TAGNAME'.
37 | #hostname_variable = tag_Name
38 |
39 | # For server inside a VPC, using DNS names may not make sense. When an instance
40 | # has 'subnet_id' set, this variable is used. If the subnet is public, setting
41 | # this to 'ip_address' will return the public IP address. For instances in a
42 | # private subnet, this should be set to 'private_ip_address', and Ansible must
43 | # be run from within EC2. The key of an EC2 tag may optionally be used; however
44 | # the boto instance variables hold precedence in the event of a collision.
45 | # WARNING: - instances that are in the private vpc, _without_ public ip address
46 | # will not be listed in the inventory until You set:
47 | # vpc_destination_variable = private_ip_address
48 | vpc_destination_variable = ip_address
49 |
50 | # The following two settings allow flexible ansible host naming based on a
51 | # python format string and a comma-separated list of ec2 tags. Note that:
52 | #
53 | # 1) If the tags referenced are not present for some instances, empty strings
54 | # will be substituted in the format string.
55 | # 2) This overrides both destination_variable and vpc_destination_variable.
56 | #
57 | #destination_format = {0}.{1}.example.com
58 | #destination_format_tags = Name,environment
59 |
60 | # To tag instances on EC2 with the resource records that point to them from
61 | # Route53, set 'route53' to True.
62 | route53 = False
63 |
64 | # To use Route53 records as the inventory hostnames, uncomment and set
65 | # to equal the domain name you wish to use. You must also have 'route53' (above)
66 | # set to True.
67 | # route53_hostnames = .example.com
68 |
69 | # To exclude RDS instances from the inventory, uncomment and set to False.
70 | #rds = False
71 |
72 | # To exclude ElastiCache instances from the inventory, uncomment and set to False.
73 | #elasticache = False
74 |
75 | # Additionally, you can specify the list of zones to exclude looking up in
76 | # 'route53_excluded_zones' as a comma-separated list.
77 | # route53_excluded_zones = samplezone1.com, samplezone2.com
78 |
79 | # By default, only EC2 instances in the 'running' state are returned. Set
80 | # 'all_instances' to True to return all instances regardless of state.
81 | all_instances = False
82 |
83 | # By default, only EC2 instances in the 'running' state are returned. Specify
84 | # EC2 instance states to return as a comma-separated list. This
85 | # option is overridden when 'all_instances' is True.
86 | # instance_states = pending, running, shutting-down, terminated, stopping, stopped
87 |
88 | # By default, only RDS instances in the 'available' state are returned. Set
89 | # 'all_rds_instances' to True return all RDS instances regardless of state.
90 | all_rds_instances = False
91 |
92 | # Include RDS cluster information (Aurora etc.)
93 | include_rds_clusters = False
94 |
95 | # By default, only ElastiCache clusters and nodes in the 'available' state
96 | # are returned. Set 'all_elasticache_clusters' and/or 'all_elastic_nodes'
97 | # to True return all ElastiCache clusters and nodes, regardless of state.
98 | #
99 | # Note that all_elasticache_nodes only applies to listed clusters. That means
100 | # if you set all_elastic_clusters to false, no node will be return from
101 | # unavailable clusters, regardless of the state and to what you set for
102 | # all_elasticache_nodes.
103 | all_elasticache_replication_groups = False
104 | all_elasticache_clusters = False
105 | all_elasticache_nodes = False
106 |
107 | # API calls to EC2 are slow. For this reason, we cache the results of an API
108 | # call. Set this to the path you want cache files to be written to. Two files
109 | # will be written to this directory:
110 | # - ansible-ec2.cache
111 | # - ansible-ec2.index
112 | cache_path = ~/.ansible/tmp
113 |
114 | # The number of seconds a cache file is considered valid. After this many
115 | # seconds, a new API call will be made, and the cache file will be updated.
116 | # To disable the cache, set this value to 0
117 | cache_max_age = 300
118 |
119 | # Organize groups into a nested/hierarchy instead of a flat namespace.
120 | nested_groups = False
121 |
122 | # Replace - tags when creating groups to avoid issues with ansible
123 | replace_dash_in_groups = True
124 |
125 | # If set to true, any tag of the form "a,b,c" is expanded into a list
126 | # and the results are used to create additional tag_* inventory groups.
127 | expand_csv_tags = False
128 |
129 | # The EC2 inventory output can become very large. To manage its size,
130 | # configure which groups should be created.
131 | group_by_instance_id = True
132 | group_by_region = True
133 | group_by_availability_zone = True
134 | group_by_aws_account = False
135 | group_by_ami_id = True
136 | group_by_instance_type = True
137 | group_by_instance_state = False
138 | group_by_platform = True
139 | group_by_key_pair = True
140 | group_by_vpc_id = True
141 | group_by_security_group = True
142 | group_by_tag_keys = True
143 | group_by_tag_none = True
144 | group_by_route53_names = True
145 | group_by_rds_engine = True
146 | group_by_rds_parameter_group = True
147 | group_by_elasticache_engine = True
148 | group_by_elasticache_cluster = True
149 | group_by_elasticache_parameter_group = True
150 | group_by_elasticache_replication_group = True
151 |
152 | # If you only want to include hosts that match a certain regular expression
153 | # pattern_include = staging-*
154 |
155 | # If you want to exclude any hosts that match a certain regular expression
156 | # pattern_exclude = staging-*
157 |
158 | # Instance filters can be used to control which instances are retrieved for
159 | # inventory. For the full list of possible filters, please read the EC2 API
160 | # docs: http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeInstances.html#query-DescribeInstances-filters
161 | # Filters are key/value pairs separated by '=', to list multiple filters use
162 | # a list separated by commas. To "AND" criteria together, use "&". Note that
163 | # the "AND" is not useful along with stack_filters and so such usage is not allowed.
164 | # See examples below.
165 |
166 | # If you want to apply multiple filters simultaneously, set stack_filters to
167 | # True. Default behaviour is to combine the results of all filters. Stacking
168 | # allows the use of multiple conditions to filter down, for example by
169 | # environment and type of host.
170 | stack_filters = False
171 |
172 | # Retrieve only instances with (key=value) env=staging tag
173 | # instance_filters = tag:env=staging
174 |
175 | # Retrieve only instances with role=webservers OR role=dbservers tag
176 | # instance_filters = tag:role=webservers,tag:role=dbservers
177 |
178 | # Retrieve only t1.micro instances OR instances with tag env=staging
179 | # instance_filters = instance-type=t1.micro,tag:env=staging
180 |
181 | # You can use wildcards in filter values also. Below will list instances which
182 | # tag Name value matches webservers1*
183 | # (ex. webservers15, webservers1a, webservers123 etc)
184 | # instance_filters = tag:Name=webservers1*
185 |
186 | # Retrieve only instances of type t1.micro that also have tag env=stage
187 | # instance_filters = instance-type=t1.micro&tag:env=stage
188 |
189 | # Retrieve instances of type t1.micro AND tag env=stage, as well as any instance
190 | # that are of type m3.large, regardless of env tag
191 | # instance_filters = instance-type=t1.micro&tag:env=stage,instance-type=m3.large
192 |
193 | # An IAM role can be assumed, so all requests are run as that role.
194 | # This can be useful for connecting across different accounts, or to limit user
195 | # access
196 | # iam_role = role-arn
197 |
198 | # A boto configuration profile may be used to separate out credentials
199 | # see https://boto.readthedocs.io/en/latest/boto_config_tut.html
200 | # boto_profile = some-boto-profile-name
201 |
202 |
203 | [credentials]
204 |
205 | # The AWS credentials can optionally be specified here. Credentials specified
206 | # here are ignored if the environment variable AWS_ACCESS_KEY_ID or
207 | # AWS_PROFILE is set, or if the boto_profile property above is set.
208 | #
209 | # Supplying AWS credentials here is not recommended, as it introduces
210 | # non-trivial security concerns. When going down this route, please make sure
211 | # to set access permissions for this file correctly, e.g. handle it the same
212 | # way as you would a private SSH key.
213 | #
214 | # Unlike the boto and AWS configure files, this section does not support
215 | # profiles.
216 | #
217 | # aws_access_key_id = AXXXXXXXXXXXXXX
218 | # aws_secret_access_key = XXXXXXXXXXXXXXXXXXX
219 | # aws_security_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXX
220 |
--------------------------------------------------------------------------------
/playbooks/ec2/ec2.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | '''
4 | EC2 external inventory script
5 | =================================
6 |
7 | Generates inventory that Ansible can understand by making API request to
8 | AWS EC2 using the Boto library.
9 |
10 | NOTE: This script assumes Ansible is being executed where the environment
11 | variables needed for Boto have already been set:
12 | export AWS_ACCESS_KEY_ID='AK123'
13 | export AWS_SECRET_ACCESS_KEY='abc123'
14 |
15 | Optional region environment variable if region is 'auto'
16 |
17 | This script also assumes that there is an ec2.ini file alongside it. To specify a
18 | different path to ec2.ini, define the EC2_INI_PATH environment variable:
19 |
20 | export EC2_INI_PATH=/path/to/my_ec2.ini
21 |
22 | If you're using eucalyptus you need to set the above variables and
23 | you need to define:
24 |
25 | export EC2_URL=http://hostname_of_your_cc:port/services/Eucalyptus
26 |
27 | If you're using boto profiles (requires boto>=2.24.0) you can choose a profile
28 | using the --boto-profile command line argument (e.g. ec2.py --boto-profile prod) or using
29 | the AWS_PROFILE variable:
30 |
31 | AWS_PROFILE=prod ansible-playbook -i ec2.py myplaybook.yml
32 |
33 | For more details, see: http://docs.pythonboto.org/en/latest/boto_config_tut.html
34 |
35 | You can filter for specific EC2 instances by creating an environment variable
36 | named EC2_INSTANCE_FILTERS, which has the same format as the instance_filters
37 | entry documented in ec2.ini. For example, to find all hosts whose name begins
38 | with 'webserver', one might use:
39 |
40 | export EC2_INSTANCE_FILTERS='tag:Name=webserver*'
41 |
42 | When run against a specific host, this script returns the following variables:
43 | - ec2_ami_launch_index
44 | - ec2_architecture
45 | - ec2_association
46 | - ec2_attachTime
47 | - ec2_attachment
48 | - ec2_attachmentId
49 | - ec2_block_devices
50 | - ec2_client_token
51 | - ec2_deleteOnTermination
52 | - ec2_description
53 | - ec2_deviceIndex
54 | - ec2_dns_name
55 | - ec2_eventsSet
56 | - ec2_group_name
57 | - ec2_hypervisor
58 | - ec2_id
59 | - ec2_image_id
60 | - ec2_instanceState
61 | - ec2_instance_type
62 | - ec2_ipOwnerId
63 | - ec2_ip_address
64 | - ec2_item
65 | - ec2_kernel
66 | - ec2_key_name
67 | - ec2_launch_time
68 | - ec2_monitored
69 | - ec2_monitoring
70 | - ec2_networkInterfaceId
71 | - ec2_ownerId
72 | - ec2_persistent
73 | - ec2_placement
74 | - ec2_platform
75 | - ec2_previous_state
76 | - ec2_private_dns_name
77 | - ec2_private_ip_address
78 | - ec2_publicIp
79 | - ec2_public_dns_name
80 | - ec2_ramdisk
81 | - ec2_reason
82 | - ec2_region
83 | - ec2_requester_id
84 | - ec2_root_device_name
85 | - ec2_root_device_type
86 | - ec2_security_group_ids
87 | - ec2_security_group_names
88 | - ec2_shutdown_state
89 | - ec2_sourceDestCheck
90 | - ec2_spot_instance_request_id
91 | - ec2_state
92 | - ec2_state_code
93 | - ec2_state_reason
94 | - ec2_status
95 | - ec2_subnet_id
96 | - ec2_tenancy
97 | - ec2_virtualization_type
98 | - ec2_vpc_id
99 |
100 | These variables are pulled out of a boto.ec2.instance object. There is a lack of
101 | consistency with variable spellings (camelCase and underscores) since this
102 | just loops through all variables the object exposes. It is preferred to use the
103 | ones with underscores when multiple exist.
104 |
105 | In addition, if an instance has AWS tags associated with it, each tag is a new
106 | variable named:
107 | - ec2_tag_[Key] = [Value]
108 |
109 | Security groups are comma-separated in 'ec2_security_group_ids' and
110 | 'ec2_security_group_names'.
111 |
112 | When destination_format and destination_format_tags are specified
113 | the destination_format can be built from the instance tags and attributes.
114 | The behavior will first check the user defined tags, then proceed to
115 | check instance attributes, and finally if neither are found 'nil' will
116 | be used instead.
117 |
118 | 'my_instance': {
119 | 'region': 'us-east-1', # attribute
120 | 'availability_zone': 'us-east-1a', # attribute
121 | 'private_dns_name': '172.31.0.1', # attribute
122 | 'ec2_tag_deployment': 'blue', # tag
123 | 'ec2_tag_clusterid': 'ansible', # tag
124 | 'ec2_tag_Name': 'webserver', # tag
125 | ...
126 | }
127 |
128 | Inside of the ec2.ini file the following settings are specified:
129 | ...
130 | destination_format: {0}-{1}-{2}-{3}
131 | destination_format_tags: Name,clusterid,deployment,private_dns_name
132 | ...
133 |
134 | These settings would produce a destination_format as the following:
135 | 'webserver-ansible-blue-172.31.0.1'
136 | '''
137 |
138 | # (c) 2012, Peter Sankauskas
139 | #
140 | # This file is part of Ansible,
141 | #
142 | # Ansible is free software: you can redistribute it and/or modify
143 | # it under the terms of the GNU General Public License as published by
144 | # the Free Software Foundation, either version 3 of the License, or
145 | # (at your option) any later version.
146 | #
147 | # Ansible is distributed in the hope that it will be useful,
148 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
149 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
150 | # GNU General Public License for more details.
151 | #
152 | # You should have received a copy of the GNU General Public License
153 | # along with Ansible. If not, see .
154 |
155 | ######################################################################
156 |
157 | import sys
158 | import os
159 | import argparse
160 | import re
161 | from time import time
162 | import boto
163 | from boto import ec2
164 | from boto import rds
165 | from boto import elasticache
166 | from boto import route53
167 | from boto import sts
168 | import six
169 |
170 | from ansible.module_utils import ec2 as ec2_utils
171 |
172 | HAS_BOTO3 = False
173 | try:
174 | import boto3 # noqa
175 | HAS_BOTO3 = True
176 | except ImportError:
177 | pass
178 |
179 | from six.moves import configparser
180 | from collections import defaultdict
181 |
182 | import json
183 |
184 | DEFAULTS = {
185 | 'all_elasticache_clusters': 'False',
186 | 'all_elasticache_nodes': 'False',
187 | 'all_elasticache_replication_groups': 'False',
188 | 'all_instances': 'False',
189 | 'all_rds_instances': 'False',
190 | 'aws_access_key_id': None,
191 | 'aws_secret_access_key': None,
192 | 'aws_security_token': None,
193 | 'boto_profile': None,
194 | 'cache_max_age': '300',
195 | 'cache_path': '~/.ansible/tmp',
196 | 'destination_variable': 'public_dns_name',
197 | 'elasticache': 'True',
198 | 'eucalyptus': 'False',
199 | 'eucalyptus_host': None,
200 | 'expand_csv_tags': 'False',
201 | 'group_by_ami_id': 'True',
202 | 'group_by_availability_zone': 'True',
203 | 'group_by_aws_account': 'False',
204 | 'group_by_elasticache_cluster': 'True',
205 | 'group_by_elasticache_engine': 'True',
206 | 'group_by_elasticache_parameter_group': 'True',
207 | 'group_by_elasticache_replication_group': 'True',
208 | 'group_by_instance_id': 'True',
209 | 'group_by_instance_state': 'False',
210 | 'group_by_instance_type': 'True',
211 | 'group_by_key_pair': 'True',
212 | 'group_by_platform': 'True',
213 | 'group_by_rds_engine': 'True',
214 | 'group_by_rds_parameter_group': 'True',
215 | 'group_by_region': 'True',
216 | 'group_by_route53_names': 'True',
217 | 'group_by_security_group': 'True',
218 | 'group_by_tag_keys': 'True',
219 | 'group_by_tag_none': 'True',
220 | 'group_by_vpc_id': 'True',
221 | 'hostname_variable': None,
222 | 'iam_role': None,
223 | 'include_rds_clusters': 'False',
224 | 'nested_groups': 'False',
225 | 'pattern_exclude': None,
226 | 'pattern_include': None,
227 | 'rds': 'False',
228 | 'regions': 'all',
229 | 'regions_exclude': 'us-gov-west-1, cn-north-1',
230 | 'replace_dash_in_groups': 'True',
231 | 'route53': 'False',
232 | 'route53_excluded_zones': '',
233 | 'route53_hostnames': None,
234 | 'stack_filters': 'False',
235 | 'vpc_destination_variable': 'ip_address'
236 | }
237 |
238 |
239 | class Ec2Inventory(object):
240 |
241 | def _empty_inventory(self):
242 | return {"_meta": {"hostvars": {}}}
243 |
244 | def __init__(self):
245 | ''' Main execution path '''
246 |
247 | # Inventory grouped by instance IDs, tags, security groups, regions,
248 | # and availability zones
249 | self.inventory = self._empty_inventory()
250 |
251 | self.aws_account_id = None
252 |
253 | # Index of hostname (address) to instance ID
254 | self.index = {}
255 |
256 | # Boto profile to use (if any)
257 | self.boto_profile = None
258 |
259 | # AWS credentials.
260 | self.credentials = {}
261 |
262 | # Read settings and parse CLI arguments
263 | self.parse_cli_args()
264 | self.read_settings()
265 |
266 | # Make sure that profile_name is not passed at all if not set
267 | # as pre 2.24 boto will fall over otherwise
268 | if self.boto_profile:
269 | if not hasattr(boto.ec2.EC2Connection, 'profile_name'):
270 | self.fail_with_error("boto version must be >= 2.24 to use profile")
271 |
272 | # Cache
273 | if self.args.refresh_cache:
274 | self.do_api_calls_update_cache()
275 | elif not self.is_cache_valid():
276 | self.do_api_calls_update_cache()
277 |
278 | # Data to print
279 | if self.args.host:
280 | data_to_print = self.get_host_info()
281 |
282 | elif self.args.list:
283 | # Display list of instances for inventory
284 | if self.inventory == self._empty_inventory():
285 | data_to_print = self.get_inventory_from_cache()
286 | else:
287 | data_to_print = self.json_format_dict(self.inventory, True)
288 |
289 | print(data_to_print)
290 |
291 | def is_cache_valid(self):
292 | ''' Determines if the cache files have expired, or if it is still valid '''
293 |
294 | if os.path.isfile(self.cache_path_cache):
295 | mod_time = os.path.getmtime(self.cache_path_cache)
296 | current_time = time()
297 | if (mod_time + self.cache_max_age) > current_time:
298 | if os.path.isfile(self.cache_path_index):
299 | return True
300 |
301 | return False
302 |
303 | def read_settings(self):
304 | ''' Reads the settings from the ec2.ini file '''
305 |
306 | scriptbasename = __file__
307 | scriptbasename = os.path.basename(scriptbasename)
308 | scriptbasename = scriptbasename.replace('.py', '')
309 |
310 | defaults = {
311 | 'ec2': {
312 | 'ini_fallback': os.path.join(os.path.dirname(__file__), 'ec2.ini'),
313 | 'ini_path': os.path.join(os.path.dirname(__file__), '%s.ini' % scriptbasename)
314 | }
315 | }
316 |
317 | if six.PY3:
318 | config = configparser.ConfigParser(DEFAULTS)
319 | else:
320 | config = configparser.SafeConfigParser(DEFAULTS)
321 | ec2_ini_path = os.environ.get('EC2_INI_PATH', defaults['ec2']['ini_path'])
322 | ec2_ini_path = os.path.expanduser(os.path.expandvars(ec2_ini_path))
323 |
324 | if not os.path.isfile(ec2_ini_path):
325 | ec2_ini_path = os.path.expanduser(defaults['ec2']['ini_fallback'])
326 |
327 | if os.path.isfile(ec2_ini_path):
328 | config.read(ec2_ini_path)
329 |
330 | # Add empty sections if they don't exist
331 | try:
332 | config.add_section('ec2')
333 | except configparser.DuplicateSectionError:
334 | pass
335 |
336 | try:
337 | config.add_section('credentials')
338 | except configparser.DuplicateSectionError:
339 | pass
340 |
341 | # is eucalyptus?
342 | self.eucalyptus = config.getboolean('ec2', 'eucalyptus')
343 | self.eucalyptus_host = config.get('ec2', 'eucalyptus_host')
344 |
345 | # Regions
346 | self.regions = []
347 | config_regions = config.get('ec2', 'regions')
348 | if (config_regions == 'all'):
349 | if self.eucalyptus_host:
350 | self.regions.append(boto.connect_euca(host=self.eucalyptus_host).region.name, **self.credentials)
351 | else:
352 | config_regions_exclude = config.get('ec2', 'regions_exclude')
353 |
354 | for region_info in ec2.regions():
355 | if region_info.name not in config_regions_exclude:
356 | self.regions.append(region_info.name)
357 | else:
358 | self.regions = config_regions.split(",")
359 | if 'auto' in self.regions:
360 | env_region = os.environ.get('AWS_REGION')
361 | if env_region is None:
362 | env_region = os.environ.get('AWS_DEFAULT_REGION')
363 | self.regions = [env_region]
364 |
365 | # Destination addresses
366 | self.destination_variable = config.get('ec2', 'destination_variable')
367 | self.vpc_destination_variable = config.get('ec2', 'vpc_destination_variable')
368 | self.hostname_variable = config.get('ec2', 'hostname_variable')
369 |
370 | if config.has_option('ec2', 'destination_format') and \
371 | config.has_option('ec2', 'destination_format_tags'):
372 | self.destination_format = config.get('ec2', 'destination_format')
373 | self.destination_format_tags = config.get('ec2', 'destination_format_tags').split(',')
374 | else:
375 | self.destination_format = None
376 | self.destination_format_tags = None
377 |
378 | # Route53
379 | self.route53_enabled = config.getboolean('ec2', 'route53')
380 | self.route53_hostnames = config.get('ec2', 'route53_hostnames')
381 |
382 | self.route53_excluded_zones = []
383 | self.route53_excluded_zones = [a for a in config.get('ec2', 'route53_excluded_zones').split(',') if a]
384 |
385 | # Include RDS instances?
386 | self.rds_enabled = config.getboolean('ec2', 'rds')
387 |
388 | # Include RDS cluster instances?
389 | self.include_rds_clusters = config.getboolean('ec2', 'include_rds_clusters')
390 |
391 | # Include ElastiCache instances?
392 | self.elasticache_enabled = config.getboolean('ec2', 'elasticache')
393 |
394 | # Return all EC2 instances?
395 | self.all_instances = config.getboolean('ec2', 'all_instances')
396 |
397 | # Instance states to be gathered in inventory. Default is 'running'.
398 | # Setting 'all_instances' to 'yes' overrides this option.
399 | ec2_valid_instance_states = [
400 | 'pending',
401 | 'running',
402 | 'shutting-down',
403 | 'terminated',
404 | 'stopping',
405 | 'stopped'
406 | ]
407 | self.ec2_instance_states = []
408 | if self.all_instances:
409 | self.ec2_instance_states = ec2_valid_instance_states
410 | elif config.has_option('ec2', 'instance_states'):
411 | for instance_state in config.get('ec2', 'instance_states').split(','):
412 | instance_state = instance_state.strip()
413 | if instance_state not in ec2_valid_instance_states:
414 | continue
415 | self.ec2_instance_states.append(instance_state)
416 | else:
417 | self.ec2_instance_states = ['running']
418 |
419 | # Return all RDS instances? (if RDS is enabled)
420 | self.all_rds_instances = config.getboolean('ec2', 'all_rds_instances')
421 |
422 | # Return all ElastiCache replication groups? (if ElastiCache is enabled)
423 | self.all_elasticache_replication_groups = config.getboolean('ec2', 'all_elasticache_replication_groups')
424 |
425 | # Return all ElastiCache clusters? (if ElastiCache is enabled)
426 | self.all_elasticache_clusters = config.getboolean('ec2', 'all_elasticache_clusters')
427 |
428 | # Return all ElastiCache nodes? (if ElastiCache is enabled)
429 | self.all_elasticache_nodes = config.getboolean('ec2', 'all_elasticache_nodes')
430 |
431 | # boto configuration profile (prefer CLI argument then environment variables then config file)
432 | self.boto_profile = self.args.boto_profile or \
433 | os.environ.get('AWS_PROFILE') or \
434 | config.get('ec2', 'boto_profile')
435 |
436 | # AWS credentials (prefer environment variables)
437 | if not (self.boto_profile or os.environ.get('AWS_ACCESS_KEY_ID') or
438 | os.environ.get('AWS_PROFILE')):
439 |
440 | aws_access_key_id = config.get('credentials', 'aws_access_key_id')
441 | aws_secret_access_key = config.get('credentials', 'aws_secret_access_key')
442 | aws_security_token = config.get('credentials', 'aws_security_token')
443 |
444 | if aws_access_key_id:
445 | self.credentials = {
446 | 'aws_access_key_id': aws_access_key_id,
447 | 'aws_secret_access_key': aws_secret_access_key
448 | }
449 | if aws_security_token:
450 | self.credentials['security_token'] = aws_security_token
451 |
452 | # Cache related
453 | cache_dir = os.path.expanduser(config.get('ec2', 'cache_path'))
454 | if self.boto_profile:
455 | cache_dir = os.path.join(cache_dir, 'profile_' + self.boto_profile)
456 | if not os.path.exists(cache_dir):
457 | os.makedirs(cache_dir)
458 |
459 | cache_name = 'ansible-ec2'
460 | cache_id = self.boto_profile or os.environ.get('AWS_ACCESS_KEY_ID', self.credentials.get('aws_access_key_id'))
461 | if cache_id:
462 | cache_name = '%s-%s' % (cache_name, cache_id)
463 | cache_name += '-' + str(abs(hash(__file__)))[1:7]
464 | self.cache_path_cache = os.path.join(cache_dir, "%s.cache" % cache_name)
465 | self.cache_path_index = os.path.join(cache_dir, "%s.index" % cache_name)
466 | self.cache_max_age = config.getint('ec2', 'cache_max_age')
467 |
468 | self.expand_csv_tags = config.getboolean('ec2', 'expand_csv_tags')
469 |
470 | # Configure nested groups instead of flat namespace.
471 | self.nested_groups = config.getboolean('ec2', 'nested_groups')
472 |
473 | # Replace dash or not in group names
474 | self.replace_dash_in_groups = config.getboolean('ec2', 'replace_dash_in_groups')
475 |
476 | # IAM role to assume for connection
477 | self.iam_role = config.get('ec2', 'iam_role')
478 |
479 | # Configure which groups should be created.
480 |
481 | group_by_options = [a for a in DEFAULTS if a.startswith('group_by')]
482 | for option in group_by_options:
483 | setattr(self, option, config.getboolean('ec2', option))
484 |
485 | # Do we need to just include hosts that match a pattern?
486 | self.pattern_include = config.get('ec2', 'pattern_include')
487 | if self.pattern_include:
488 | self.pattern_include = re.compile(self.pattern_include)
489 |
490 | # Do we need to exclude hosts that match a pattern?
491 | self.pattern_exclude = config.get('ec2', 'pattern_exclude')
492 | if self.pattern_exclude:
493 | self.pattern_exclude = re.compile(self.pattern_exclude)
494 |
495 | # Do we want to stack multiple filters?
496 | self.stack_filters = config.getboolean('ec2', 'stack_filters')
497 |
498 | # Instance filters (see boto and EC2 API docs). Ignore invalid filters.
499 | self.ec2_instance_filters = []
500 |
501 | if config.has_option('ec2', 'instance_filters') or 'EC2_INSTANCE_FILTERS' in os.environ:
502 | filters = os.getenv('EC2_INSTANCE_FILTERS', config.get('ec2', 'instance_filters') if config.has_option('ec2', 'instance_filters') else '')
503 |
504 | if self.stack_filters and '&' in filters:
505 | self.fail_with_error("AND filters along with stack_filter enabled is not supported.\n")
506 |
507 | filter_sets = [f for f in filters.split(',') if f]
508 |
509 | for filter_set in filter_sets:
510 | filters = {}
511 | filter_set = filter_set.strip()
512 | for instance_filter in filter_set.split("&"):
513 | instance_filter = instance_filter.strip()
514 | if not instance_filter or '=' not in instance_filter:
515 | continue
516 | filter_key, filter_value = [x.strip() for x in instance_filter.split('=', 1)]
517 | if not filter_key:
518 | continue
519 | filters[filter_key] = filter_value
520 | self.ec2_instance_filters.append(filters.copy())
521 |
522 | def parse_cli_args(self):
523 | ''' Command line argument processing '''
524 |
525 | parser = argparse.ArgumentParser(description='Produce an Ansible Inventory file based on EC2')
526 | parser.add_argument('--list', action='store_true', default=True,
527 | help='List instances (default: True)')
528 | parser.add_argument('--host', action='store',
529 | help='Get all the variables about a specific instance')
530 | parser.add_argument('--refresh-cache', action='store_true', default=False,
531 | help='Force refresh of cache by making API requests to EC2 (default: False - use cache files)')
532 | parser.add_argument('--profile', '--boto-profile', action='store', dest='boto_profile',
533 | help='Use boto profile for connections to EC2')
534 | self.args = parser.parse_args()
535 |
536 | def do_api_calls_update_cache(self):
537 | ''' Do API calls to each region, and save data in cache files '''
538 |
539 | if self.route53_enabled:
540 | self.get_route53_records()
541 |
542 | for region in self.regions:
543 | self.get_instances_by_region(region)
544 | if self.rds_enabled:
545 | self.get_rds_instances_by_region(region)
546 | if self.elasticache_enabled:
547 | self.get_elasticache_clusters_by_region(region)
548 | self.get_elasticache_replication_groups_by_region(region)
549 | if self.include_rds_clusters:
550 | self.include_rds_clusters_by_region(region)
551 |
552 | self.write_to_cache(self.inventory, self.cache_path_cache)
553 | self.write_to_cache(self.index, self.cache_path_index)
554 |
555 | def connect(self, region):
556 | ''' create connection to api server'''
557 | if self.eucalyptus:
558 | conn = boto.connect_euca(host=self.eucalyptus_host, **self.credentials)
559 | conn.APIVersion = '2010-08-31'
560 | else:
561 | conn = self.connect_to_aws(ec2, region)
562 | return conn
563 |
564 | def boto_fix_security_token_in_profile(self, connect_args):
565 | ''' monkey patch for boto issue boto/boto#2100 '''
566 | profile = 'profile ' + self.boto_profile
567 | if boto.config.has_option(profile, 'aws_security_token'):
568 | connect_args['security_token'] = boto.config.get(profile, 'aws_security_token')
569 | return connect_args
570 |
571 | def connect_to_aws(self, module, region):
572 | connect_args = self.credentials
573 |
574 | # only pass the profile name if it's set (as it is not supported by older boto versions)
575 | if self.boto_profile:
576 | connect_args['profile_name'] = self.boto_profile
577 | self.boto_fix_security_token_in_profile(connect_args)
578 |
579 | if self.iam_role:
580 | sts_conn = sts.connect_to_region(region, **connect_args)
581 | role = sts_conn.assume_role(self.iam_role, 'ansible_dynamic_inventory')
582 | connect_args['aws_access_key_id'] = role.credentials.access_key
583 | connect_args['aws_secret_access_key'] = role.credentials.secret_key
584 | connect_args['security_token'] = role.credentials.session_token
585 |
586 | conn = module.connect_to_region(region, **connect_args)
587 | # connect_to_region will fail "silently" by returning None if the region name is wrong or not supported
588 | if conn is None:
589 | self.fail_with_error("region name: %s likely not supported, or AWS is down. connection to region failed." % region)
590 | return conn
591 |
592 | def get_instances_by_region(self, region):
593 | ''' Makes an AWS EC2 API call to the list of instances in a particular
594 | region '''
595 |
596 | try:
597 | conn = self.connect(region)
598 | reservations = []
599 | if self.ec2_instance_filters:
600 | if self.stack_filters:
601 | filters_dict = {}
602 | for filters in self.ec2_instance_filters:
603 | filters_dict.update(filters)
604 | reservations.extend(conn.get_all_instances(filters=filters_dict))
605 | else:
606 | for filters in self.ec2_instance_filters:
607 | reservations.extend(conn.get_all_instances(filters=filters))
608 | else:
609 | reservations = conn.get_all_instances()
610 |
611 | # Pull the tags back in a second step
612 | # AWS are on record as saying that the tags fetched in the first `get_all_instances` request are not
613 | # reliable and may be missing, and the only way to guarantee they are there is by calling `get_all_tags`
614 | instance_ids = []
615 | for reservation in reservations:
616 | instance_ids.extend([instance.id for instance in reservation.instances])
617 |
618 | max_filter_value = 199
619 | tags = []
620 | for i in range(0, len(instance_ids), max_filter_value):
621 | tags.extend(conn.get_all_tags(filters={'resource-type': 'instance', 'resource-id': instance_ids[i:i + max_filter_value]}))
622 |
623 | tags_by_instance_id = defaultdict(dict)
624 | for tag in tags:
625 | tags_by_instance_id[tag.res_id][tag.name] = tag.value
626 |
627 | if (not self.aws_account_id) and reservations:
628 | self.aws_account_id = reservations[0].owner_id
629 |
630 | for reservation in reservations:
631 | for instance in reservation.instances:
632 | instance.tags = tags_by_instance_id[instance.id]
633 | self.add_instance(instance, region)
634 |
635 | except boto.exception.BotoServerError as e:
636 | if e.error_code == 'AuthFailure':
637 | error = self.get_auth_error_message()
638 | else:
639 | backend = 'Eucalyptus' if self.eucalyptus else 'AWS'
640 | error = "Error connecting to %s backend.\n%s" % (backend, e.message)
641 | self.fail_with_error(error, 'getting EC2 instances')
642 |
643 | def tags_match_filters(self, tags):
644 | ''' return True if given tags match configured filters '''
645 | if not self.ec2_instance_filters:
646 | return True
647 |
648 | for filters in self.ec2_instance_filters:
649 | for filter_name, filter_value in filters.items():
650 | if filter_name[:4] != 'tag:':
651 | continue
652 | filter_name = filter_name[4:]
653 | if filter_name not in tags:
654 | if self.stack_filters:
655 | return False
656 | continue
657 | if isinstance(filter_value, list):
658 | if self.stack_filters and tags[filter_name] not in filter_value:
659 | return False
660 | if not self.stack_filters and tags[filter_name] in filter_value:
661 | return True
662 | if isinstance(filter_value, six.string_types):
663 | if self.stack_filters and tags[filter_name] != filter_value:
664 | return False
665 | if not self.stack_filters and tags[filter_name] == filter_value:
666 | return True
667 |
668 | return self.stack_filters
669 |
670 | def get_rds_instances_by_region(self, region):
671 | ''' Makes an AWS API call to the list of RDS instances in a particular
672 | region '''
673 |
674 | if not HAS_BOTO3:
675 | self.fail_with_error("Working with RDS instances requires boto3 - please install boto3 and try again",
676 | "getting RDS instances")
677 |
678 | client = ec2_utils.boto3_inventory_conn('client', 'rds', region, **self.credentials)
679 | db_instances = client.describe_db_instances()
680 |
681 | try:
682 | conn = self.connect_to_aws(rds, region)
683 | if conn:
684 | marker = None
685 | while True:
686 | instances = conn.get_all_dbinstances(marker=marker)
687 | marker = instances.marker
688 | for index, instance in enumerate(instances):
689 | # Add tags to instances.
690 | instance.arn = db_instances['DBInstances'][index]['DBInstanceArn']
691 | tags = client.list_tags_for_resource(ResourceName=instance.arn)['TagList']
692 | instance.tags = {}
693 | for tag in tags:
694 | instance.tags[tag['Key']] = tag['Value']
695 | if self.tags_match_filters(instance.tags):
696 | self.add_rds_instance(instance, region)
697 | if not marker:
698 | break
699 | except boto.exception.BotoServerError as e:
700 | error = e.reason
701 |
702 | if e.error_code == 'AuthFailure':
703 | error = self.get_auth_error_message()
704 | elif e.error_code == "OptInRequired":
705 | error = "RDS hasn't been enabled for this account yet. " \
706 | "You must either log in to the RDS service through the AWS console to enable it, " \
707 | "or set 'rds = False' in ec2.ini"
708 | elif not e.reason == "Forbidden":
709 | error = "Looks like AWS RDS is down:\n%s" % e.message
710 | self.fail_with_error(error, 'getting RDS instances')
711 |
712 | def include_rds_clusters_by_region(self, region):
713 | if not HAS_BOTO3:
714 | self.fail_with_error("Working with RDS clusters requires boto3 - please install boto3 and try again",
715 | "getting RDS clusters")
716 |
717 | client = ec2_utils.boto3_inventory_conn('client', 'rds', region, **self.credentials)
718 |
719 | marker, clusters = '', []
720 | while marker is not None:
721 | resp = client.describe_db_clusters(Marker=marker)
722 | clusters.extend(resp["DBClusters"])
723 | marker = resp.get('Marker', None)
724 |
725 | account_id = boto.connect_iam().get_user().arn.split(':')[4]
726 | c_dict = {}
727 | for c in clusters:
728 | # remove these datetime objects as there is no serialisation to json
729 | # currently in place and we don't need the data yet
730 | if 'EarliestRestorableTime' in c:
731 | del c['EarliestRestorableTime']
732 | if 'LatestRestorableTime' in c:
733 | del c['LatestRestorableTime']
734 |
735 | if not self.ec2_instance_filters:
736 | matches_filter = True
737 | else:
738 | matches_filter = False
739 |
740 | try:
741 | # arn:aws:rds::::
742 | tags = client.list_tags_for_resource(
743 | ResourceName='arn:aws:rds:' + region + ':' + account_id + ':cluster:' + c['DBClusterIdentifier'])
744 | c['Tags'] = tags['TagList']
745 |
746 | if self.ec2_instance_filters:
747 | for filters in self.ec2_instance_filters:
748 | for filter_key, filter_values in filters.items():
749 | # get AWS tag key e.g. tag:env will be 'env'
750 | tag_name = filter_key.split(":", 1)[1]
751 | # Filter values is a list (if you put multiple values for the same tag name)
752 | matches_filter = any(d['Key'] == tag_name and d['Value'] in filter_values for d in c['Tags'])
753 |
754 | if matches_filter:
755 | # it matches a filter, so stop looking for further matches
756 | break
757 |
758 | if matches_filter:
759 | break
760 |
761 | except Exception as e:
762 | if e.message.find('DBInstanceNotFound') >= 0:
763 | # AWS RDS bug (2016-01-06) means deletion does not fully complete and leave an 'empty' cluster.
764 | # Ignore errors when trying to find tags for these
765 | pass
766 |
767 | # ignore empty clusters caused by AWS bug
768 | if len(c['DBClusterMembers']) == 0:
769 | continue
770 | elif matches_filter:
771 | c_dict[c['DBClusterIdentifier']] = c
772 |
773 | self.inventory['db_clusters'] = c_dict
774 |
775 | def get_elasticache_clusters_by_region(self, region):
776 | ''' Makes an AWS API call to the list of ElastiCache clusters (with
777 | nodes' info) in a particular region.'''
778 |
779 | # ElastiCache boto module doesn't provide a get_all_instances method,
780 | # that's why we need to call describe directly (it would be called by
781 | # the shorthand method anyway...)
782 | clusters = []
783 | try:
784 | conn = self.connect_to_aws(elasticache, region)
785 | if conn:
786 | # show_cache_node_info = True
787 | # because we also want nodes' information
788 | _marker = 1
789 | while _marker:
790 | if _marker == 1:
791 | _marker = None
792 | response = conn.describe_cache_clusters(None, None, _marker, True)
793 | _marker = response['DescribeCacheClustersResponse']['DescribeCacheClustersResult']['Marker']
794 | try:
795 | # Boto also doesn't provide wrapper classes to CacheClusters or
796 | # CacheNodes. Because of that we can't make use of the get_list
797 | # method in the AWSQueryConnection. Let's do the work manually
798 | clusters = clusters + response['DescribeCacheClustersResponse']['DescribeCacheClustersResult']['CacheClusters']
799 | except KeyError as e:
800 | error = "ElastiCache query to AWS failed (unexpected format)."
801 | self.fail_with_error(error, 'getting ElastiCache clusters')
802 | except boto.exception.BotoServerError as e:
803 | error = e.reason
804 |
805 | if e.error_code == 'AuthFailure':
806 | error = self.get_auth_error_message()
807 | elif e.error_code == "OptInRequired":
808 | error = "ElastiCache hasn't been enabled for this account yet. " \
809 | "You must either log in to the ElastiCache service through the AWS console to enable it, " \
810 | "or set 'elasticache = False' in ec2.ini"
811 | elif not e.reason == "Forbidden":
812 | error = "Looks like AWS ElastiCache is down:\n%s" % e.message
813 | self.fail_with_error(error, 'getting ElastiCache clusters')
814 |
815 | for cluster in clusters:
816 | self.add_elasticache_cluster(cluster, region)
817 |
818 | def get_elasticache_replication_groups_by_region(self, region):
819 | ''' Makes an AWS API call to the list of ElastiCache replication groups
820 | in a particular region.'''
821 |
822 | # ElastiCache boto module doesn't provide a get_all_instances method,
823 | # that's why we need to call describe directly (it would be called by
824 | # the shorthand method anyway...)
825 | try:
826 | conn = self.connect_to_aws(elasticache, region)
827 | if conn:
828 | response = conn.describe_replication_groups()
829 |
830 | except boto.exception.BotoServerError as e:
831 | error = e.reason
832 |
833 | if e.error_code == 'AuthFailure':
834 | error = self.get_auth_error_message()
835 | if not e.reason == "Forbidden":
836 | error = "Looks like AWS ElastiCache [Replication Groups] is down:\n%s" % e.message
837 | self.fail_with_error(error, 'getting ElastiCache clusters')
838 |
839 | try:
840 | # Boto also doesn't provide wrapper classes to ReplicationGroups
841 | # Because of that we can't make use of the get_list method in the
842 | # AWSQueryConnection. Let's do the work manually
843 | replication_groups = response['DescribeReplicationGroupsResponse']['DescribeReplicationGroupsResult']['ReplicationGroups']
844 |
845 | except KeyError as e:
846 | error = "ElastiCache [Replication Groups] query to AWS failed (unexpected format)."
847 | self.fail_with_error(error, 'getting ElastiCache clusters')
848 |
849 | for replication_group in replication_groups:
850 | self.add_elasticache_replication_group(replication_group, region)
851 |
852 | def get_auth_error_message(self):
853 | ''' create an informative error message if there is an issue authenticating'''
854 | errors = ["Authentication error retrieving ec2 inventory."]
855 | if None in [os.environ.get('AWS_ACCESS_KEY_ID'), os.environ.get('AWS_SECRET_ACCESS_KEY')]:
856 | errors.append(' - No AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY environment vars found')
857 | else:
858 | errors.append(' - AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment vars found but may not be correct')
859 |
860 | boto_paths = ['/etc/boto.cfg', '~/.boto', '~/.aws/credentials']
861 | boto_config_found = [p for p in boto_paths if os.path.isfile(os.path.expanduser(p))]
862 | if len(boto_config_found) > 0:
863 | errors.append(" - Boto configs found at '%s', but the credentials contained may not be correct" % ', '.join(boto_config_found))
864 | else:
865 | errors.append(" - No Boto config found at any expected location '%s'" % ', '.join(boto_paths))
866 |
867 | return '\n'.join(errors)
868 |
869 | def fail_with_error(self, err_msg, err_operation=None):
870 | '''log an error to std err for ansible-playbook to consume and exit'''
871 | if err_operation:
872 | err_msg = 'ERROR: "{err_msg}", while: {err_operation}'.format(
873 | err_msg=err_msg, err_operation=err_operation)
874 | sys.stderr.write(err_msg)
875 | sys.exit(1)
876 |
877 | def get_instance(self, region, instance_id):
878 | conn = self.connect(region)
879 |
880 | reservations = conn.get_all_instances([instance_id])
881 | for reservation in reservations:
882 | for instance in reservation.instances:
883 | return instance
884 |
885 | def add_instance(self, instance, region):
886 | ''' Adds an instance to the inventory and index, as long as it is
887 | addressable '''
888 |
889 | # Only return instances with desired instance states
890 | if instance.state not in self.ec2_instance_states:
891 | return
892 |
893 | # Select the best destination address
894 | # When destination_format and destination_format_tags are specified
895 | # the following code will attempt to find the instance tags first,
896 | # then the instance attributes next, and finally if neither are found
897 | # assign nil for the desired destination format attribute.
898 | if self.destination_format and self.destination_format_tags:
899 | dest_vars = []
900 | inst_tags = getattr(instance, 'tags')
901 | for tag in self.destination_format_tags:
902 | if tag in inst_tags:
903 | dest_vars.append(inst_tags[tag])
904 | elif hasattr(instance, tag):
905 | dest_vars.append(getattr(instance, tag))
906 | else:
907 | dest_vars.append('nil')
908 |
909 | dest = self.destination_format.format(*dest_vars)
910 | elif instance.subnet_id:
911 | dest = getattr(instance, self.vpc_destination_variable, None)
912 | if dest is None:
913 | dest = getattr(instance, 'tags').get(self.vpc_destination_variable, None)
914 | else:
915 | dest = getattr(instance, self.destination_variable, None)
916 | if dest is None:
917 | dest = getattr(instance, 'tags').get(self.destination_variable, None)
918 |
919 | if not dest:
920 | # Skip instances we cannot address (e.g. private VPC subnet)
921 | return
922 |
923 | # Set the inventory name
924 | hostname = None
925 | if self.hostname_variable:
926 | if self.hostname_variable.startswith('tag_'):
927 | hostname = instance.tags.get(self.hostname_variable[4:], None)
928 | else:
929 | hostname = getattr(instance, self.hostname_variable)
930 |
931 | # set the hostname from route53
932 | if self.route53_enabled and self.route53_hostnames:
933 | route53_names = self.get_instance_route53_names(instance)
934 | for name in route53_names:
935 | if name.endswith(self.route53_hostnames):
936 | hostname = name
937 |
938 | # If we can't get a nice hostname, use the destination address
939 | if not hostname:
940 | hostname = dest
941 | # to_safe strips hostname characters like dots, so don't strip route53 hostnames
942 | elif self.route53_enabled and self.route53_hostnames and hostname.endswith(self.route53_hostnames):
943 | hostname = hostname.lower()
944 | else:
945 | hostname = self.to_safe(hostname).lower()
946 |
947 | # if we only want to include hosts that match a pattern, skip those that don't
948 | if self.pattern_include and not self.pattern_include.match(hostname):
949 | return
950 |
951 | # if we need to exclude hosts that match a pattern, skip those
952 | if self.pattern_exclude and self.pattern_exclude.match(hostname):
953 | return
954 |
955 | # Add to index
956 | self.index[hostname] = [region, instance.id]
957 |
958 | # Inventory: Group by instance ID (always a group of 1)
959 | if self.group_by_instance_id:
960 | self.inventory[instance.id] = [hostname]
961 | if self.nested_groups:
962 | self.push_group(self.inventory, 'instances', instance.id)
963 |
964 | # Inventory: Group by region
965 | if self.group_by_region:
966 | self.push(self.inventory, region, hostname)
967 | if self.nested_groups:
968 | self.push_group(self.inventory, 'regions', region)
969 |
970 | # Inventory: Group by availability zone
971 | if self.group_by_availability_zone:
972 | self.push(self.inventory, instance.placement, hostname)
973 | if self.nested_groups:
974 | if self.group_by_region:
975 | self.push_group(self.inventory, region, instance.placement)
976 | self.push_group(self.inventory, 'zones', instance.placement)
977 |
978 | # Inventory: Group by Amazon Machine Image (AMI) ID
979 | if self.group_by_ami_id:
980 | ami_id = self.to_safe(instance.image_id)
981 | self.push(self.inventory, ami_id, hostname)
982 | if self.nested_groups:
983 | self.push_group(self.inventory, 'images', ami_id)
984 |
985 | # Inventory: Group by instance type
986 | if self.group_by_instance_type:
987 | type_name = self.to_safe('type_' + instance.instance_type)
988 | self.push(self.inventory, type_name, hostname)
989 | if self.nested_groups:
990 | self.push_group(self.inventory, 'types', type_name)
991 |
992 | # Inventory: Group by instance state
993 | if self.group_by_instance_state:
994 | state_name = self.to_safe('instance_state_' + instance.state)
995 | self.push(self.inventory, state_name, hostname)
996 | if self.nested_groups:
997 | self.push_group(self.inventory, 'instance_states', state_name)
998 |
999 | # Inventory: Group by platform
1000 | if self.group_by_platform:
1001 | if instance.platform:
1002 | platform = self.to_safe('platform_' + instance.platform)
1003 | else:
1004 | platform = self.to_safe('platform_undefined')
1005 | self.push(self.inventory, platform, hostname)
1006 | if self.nested_groups:
1007 | self.push_group(self.inventory, 'platforms', platform)
1008 |
1009 | # Inventory: Group by key pair
1010 | if self.group_by_key_pair and instance.key_name:
1011 | key_name = self.to_safe('key_' + instance.key_name)
1012 | self.push(self.inventory, key_name, hostname)
1013 | if self.nested_groups:
1014 | self.push_group(self.inventory, 'keys', key_name)
1015 |
1016 | # Inventory: Group by VPC
1017 | if self.group_by_vpc_id and instance.vpc_id:
1018 | vpc_id_name = self.to_safe('vpc_id_' + instance.vpc_id)
1019 | self.push(self.inventory, vpc_id_name, hostname)
1020 | if self.nested_groups:
1021 | self.push_group(self.inventory, 'vpcs', vpc_id_name)
1022 |
1023 | # Inventory: Group by security group
1024 | if self.group_by_security_group:
1025 | try:
1026 | for group in instance.groups:
1027 | key = self.to_safe("security_group_" + group.name)
1028 | self.push(self.inventory, key, hostname)
1029 | if self.nested_groups:
1030 | self.push_group(self.inventory, 'security_groups', key)
1031 | except AttributeError:
1032 | self.fail_with_error('\n'.join(['Package boto seems a bit older.',
1033 | 'Please upgrade boto >= 2.3.0.']))
1034 |
1035 | # Inventory: Group by AWS account ID
1036 | if self.group_by_aws_account:
1037 | self.push(self.inventory, self.aws_account_id, hostname)
1038 | if self.nested_groups:
1039 | self.push_group(self.inventory, 'accounts', self.aws_account_id)
1040 |
1041 | # Inventory: Group by tag keys
1042 | if self.group_by_tag_keys:
1043 | for k, v in instance.tags.items():
1044 | if self.expand_csv_tags and v and ',' in v:
1045 | values = map(lambda x: x.strip(), v.split(','))
1046 | else:
1047 | values = [v]
1048 |
1049 | for v in values:
1050 | if v:
1051 | key = self.to_safe("tag_" + k + "=" + v)
1052 | else:
1053 | key = self.to_safe("tag_" + k)
1054 | self.push(self.inventory, key, hostname)
1055 | if self.nested_groups:
1056 | self.push_group(self.inventory, 'tags', self.to_safe("tag_" + k))
1057 | if v:
1058 | self.push_group(self.inventory, self.to_safe("tag_" + k), key)
1059 |
1060 | # Inventory: Group by Route53 domain names if enabled
1061 | if self.route53_enabled and self.group_by_route53_names:
1062 | route53_names = self.get_instance_route53_names(instance)
1063 | for name in route53_names:
1064 | self.push(self.inventory, name, hostname)
1065 | if self.nested_groups:
1066 | self.push_group(self.inventory, 'route53', name)
1067 |
1068 | # Global Tag: instances without tags
1069 | if self.group_by_tag_none and len(instance.tags) == 0:
1070 | self.push(self.inventory, 'tag_none', hostname)
1071 | if self.nested_groups:
1072 | self.push_group(self.inventory, 'tags', 'tag_none')
1073 |
1074 | # Global Tag: tag all EC2 instances
1075 | self.push(self.inventory, 'ec2', hostname)
1076 |
1077 | self.inventory["_meta"]["hostvars"][hostname] = self.get_host_info_dict_from_instance(instance)
1078 | self.inventory["_meta"]["hostvars"][hostname]['ansible_host'] = dest
1079 |
1080 | def add_rds_instance(self, instance, region):
1081 | ''' Adds an RDS instance to the inventory and index, as long as it is
1082 | addressable '''
1083 |
1084 | # Only want available instances unless all_rds_instances is True
1085 | if not self.all_rds_instances and instance.status != 'available':
1086 | return
1087 |
1088 | # Select the best destination address
1089 | dest = instance.endpoint[0]
1090 |
1091 | if not dest:
1092 | # Skip instances we cannot address (e.g. private VPC subnet)
1093 | return
1094 |
1095 | # Set the inventory name
1096 | hostname = None
1097 | if self.hostname_variable:
1098 | if self.hostname_variable.startswith('tag_'):
1099 | hostname = instance.tags.get(self.hostname_variable[4:], None)
1100 | else:
1101 | hostname = getattr(instance, self.hostname_variable)
1102 |
1103 | # If we can't get a nice hostname, use the destination address
1104 | if not hostname:
1105 | hostname = dest
1106 |
1107 | hostname = self.to_safe(hostname).lower()
1108 |
1109 | # Add to index
1110 | self.index[hostname] = [region, instance.id]
1111 |
1112 | # Inventory: Group by instance ID (always a group of 1)
1113 | if self.group_by_instance_id:
1114 | self.inventory[instance.id] = [hostname]
1115 | if self.nested_groups:
1116 | self.push_group(self.inventory, 'instances', instance.id)
1117 |
1118 | # Inventory: Group by region
1119 | if self.group_by_region:
1120 | self.push(self.inventory, region, hostname)
1121 | if self.nested_groups:
1122 | self.push_group(self.inventory, 'regions', region)
1123 |
1124 | # Inventory: Group by availability zone
1125 | if self.group_by_availability_zone:
1126 | self.push(self.inventory, instance.availability_zone, hostname)
1127 | if self.nested_groups:
1128 | if self.group_by_region:
1129 | self.push_group(self.inventory, region, instance.availability_zone)
1130 | self.push_group(self.inventory, 'zones', instance.availability_zone)
1131 |
1132 | # Inventory: Group by instance type
1133 | if self.group_by_instance_type:
1134 | type_name = self.to_safe('type_' + instance.instance_class)
1135 | self.push(self.inventory, type_name, hostname)
1136 | if self.nested_groups:
1137 | self.push_group(self.inventory, 'types', type_name)
1138 |
1139 | # Inventory: Group by VPC
1140 | if self.group_by_vpc_id and instance.subnet_group and instance.subnet_group.vpc_id:
1141 | vpc_id_name = self.to_safe('vpc_id_' + instance.subnet_group.vpc_id)
1142 | self.push(self.inventory, vpc_id_name, hostname)
1143 | if self.nested_groups:
1144 | self.push_group(self.inventory, 'vpcs', vpc_id_name)
1145 |
1146 | # Inventory: Group by security group
1147 | if self.group_by_security_group:
1148 | try:
1149 | if instance.security_group:
1150 | key = self.to_safe("security_group_" + instance.security_group.name)
1151 | self.push(self.inventory, key, hostname)
1152 | if self.nested_groups:
1153 | self.push_group(self.inventory, 'security_groups', key)
1154 |
1155 | except AttributeError:
1156 | self.fail_with_error('\n'.join(['Package boto seems a bit older.',
1157 | 'Please upgrade boto >= 2.3.0.']))
1158 | # Inventory: Group by tag keys
1159 | if self.group_by_tag_keys:
1160 | for k, v in instance.tags.items():
1161 | if self.expand_csv_tags and v and ',' in v:
1162 | values = map(lambda x: x.strip(), v.split(','))
1163 | else:
1164 | values = [v]
1165 |
1166 | for v in values:
1167 | if v:
1168 | key = self.to_safe("tag_" + k + "=" + v)
1169 | else:
1170 | key = self.to_safe("tag_" + k)
1171 | self.push(self.inventory, key, hostname)
1172 | if self.nested_groups:
1173 | self.push_group(self.inventory, 'tags', self.to_safe("tag_" + k))
1174 | if v:
1175 | self.push_group(self.inventory, self.to_safe("tag_" + k), key)
1176 |
1177 | # Inventory: Group by engine
1178 | if self.group_by_rds_engine:
1179 | self.push(self.inventory, self.to_safe("rds_" + instance.engine), hostname)
1180 | if self.nested_groups:
1181 | self.push_group(self.inventory, 'rds_engines', self.to_safe("rds_" + instance.engine))
1182 |
1183 | # Inventory: Group by parameter group
1184 | if self.group_by_rds_parameter_group:
1185 | self.push(self.inventory, self.to_safe("rds_parameter_group_" + instance.parameter_group.name), hostname)
1186 | if self.nested_groups:
1187 | self.push_group(self.inventory, 'rds_parameter_groups', self.to_safe("rds_parameter_group_" + instance.parameter_group.name))
1188 |
1189 | # Global Tag: instances without tags
1190 | if self.group_by_tag_none and len(instance.tags) == 0:
1191 | self.push(self.inventory, 'tag_none', hostname)
1192 | if self.nested_groups:
1193 | self.push_group(self.inventory, 'tags', 'tag_none')
1194 |
1195 | # Global Tag: all RDS instances
1196 | self.push(self.inventory, 'rds', hostname)
1197 |
1198 | self.inventory["_meta"]["hostvars"][hostname] = self.get_host_info_dict_from_instance(instance)
1199 | self.inventory["_meta"]["hostvars"][hostname]['ansible_host'] = dest
1200 |
1201 | def add_elasticache_cluster(self, cluster, region):
1202 | ''' Adds an ElastiCache cluster to the inventory and index, as long as
1203 | it's nodes are addressable '''
1204 |
1205 | # Only want available clusters unless all_elasticache_clusters is True
1206 | if not self.all_elasticache_clusters and cluster['CacheClusterStatus'] != 'available':
1207 | return
1208 |
1209 | # Select the best destination address
1210 | if 'ConfigurationEndpoint' in cluster and cluster['ConfigurationEndpoint']:
1211 | # Memcached cluster
1212 | dest = cluster['ConfigurationEndpoint']['Address']
1213 | is_redis = False
1214 | else:
1215 | # Redis sigle node cluster
1216 | # Because all Redis clusters are single nodes, we'll merge the
1217 | # info from the cluster with info about the node
1218 | dest = cluster['CacheNodes'][0]['Endpoint']['Address']
1219 | is_redis = True
1220 |
1221 | if not dest:
1222 | # Skip clusters we cannot address (e.g. private VPC subnet)
1223 | return
1224 |
1225 | # Add to index
1226 | self.index[dest] = [region, cluster['CacheClusterId']]
1227 |
1228 | # Inventory: Group by instance ID (always a group of 1)
1229 | if self.group_by_instance_id:
1230 | self.inventory[cluster['CacheClusterId']] = [dest]
1231 | if self.nested_groups:
1232 | self.push_group(self.inventory, 'instances', cluster['CacheClusterId'])
1233 |
1234 | # Inventory: Group by region
1235 | if self.group_by_region and not is_redis:
1236 | self.push(self.inventory, region, dest)
1237 | if self.nested_groups:
1238 | self.push_group(self.inventory, 'regions', region)
1239 |
1240 | # Inventory: Group by availability zone
1241 | if self.group_by_availability_zone and not is_redis:
1242 | self.push(self.inventory, cluster['PreferredAvailabilityZone'], dest)
1243 | if self.nested_groups:
1244 | if self.group_by_region:
1245 | self.push_group(self.inventory, region, cluster['PreferredAvailabilityZone'])
1246 | self.push_group(self.inventory, 'zones', cluster['PreferredAvailabilityZone'])
1247 |
1248 | # Inventory: Group by node type
1249 | if self.group_by_instance_type and not is_redis:
1250 | type_name = self.to_safe('type_' + cluster['CacheNodeType'])
1251 | self.push(self.inventory, type_name, dest)
1252 | if self.nested_groups:
1253 | self.push_group(self.inventory, 'types', type_name)
1254 |
1255 | # Inventory: Group by VPC (information not available in the current
1256 | # AWS API version for ElastiCache)
1257 |
1258 | # Inventory: Group by security group
1259 | if self.group_by_security_group and not is_redis:
1260 |
1261 | # Check for the existence of the 'SecurityGroups' key and also if
1262 | # this key has some value. When the cluster is not placed in a SG
1263 | # the query can return None here and cause an error.
1264 | if 'SecurityGroups' in cluster and cluster['SecurityGroups'] is not None:
1265 | for security_group in cluster['SecurityGroups']:
1266 | key = self.to_safe("security_group_" + security_group['SecurityGroupId'])
1267 | self.push(self.inventory, key, dest)
1268 | if self.nested_groups:
1269 | self.push_group(self.inventory, 'security_groups', key)
1270 |
1271 | # Inventory: Group by engine
1272 | if self.group_by_elasticache_engine and not is_redis:
1273 | self.push(self.inventory, self.to_safe("elasticache_" + cluster['Engine']), dest)
1274 | if self.nested_groups:
1275 | self.push_group(self.inventory, 'elasticache_engines', self.to_safe(cluster['Engine']))
1276 |
1277 | # Inventory: Group by parameter group
1278 | if self.group_by_elasticache_parameter_group:
1279 | self.push(self.inventory, self.to_safe("elasticache_parameter_group_" + cluster['CacheParameterGroup']['CacheParameterGroupName']), dest)
1280 | if self.nested_groups:
1281 | self.push_group(self.inventory, 'elasticache_parameter_groups', self.to_safe(cluster['CacheParameterGroup']['CacheParameterGroupName']))
1282 |
1283 | # Inventory: Group by replication group
1284 | if self.group_by_elasticache_replication_group and 'ReplicationGroupId' in cluster and cluster['ReplicationGroupId']:
1285 | self.push(self.inventory, self.to_safe("elasticache_replication_group_" + cluster['ReplicationGroupId']), dest)
1286 | if self.nested_groups:
1287 | self.push_group(self.inventory, 'elasticache_replication_groups', self.to_safe(cluster['ReplicationGroupId']))
1288 |
1289 | # Global Tag: all ElastiCache clusters
1290 | self.push(self.inventory, 'elasticache_clusters', cluster['CacheClusterId'])
1291 |
1292 | host_info = self.get_host_info_dict_from_describe_dict(cluster)
1293 |
1294 | self.inventory["_meta"]["hostvars"][dest] = host_info
1295 |
1296 | # Add the nodes
1297 | for node in cluster['CacheNodes']:
1298 | self.add_elasticache_node(node, cluster, region)
1299 |
1300 | def add_elasticache_node(self, node, cluster, region):
1301 | ''' Adds an ElastiCache node to the inventory and index, as long as
1302 | it is addressable '''
1303 |
1304 | # Only want available nodes unless all_elasticache_nodes is True
1305 | if not self.all_elasticache_nodes and node['CacheNodeStatus'] != 'available':
1306 | return
1307 |
1308 | # Select the best destination address
1309 | dest = node['Endpoint']['Address']
1310 |
1311 | if not dest:
1312 | # Skip nodes we cannot address (e.g. private VPC subnet)
1313 | return
1314 |
1315 | node_id = self.to_safe(cluster['CacheClusterId'] + '_' + node['CacheNodeId'])
1316 |
1317 | # Add to index
1318 | self.index[dest] = [region, node_id]
1319 |
1320 | # Inventory: Group by node ID (always a group of 1)
1321 | if self.group_by_instance_id:
1322 | self.inventory[node_id] = [dest]
1323 | if self.nested_groups:
1324 | self.push_group(self.inventory, 'instances', node_id)
1325 |
1326 | # Inventory: Group by region
1327 | if self.group_by_region:
1328 | self.push(self.inventory, region, dest)
1329 | if self.nested_groups:
1330 | self.push_group(self.inventory, 'regions', region)
1331 |
1332 | # Inventory: Group by availability zone
1333 | if self.group_by_availability_zone:
1334 | self.push(self.inventory, cluster['PreferredAvailabilityZone'], dest)
1335 | if self.nested_groups:
1336 | if self.group_by_region:
1337 | self.push_group(self.inventory, region, cluster['PreferredAvailabilityZone'])
1338 | self.push_group(self.inventory, 'zones', cluster['PreferredAvailabilityZone'])
1339 |
1340 | # Inventory: Group by node type
1341 | if self.group_by_instance_type:
1342 | type_name = self.to_safe('type_' + cluster['CacheNodeType'])
1343 | self.push(self.inventory, type_name, dest)
1344 | if self.nested_groups:
1345 | self.push_group(self.inventory, 'types', type_name)
1346 |
1347 | # Inventory: Group by VPC (information not available in the current
1348 | # AWS API version for ElastiCache)
1349 |
1350 | # Inventory: Group by security group
1351 | if self.group_by_security_group:
1352 |
1353 | # Check for the existence of the 'SecurityGroups' key and also if
1354 | # this key has some value. When the cluster is not placed in a SG
1355 | # the query can return None here and cause an error.
1356 | if 'SecurityGroups' in cluster and cluster['SecurityGroups'] is not None:
1357 | for security_group in cluster['SecurityGroups']:
1358 | key = self.to_safe("security_group_" + security_group['SecurityGroupId'])
1359 | self.push(self.inventory, key, dest)
1360 | if self.nested_groups:
1361 | self.push_group(self.inventory, 'security_groups', key)
1362 |
1363 | # Inventory: Group by engine
1364 | if self.group_by_elasticache_engine:
1365 | self.push(self.inventory, self.to_safe("elasticache_" + cluster['Engine']), dest)
1366 | if self.nested_groups:
1367 | self.push_group(self.inventory, 'elasticache_engines', self.to_safe("elasticache_" + cluster['Engine']))
1368 |
1369 | # Inventory: Group by parameter group (done at cluster level)
1370 |
1371 | # Inventory: Group by replication group (done at cluster level)
1372 |
1373 | # Inventory: Group by ElastiCache Cluster
1374 | if self.group_by_elasticache_cluster:
1375 | self.push(self.inventory, self.to_safe("elasticache_cluster_" + cluster['CacheClusterId']), dest)
1376 |
1377 | # Global Tag: all ElastiCache nodes
1378 | self.push(self.inventory, 'elasticache_nodes', dest)
1379 |
1380 | host_info = self.get_host_info_dict_from_describe_dict(node)
1381 |
1382 | if dest in self.inventory["_meta"]["hostvars"]:
1383 | self.inventory["_meta"]["hostvars"][dest].update(host_info)
1384 | else:
1385 | self.inventory["_meta"]["hostvars"][dest] = host_info
1386 |
1387 | def add_elasticache_replication_group(self, replication_group, region):
1388 | ''' Adds an ElastiCache replication group to the inventory and index '''
1389 |
1390 | # Only want available clusters unless all_elasticache_replication_groups is True
1391 | if not self.all_elasticache_replication_groups and replication_group['Status'] != 'available':
1392 | return
1393 |
1394 | # Skip clusters we cannot address (e.g. private VPC subnet or clustered redis)
1395 | if replication_group['NodeGroups'][0]['PrimaryEndpoint'] is None or \
1396 | replication_group['NodeGroups'][0]['PrimaryEndpoint']['Address'] is None:
1397 | return
1398 |
1399 | # Select the best destination address (PrimaryEndpoint)
1400 | dest = replication_group['NodeGroups'][0]['PrimaryEndpoint']['Address']
1401 |
1402 | # Add to index
1403 | self.index[dest] = [region, replication_group['ReplicationGroupId']]
1404 |
1405 | # Inventory: Group by ID (always a group of 1)
1406 | if self.group_by_instance_id:
1407 | self.inventory[replication_group['ReplicationGroupId']] = [dest]
1408 | if self.nested_groups:
1409 | self.push_group(self.inventory, 'instances', replication_group['ReplicationGroupId'])
1410 |
1411 | # Inventory: Group by region
1412 | if self.group_by_region:
1413 | self.push(self.inventory, region, dest)
1414 | if self.nested_groups:
1415 | self.push_group(self.inventory, 'regions', region)
1416 |
1417 | # Inventory: Group by availability zone (doesn't apply to replication groups)
1418 |
1419 | # Inventory: Group by node type (doesn't apply to replication groups)
1420 |
1421 | # Inventory: Group by VPC (information not available in the current
1422 | # AWS API version for replication groups
1423 |
1424 | # Inventory: Group by security group (doesn't apply to replication groups)
1425 | # Check this value in cluster level
1426 |
1427 | # Inventory: Group by engine (replication groups are always Redis)
1428 | if self.group_by_elasticache_engine:
1429 | self.push(self.inventory, 'elasticache_redis', dest)
1430 | if self.nested_groups:
1431 | self.push_group(self.inventory, 'elasticache_engines', 'redis')
1432 |
1433 | # Global Tag: all ElastiCache clusters
1434 | self.push(self.inventory, 'elasticache_replication_groups', replication_group['ReplicationGroupId'])
1435 |
1436 | host_info = self.get_host_info_dict_from_describe_dict(replication_group)
1437 |
1438 | self.inventory["_meta"]["hostvars"][dest] = host_info
1439 |
1440 | def get_route53_records(self):
1441 | ''' Get and store the map of resource records to domain names that
1442 | point to them. '''
1443 |
1444 | if self.boto_profile:
1445 | r53_conn = route53.Route53Connection(profile_name=self.boto_profile)
1446 | else:
1447 | r53_conn = route53.Route53Connection()
1448 | all_zones = r53_conn.get_zones()
1449 |
1450 | route53_zones = [zone for zone in all_zones if zone.name[:-1] not in self.route53_excluded_zones]
1451 |
1452 | self.route53_records = {}
1453 |
1454 | for zone in route53_zones:
1455 | rrsets = r53_conn.get_all_rrsets(zone.id)
1456 |
1457 | for record_set in rrsets:
1458 | record_name = record_set.name
1459 |
1460 | if record_name.endswith('.'):
1461 | record_name = record_name[:-1]
1462 |
1463 | for resource in record_set.resource_records:
1464 | self.route53_records.setdefault(resource, set())
1465 | self.route53_records[resource].add(record_name)
1466 |
1467 | def get_instance_route53_names(self, instance):
1468 | ''' Check if an instance is referenced in the records we have from
1469 | Route53. If it is, return the list of domain names pointing to said
1470 | instance. If nothing points to it, return an empty list. '''
1471 |
1472 | instance_attributes = ['public_dns_name', 'private_dns_name',
1473 | 'ip_address', 'private_ip_address']
1474 |
1475 | name_list = set()
1476 |
1477 | for attrib in instance_attributes:
1478 | try:
1479 | value = getattr(instance, attrib)
1480 | except AttributeError:
1481 | continue
1482 |
1483 | if value in self.route53_records:
1484 | name_list.update(self.route53_records[value])
1485 |
1486 | return list(name_list)
1487 |
1488 | def get_host_info_dict_from_instance(self, instance):
1489 | instance_vars = {}
1490 | for key in vars(instance):
1491 | value = getattr(instance, key)
1492 | key = self.to_safe('ec2_' + key)
1493 |
1494 | # Handle complex types
1495 | # state/previous_state changed to properties in boto in https://github.com/boto/boto/commit/a23c379837f698212252720d2af8dec0325c9518
1496 | if key == 'ec2__state':
1497 | instance_vars['ec2_state'] = instance.state or ''
1498 | instance_vars['ec2_state_code'] = instance.state_code
1499 | elif key == 'ec2__previous_state':
1500 | instance_vars['ec2_previous_state'] = instance.previous_state or ''
1501 | instance_vars['ec2_previous_state_code'] = instance.previous_state_code
1502 | elif isinstance(value, (int, bool)):
1503 | instance_vars[key] = value
1504 | elif isinstance(value, six.string_types):
1505 | instance_vars[key] = value.strip()
1506 | elif value is None:
1507 | instance_vars[key] = ''
1508 | elif key == 'ec2_region':
1509 | instance_vars[key] = value.name
1510 | elif key == 'ec2__placement':
1511 | instance_vars['ec2_placement'] = value.zone
1512 | elif key == 'ec2_tags':
1513 | for k, v in value.items():
1514 | if self.expand_csv_tags and ',' in v:
1515 | v = list(map(lambda x: x.strip(), v.split(',')))
1516 | key = self.to_safe('ec2_tag_' + k)
1517 | instance_vars[key] = v
1518 | elif key == 'ec2_groups':
1519 | group_ids = []
1520 | group_names = []
1521 | for group in value:
1522 | group_ids.append(group.id)
1523 | group_names.append(group.name)
1524 | instance_vars["ec2_security_group_ids"] = ','.join([str(i) for i in group_ids])
1525 | instance_vars["ec2_security_group_names"] = ','.join([str(i) for i in group_names])
1526 | elif key == 'ec2_block_device_mapping':
1527 | instance_vars["ec2_block_devices"] = {}
1528 | for k, v in value.items():
1529 | instance_vars["ec2_block_devices"][os.path.basename(k)] = v.volume_id
1530 | else:
1531 | pass
1532 | # TODO Product codes if someone finds them useful
1533 | # print key
1534 | # print type(value)
1535 | # print value
1536 |
1537 | instance_vars[self.to_safe('ec2_account_id')] = self.aws_account_id
1538 |
1539 | return instance_vars
1540 |
1541 | def get_host_info_dict_from_describe_dict(self, describe_dict):
1542 | ''' Parses the dictionary returned by the API call into a flat list
1543 | of parameters. This method should be used only when 'describe' is
1544 | used directly because Boto doesn't provide specific classes. '''
1545 |
1546 | # I really don't agree with prefixing everything with 'ec2'
1547 | # because EC2, RDS and ElastiCache are different services.
1548 | # I'm just following the pattern used until now to not break any
1549 | # compatibility.
1550 |
1551 | host_info = {}
1552 | for key in describe_dict:
1553 | value = describe_dict[key]
1554 | key = self.to_safe('ec2_' + self.uncammelize(key))
1555 |
1556 | # Handle complex types
1557 |
1558 | # Target: Memcached Cache Clusters
1559 | if key == 'ec2_configuration_endpoint' and value:
1560 | host_info['ec2_configuration_endpoint_address'] = value['Address']
1561 | host_info['ec2_configuration_endpoint_port'] = value['Port']
1562 |
1563 | # Target: Cache Nodes and Redis Cache Clusters (single node)
1564 | if key == 'ec2_endpoint' and value:
1565 | host_info['ec2_endpoint_address'] = value['Address']
1566 | host_info['ec2_endpoint_port'] = value['Port']
1567 |
1568 | # Target: Redis Replication Groups
1569 | if key == 'ec2_node_groups' and value:
1570 | host_info['ec2_endpoint_address'] = value[0]['PrimaryEndpoint']['Address']
1571 | host_info['ec2_endpoint_port'] = value[0]['PrimaryEndpoint']['Port']
1572 | replica_count = 0
1573 | for node in value[0]['NodeGroupMembers']:
1574 | if node['CurrentRole'] == 'primary':
1575 | host_info['ec2_primary_cluster_address'] = node['ReadEndpoint']['Address']
1576 | host_info['ec2_primary_cluster_port'] = node['ReadEndpoint']['Port']
1577 | host_info['ec2_primary_cluster_id'] = node['CacheClusterId']
1578 | elif node['CurrentRole'] == 'replica':
1579 | host_info['ec2_replica_cluster_address_' + str(replica_count)] = node['ReadEndpoint']['Address']
1580 | host_info['ec2_replica_cluster_port_' + str(replica_count)] = node['ReadEndpoint']['Port']
1581 | host_info['ec2_replica_cluster_id_' + str(replica_count)] = node['CacheClusterId']
1582 | replica_count += 1
1583 |
1584 | # Target: Redis Replication Groups
1585 | if key == 'ec2_member_clusters' and value:
1586 | host_info['ec2_member_clusters'] = ','.join([str(i) for i in value])
1587 |
1588 | # Target: All Cache Clusters
1589 | elif key == 'ec2_cache_parameter_group':
1590 | host_info["ec2_cache_node_ids_to_reboot"] = ','.join([str(i) for i in value['CacheNodeIdsToReboot']])
1591 | host_info['ec2_cache_parameter_group_name'] = value['CacheParameterGroupName']
1592 | host_info['ec2_cache_parameter_apply_status'] = value['ParameterApplyStatus']
1593 |
1594 | # Target: Almost everything
1595 | elif key == 'ec2_security_groups':
1596 |
1597 | # Skip if SecurityGroups is None
1598 | # (it is possible to have the key defined but no value in it).
1599 | if value is not None:
1600 | sg_ids = []
1601 | for sg in value:
1602 | sg_ids.append(sg['SecurityGroupId'])
1603 | host_info["ec2_security_group_ids"] = ','.join([str(i) for i in sg_ids])
1604 |
1605 | # Target: Everything
1606 | # Preserve booleans and integers
1607 | elif isinstance(value, (int, bool)):
1608 | host_info[key] = value
1609 |
1610 | # Target: Everything
1611 | # Sanitize string values
1612 | elif isinstance(value, six.string_types):
1613 | host_info[key] = value.strip()
1614 |
1615 | # Target: Everything
1616 | # Replace None by an empty string
1617 | elif value is None:
1618 | host_info[key] = ''
1619 |
1620 | else:
1621 | # Remove non-processed complex types
1622 | pass
1623 |
1624 | return host_info
1625 |
1626 | def get_host_info(self):
1627 | ''' Get variables about a specific host '''
1628 |
1629 | if len(self.index) == 0:
1630 | # Need to load index from cache
1631 | self.load_index_from_cache()
1632 |
1633 | if self.args.host not in self.index:
1634 | # try updating the cache
1635 | self.do_api_calls_update_cache()
1636 | if self.args.host not in self.index:
1637 | # host might not exist anymore
1638 | return self.json_format_dict({}, True)
1639 |
1640 | (region, instance_id) = self.index[self.args.host]
1641 |
1642 | instance = self.get_instance(region, instance_id)
1643 | return self.json_format_dict(self.get_host_info_dict_from_instance(instance), True)
1644 |
1645 | def push(self, my_dict, key, element):
1646 | ''' Push an element onto an array that may not have been defined in
1647 | the dict '''
1648 | group_info = my_dict.setdefault(key, [])
1649 | if isinstance(group_info, dict):
1650 | host_list = group_info.setdefault('hosts', [])
1651 | host_list.append(element)
1652 | else:
1653 | group_info.append(element)
1654 |
1655 | def push_group(self, my_dict, key, element):
1656 | ''' Push a group as a child of another group. '''
1657 | parent_group = my_dict.setdefault(key, {})
1658 | if not isinstance(parent_group, dict):
1659 | parent_group = my_dict[key] = {'hosts': parent_group}
1660 | child_groups = parent_group.setdefault('children', [])
1661 | if element not in child_groups:
1662 | child_groups.append(element)
1663 |
1664 | def get_inventory_from_cache(self):
1665 | ''' Reads the inventory from the cache file and returns it as a JSON
1666 | object '''
1667 |
1668 | with open(self.cache_path_cache, 'r') as f:
1669 | json_inventory = f.read()
1670 | return json_inventory
1671 |
1672 | def load_index_from_cache(self):
1673 | ''' Reads the index from the cache file sets self.index '''
1674 |
1675 | with open(self.cache_path_index, 'rb') as f:
1676 | self.index = json.load(f)
1677 |
1678 | def write_to_cache(self, data, filename):
1679 | ''' Writes data in JSON format to a file '''
1680 |
1681 | json_data = self.json_format_dict(data, True)
1682 | with open(filename, 'w') as f:
1683 | f.write(json_data)
1684 |
1685 | def uncammelize(self, key):
1686 | temp = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', key)
1687 | return re.sub('([a-z0-9])([A-Z])', r'\1_\2', temp).lower()
1688 |
1689 | def to_safe(self, word):
1690 | ''' Converts 'bad' characters in a string to underscores so they can be used as Ansible groups '''
1691 | regex = r"[^A-Za-z0-9\_"
1692 | if not self.replace_dash_in_groups:
1693 | regex += r"\-"
1694 | return re.sub(regex + "]", "_", word)
1695 |
1696 | def json_format_dict(self, data, pretty=False):
1697 | ''' Converts a dict to a JSON object and dumps it as a formatted
1698 | string '''
1699 |
1700 | if pretty:
1701 | return json.dumps(data, sort_keys=True, indent=2)
1702 | else:
1703 | return json.dumps(data)
1704 |
1705 |
1706 | if __name__ == '__main__':
1707 | # Run the script
1708 | Ec2Inventory()
1709 |
--------------------------------------------------------------------------------
/playbooks/ec2/inventory/ec2.ini:
--------------------------------------------------------------------------------
1 | # Ansible EC2 external inventory script settings
2 | #
3 |
4 | [ec2]
5 |
6 | # to talk to a private eucalyptus instance uncomment these lines
7 | # and edit edit eucalyptus_host to be the host name of your cloud controller
8 | #eucalyptus = True
9 | #eucalyptus_host = clc.cloud.domain.org
10 |
11 | # AWS regions to make calls to. Set this to 'all' to make request to all regions
12 | # in AWS and merge the results together. Alternatively, set this to a comma
13 | # separated list of regions. E.g. 'us-east-1,us-west-1,us-west-2' and do not
14 | # provide the 'regions_exclude' option. If this is set to 'auto', AWS_REGION or
15 | # AWS_DEFAULT_REGION environment variable will be read to determine the region.
16 | regions = all
17 | regions_exclude = us-gov-west-1, cn-north-1
18 |
19 | # When generating inventory, Ansible needs to know how to address a server.
20 | # Each EC2 instance has a lot of variables associated with it. Here is the list:
21 | # http://docs.pythonboto.org/en/latest/ref/ec2.html#module-boto.ec2.instance
22 | # Below are 2 variables that are used as the address of a server:
23 | # - destination_variable
24 | # - vpc_destination_variable
25 |
26 | # This is the normal destination variable to use. If you are running Ansible
27 | # from outside EC2, then 'public_dns_name' makes the most sense. If you are
28 | # running Ansible from within EC2, then perhaps you want to use the internal
29 | # address, and should set this to 'private_dns_name'. The key of an EC2 tag
30 | # may optionally be used; however the boto instance variables hold precedence
31 | # in the event of a collision.
32 | destination_variable = public_dns_name
33 |
34 | # This allows you to override the inventory_name with an ec2 variable, instead
35 | # of using the destination_variable above. Addressing (aka ansible_ssh_host)
36 | # will still use destination_variable. Tags should be written as 'tag_TAGNAME'.
37 | #hostname_variable = tag_Name
38 |
39 | # For server inside a VPC, using DNS names may not make sense. When an instance
40 | # has 'subnet_id' set, this variable is used. If the subnet is public, setting
41 | # this to 'ip_address' will return the public IP address. For instances in a
42 | # private subnet, this should be set to 'private_ip_address', and Ansible must
43 | # be run from within EC2. The key of an EC2 tag may optionally be used; however
44 | # the boto instance variables hold precedence in the event of a collision.
45 | # WARNING: - instances that are in the private vpc, _without_ public ip address
46 | # will not be listed in the inventory until You set:
47 | # vpc_destination_variable = private_ip_address
48 | vpc_destination_variable = ip_address
49 |
50 | # The following two settings allow flexible ansible host naming based on a
51 | # python format string and a comma-separated list of ec2 tags. Note that:
52 | #
53 | # 1) If the tags referenced are not present for some instances, empty strings
54 | # will be substituted in the format string.
55 | # 2) This overrides both destination_variable and vpc_destination_variable.
56 | #
57 | #destination_format = {0}.{1}.example.com
58 | #destination_format_tags = Name,environment
59 |
60 | # To tag instances on EC2 with the resource records that point to them from
61 | # Route53, set 'route53' to True.
62 | route53 = False
63 |
64 | # To use Route53 records as the inventory hostnames, uncomment and set
65 | # to equal the domain name you wish to use. You must also have 'route53' (above)
66 | # set to True.
67 | # route53_hostnames = .example.com
68 |
69 | # To exclude RDS instances from the inventory, uncomment and set to False.
70 | #rds = False
71 |
72 | # To exclude ElastiCache instances from the inventory, uncomment and set to False.
73 | #elasticache = False
74 |
75 | # Additionally, you can specify the list of zones to exclude looking up in
76 | # 'route53_excluded_zones' as a comma-separated list.
77 | # route53_excluded_zones = samplezone1.com, samplezone2.com
78 |
79 | # By default, only EC2 instances in the 'running' state are returned. Set
80 | # 'all_instances' to True to return all instances regardless of state.
81 | all_instances = False
82 |
83 | # By default, only EC2 instances in the 'running' state are returned. Specify
84 | # EC2 instance states to return as a comma-separated list. This
85 | # option is overridden when 'all_instances' is True.
86 | # instance_states = pending, running, shutting-down, terminated, stopping, stopped
87 |
88 | # By default, only RDS instances in the 'available' state are returned. Set
89 | # 'all_rds_instances' to True return all RDS instances regardless of state.
90 | all_rds_instances = False
91 |
92 | # Include RDS cluster information (Aurora etc.)
93 | include_rds_clusters = False
94 |
95 | # By default, only ElastiCache clusters and nodes in the 'available' state
96 | # are returned. Set 'all_elasticache_clusters' and/or 'all_elastic_nodes'
97 | # to True return all ElastiCache clusters and nodes, regardless of state.
98 | #
99 | # Note that all_elasticache_nodes only applies to listed clusters. That means
100 | # if you set all_elastic_clusters to false, no node will be return from
101 | # unavailable clusters, regardless of the state and to what you set for
102 | # all_elasticache_nodes.
103 | all_elasticache_replication_groups = False
104 | all_elasticache_clusters = False
105 | all_elasticache_nodes = False
106 |
107 | # API calls to EC2 are slow. For this reason, we cache the results of an API
108 | # call. Set this to the path you want cache files to be written to. Two files
109 | # will be written to this directory:
110 | # - ansible-ec2.cache
111 | # - ansible-ec2.index
112 | cache_path = ~/.ansible/tmp
113 |
114 | # The number of seconds a cache file is considered valid. After this many
115 | # seconds, a new API call will be made, and the cache file will be updated.
116 | # To disable the cache, set this value to 0
117 | cache_max_age = 300
118 |
119 | # Organize groups into a nested/hierarchy instead of a flat namespace.
120 | nested_groups = False
121 |
122 | # Replace - tags when creating groups to avoid issues with ansible
123 | replace_dash_in_groups = True
124 |
125 | # If set to true, any tag of the form "a,b,c" is expanded into a list
126 | # and the results are used to create additional tag_* inventory groups.
127 | expand_csv_tags = False
128 |
129 | # The EC2 inventory output can become very large. To manage its size,
130 | # configure which groups should be created.
131 | group_by_instance_id = True
132 | group_by_region = True
133 | group_by_availability_zone = True
134 | group_by_aws_account = False
135 | group_by_ami_id = True
136 | group_by_instance_type = True
137 | group_by_instance_state = False
138 | group_by_platform = True
139 | group_by_key_pair = True
140 | group_by_vpc_id = True
141 | group_by_security_group = True
142 | group_by_tag_keys = True
143 | group_by_tag_none = True
144 | group_by_route53_names = True
145 | group_by_rds_engine = True
146 | group_by_rds_parameter_group = True
147 | group_by_elasticache_engine = True
148 | group_by_elasticache_cluster = True
149 | group_by_elasticache_parameter_group = True
150 | group_by_elasticache_replication_group = True
151 |
152 | # If you only want to include hosts that match a certain regular expression
153 | # pattern_include = staging-*
154 |
155 | # If you want to exclude any hosts that match a certain regular expression
156 | # pattern_exclude = staging-*
157 |
158 | # Instance filters can be used to control which instances are retrieved for
159 | # inventory. For the full list of possible filters, please read the EC2 API
160 | # docs: http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeInstances.html#query-DescribeInstances-filters
161 | # Filters are key/value pairs separated by '=', to list multiple filters use
162 | # a list separated by commas. To "AND" criteria together, use "&". Note that
163 | # the "AND" is not useful along with stack_filters and so such usage is not allowed.
164 | # See examples below.
165 |
166 | # If you want to apply multiple filters simultaneously, set stack_filters to
167 | # True. Default behaviour is to combine the results of all filters. Stacking
168 | # allows the use of multiple conditions to filter down, for example by
169 | # environment and type of host.
170 | stack_filters = False
171 |
172 | # Retrieve only instances with (key=value) env=staging tag
173 | # instance_filters = tag:env=staging
174 |
175 | # Retrieve only instances with role=webservers OR role=dbservers tag
176 | # instance_filters = tag:role=webservers,tag:role=dbservers
177 |
178 | # Retrieve only t1.micro instances OR instances with tag env=staging
179 | # instance_filters = instance-type=t1.micro,tag:env=staging
180 |
181 | # You can use wildcards in filter values also. Below will list instances which
182 | # tag Name value matches webservers1*
183 | # (ex. webservers15, webservers1a, webservers123 etc)
184 | # instance_filters = tag:Name=webservers1*
185 |
186 | # Retrieve only instances of type t1.micro that also have tag env=stage
187 | # instance_filters = instance-type=t1.micro&tag:env=stage
188 |
189 | # Retrieve instances of type t1.micro AND tag env=stage, as well as any instance
190 | # that are of type m3.large, regardless of env tag
191 | # instance_filters = instance-type=t1.micro&tag:env=stage,instance-type=m3.large
192 |
193 | # An IAM role can be assumed, so all requests are run as that role.
194 | # This can be useful for connecting across different accounts, or to limit user
195 | # access
196 | # iam_role = role-arn
197 |
198 | # A boto configuration profile may be used to separate out credentials
199 | # see https://boto.readthedocs.io/en/latest/boto_config_tut.html
200 | # boto_profile = some-boto-profile-name
201 |
202 |
203 | [credentials]
204 |
205 | # The AWS credentials can optionally be specified here. Credentials specified
206 | # here are ignored if the environment variable AWS_ACCESS_KEY_ID or
207 | # AWS_PROFILE is set, or if the boto_profile property above is set.
208 | #
209 | # Supplying AWS credentials here is not recommended, as it introduces
210 | # non-trivial security concerns. When going down this route, please make sure
211 | # to set access permissions for this file correctly, e.g. handle it the same
212 | # way as you would a private SSH key.
213 | #
214 | # Unlike the boto and AWS configure files, this section does not support
215 | # profiles.
216 | #
217 | # aws_access_key_id = AXXXXXXXXXXXXXX
218 | # aws_secret_access_key = XXXXXXXXXXXXXXXXXXX
219 | # aws_security_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXX
220 |
--------------------------------------------------------------------------------
/playbooks/ec2/inventory/inventory:
--------------------------------------------------------------------------------
1 | ansible1.example.com
2 |
3 | [webservers]
4 | ansible2.example.com
5 |
6 | [proxyservers]
7 | proxy.example.com
8 |
9 | [dockerhost]
10 | docker.example.com
11 |
--------------------------------------------------------------------------------
/playbooks/ec2/list-instances.yaml:
--------------------------------------------------------------------------------
1 | - name: list instances
2 | hosts: localhost
3 | tasks:
4 | - name: find instances
5 | ec2_instance_facts:
6 | filters:
7 | availability-zone: usa-west-2
8 |
--------------------------------------------------------------------------------
/playbooks/ec2/ubuntu-ami.yaml:
--------------------------------------------------------------------------------
1 | - name: create an ubuntu instance on EC2
2 | hosts: localhost
3 | tasks:
4 | - name: get the ubuntu bionic latest AMI
5 | ec2_ami_find:
6 | name: "ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*"
7 | aws_access_key:
8 | aws_secret_key:
9 | region: us-west-2
10 | sort: name
11 | sort_order: descending
12 | sort_end: 1
13 | register: ubuntu_image
14 |
15 | - name: start the instance
16 | ec2:
17 | image: "{{ ubuntu_image.results[0].ami_id }}"
18 | region: us-west-2
19 | aws_access_key:
20 | aws_secret_key:
21 | instance_type: t2.nano
22 | key_name: june2018
23 | group: all-open
24 |
--------------------------------------------------------------------------------
/playbooks/ec2/ubuntu.yaml:
--------------------------------------------------------------------------------
1 | - name: create an ubuntu instance on EC2
2 | hosts: localhost
3 | tasks:
4 | - name: start the instance
5 | ec2:
6 | image: ami-09eb876a926ae86db
7 | region: us-west-2
8 | instance_type: t2.nano
9 | key_name: june2018
10 | group: all-open
11 |
--------------------------------------------------------------------------------
/playbooks/error_handling/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | inventory = inventory
3 | remote_user = ansible
4 | host_key_checking = false
5 |
6 | [privilege_escalation]
7 | become = True
8 | become_method = sudo
9 | become_user = root
10 | become_ask_pass = False
11 |
12 |
--------------------------------------------------------------------------------
/playbooks/error_handling/assert.retry:
--------------------------------------------------------------------------------
1 | ansible2.example.com
2 |
--------------------------------------------------------------------------------
/playbooks/error_handling/assert.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: test for file existence
3 | hosts: ansible2.example.com
4 | tasks:
5 | - stat:
6 | path: /etc/osts
7 | register: hosts
8 |
9 | - assert:
10 | that:
11 | - hosts.stat.exists
12 |
--------------------------------------------------------------------------------
/playbooks/error_handling/block.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: block example
3 | hosts: all
4 | tasks:
5 | - name: install apache
6 | block:
7 | - package:
8 | name: "{{ item }}"
9 | state: installed
10 | with_items:
11 | - httpd
12 | - elinks
13 | - mod_ssl
14 | - service:
15 | name: httpd
16 | state: started
17 | enabled: True
18 | when: ansible_distribution == 'CentOS'
19 |
20 |
21 |
--------------------------------------------------------------------------------
/playbooks/error_handling/inventory:
--------------------------------------------------------------------------------
1 | [all]
2 | ansible1.example.com
3 | ansible2.example.com
4 |
5 | [lamp]
6 | ansible1.example.com
7 |
8 | [file]
9 | ansible2.example.com
10 |
--------------------------------------------------------------------------------
/playbooks/error_handling/rescue.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: error handling
3 | hosts: all
4 | tasks:
5 | - block:
6 | - name: upgrade the database
7 | shell:
8 | cmd: /usr/local/lib/upgrade-database
9 | rescue:
10 | - name: revert after failure
11 | shell:
12 | cmd: /usr/local/lib/revert-database
13 | always:
14 | - name: always restart the database
15 | service:
16 | name: mariadb
17 | state: restarted
18 |
--------------------------------------------------------------------------------
/playbooks/error_handling/uricheck.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: check URL
3 | hosts: ansible2.example.com
4 | tasks:
5 | - uri:
6 | url: http://sandervanvugt.com
7 | return_content: yes
8 | register: webpage
9 |
10 | - name: Fail if sander is not in the page content
11 | fail:
12 | msg: 'not the right content'
13 | when: "'sander' not in webpage.content"
14 |
--------------------------------------------------------------------------------
/playbooks/exam-lab/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | remote_user = ansible
3 | host_key_checking = false
4 | inventory = inventory
5 |
6 | [privilege_escalation]
7 | become = True
8 | become_method = sudo
9 | become_user = root
10 | become_ask_pass = False
11 |
12 |
--------------------------------------------------------------------------------
/playbooks/exam-lab/inventory:
--------------------------------------------------------------------------------
1 | [webservers]
2 | ansible1.example.com
3 |
4 | [webclients]
5 | ansible2.example.com
6 | ansible3.example.com
7 |
--------------------------------------------------------------------------------
/playbooks/exam-lab/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: setup web environment
3 | hosts: all
4 |
5 | roles:
6 | - web-role
7 | - mountweb
8 |
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/mountweb/README.md:
--------------------------------------------------------------------------------
1 | Role Name
2 | =========
3 |
4 | A brief description of the role goes here.
5 |
6 | Requirements
7 | ------------
8 |
9 | Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
10 |
11 | Role Variables
12 | --------------
13 |
14 | A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
15 |
16 | Dependencies
17 | ------------
18 |
19 | A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
20 |
21 | Example Playbook
22 | ----------------
23 |
24 | Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
25 |
26 | - hosts: servers
27 | roles:
28 | - { role: username.rolename, x: 42 }
29 |
30 | License
31 | -------
32 |
33 | BSD
34 |
35 | Author Information
36 | ------------------
37 |
38 | An optional section for the role authors to include contact information, or a website (HTML is not allowed).
39 |
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/mountweb/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # defaults file for mountweb
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/mountweb/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # handlers file for mountweb
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/mountweb/meta/main.yml:
--------------------------------------------------------------------------------
1 | galaxy_info:
2 | author: your name
3 | description: your description
4 | company: your company (optional)
5 |
6 | # If the issue tracker for your role is not on github, uncomment the
7 | # next line and provide a value
8 | # issue_tracker_url: http://example.com/issue/tracker
9 |
10 | # Some suggested licenses:
11 | # - BSD (default)
12 | # - MIT
13 | # - GPLv2
14 | # - GPLv3
15 | # - Apache
16 | # - CC-BY
17 | license: license (GPLv2, CC-BY, etc)
18 |
19 | min_ansible_version: 1.2
20 |
21 | # If this a Container Enabled role, provide the minimum Ansible Container version.
22 | # min_ansible_container_version:
23 |
24 | # Optionally specify the branch Galaxy will use when accessing the GitHub
25 | # repo for this role. During role install, if no tags are available,
26 | # Galaxy will use this branch. During import Galaxy will access files on
27 | # this branch. If Travis integration is configured, only notifications for this
28 | # branch will be accepted. Otherwise, in all cases, the repo's default branch
29 | # (usually master) will be used.
30 | #github_branch:
31 |
32 | #
33 | # platforms is a list of platforms, and each platform has a name and a list of versions.
34 | #
35 | # platforms:
36 | # - name: Fedora
37 | # versions:
38 | # - all
39 | # - 25
40 | # - name: SomePlatform
41 | # versions:
42 | # - all
43 | # - 1.0
44 | # - 7
45 | # - 99.99
46 |
47 | galaxy_tags: []
48 | # List tags for your role here, one per line. A tag is a keyword that describes
49 | # and categorizes the role. Users find roles by searching for tags. Be sure to
50 | # remove the '[]' above, if you add tags to this list.
51 | #
52 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters.
53 | # Maximum 20 tags per role.
54 |
55 | dependencies: []
56 | # List your role dependencies here, one per line. Be sure to remove the '[]' above,
57 | # if you add dependencies to this list.
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/mountweb/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # tasks file for mountweb
3 | - name: setup webserver
4 | hosts: webservers
5 | tasks:
6 | - name: create partition
7 | parted:
8 | device: /dev/sdb
9 | number: 1
10 | state: present
11 | part_end: 2G
12 | - name: create filesystem
13 | filesystem:
14 | fstype: xfs
15 | dev: /dev/sdb1
16 | force: no
17 | - name: mount
18 | mount:
19 | path: /web
20 | src: /dev/sdb1
21 | fstype: xfs
22 | state: present
23 | - name: set_selinux
24 | sefcontext:
25 | setype: httpd_sys_content_t
26 | target: '/web(/.*)?'
27 | state: present
28 | - name: run_restorecon
29 | command: 'restorecon -r /web'
30 |
31 |
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/mountweb/templates/http.j2:
--------------------------------------------------------------------------------
1 | Listen *:80
2 | NameVirtualHost {{ ansible_fqdn }}
3 |
4 |
5 | ServerName {{ ansible_fqdn }}
6 | DocumentRoot /web
7 |
8 |
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/mountweb/tests/inventory:
--------------------------------------------------------------------------------
1 | localhost
2 |
3 |
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/mountweb/tests/test.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: localhost
3 | remote_user: root
4 | roles:
5 | - mountweb
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/mountweb/vars/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # vars file for mountweb
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/web-role/README.md:
--------------------------------------------------------------------------------
1 | Role Name
2 | =========
3 |
4 | A brief description of the role goes here.
5 |
6 | Requirements
7 | ------------
8 |
9 | Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
10 |
11 | Role Variables
12 | --------------
13 |
14 | A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
15 |
16 | Dependencies
17 | ------------
18 |
19 | A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
20 |
21 | Example Playbook
22 | ----------------
23 |
24 | Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
25 |
26 | - hosts: servers
27 | roles:
28 | - { role: username.rolename, x: 42 }
29 |
30 | License
31 | -------
32 |
33 | BSD
34 |
35 | Author Information
36 | ------------------
37 |
38 | An optional section for the role authors to include contact information, or a website (HTML is not allowed).
39 |
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/web-role/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # defaults file for web-role
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/web-role/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # handlers file for web-role
3 |
4 | handlers:
5 | - name: restart_web
6 | service:
7 | name: httpd
8 | state: restarted
9 |
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/web-role/meta/main.yml:
--------------------------------------------------------------------------------
1 | galaxy_info:
2 | author: your name
3 | description: your description
4 | company: your company (optional)
5 |
6 | # If the issue tracker for your role is not on github, uncomment the
7 | # next line and provide a value
8 | # issue_tracker_url: http://example.com/issue/tracker
9 |
10 | # Some suggested licenses:
11 | # - BSD (default)
12 | # - MIT
13 | # - GPLv2
14 | # - GPLv3
15 | # - Apache
16 | # - CC-BY
17 | license: license (GPLv2, CC-BY, etc)
18 |
19 | min_ansible_version: 1.2
20 |
21 | # If this a Container Enabled role, provide the minimum Ansible Container version.
22 | # min_ansible_container_version:
23 |
24 | # Optionally specify the branch Galaxy will use when accessing the GitHub
25 | # repo for this role. During role install, if no tags are available,
26 | # Galaxy will use this branch. During import Galaxy will access files on
27 | # this branch. If Travis integration is configured, only notifications for this
28 | # branch will be accepted. Otherwise, in all cases, the repo's default branch
29 | # (usually master) will be used.
30 | #github_branch:
31 |
32 | #
33 | # platforms is a list of platforms, and each platform has a name and a list of versions.
34 | #
35 | # platforms:
36 | # - name: Fedora
37 | # versions:
38 | # - all
39 | # - 25
40 | # - name: SomePlatform
41 | # versions:
42 | # - all
43 | # - 1.0
44 | # - 7
45 | # - 99.99
46 |
47 | galaxy_tags: []
48 | # List tags for your role here, one per line. A tag is a keyword that describes
49 | # and categorizes the role. Users find roles by searching for tags. Be sure to
50 | # remove the '[]' above, if you add tags to this list.
51 | #
52 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters.
53 | # Maximum 20 tags per role.
54 |
55 | dependencies: []
56 | # List your role dependencies here, one per line. Be sure to remove the '[]' above,
57 | # if you add dependencies to this list.
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/web-role/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - include_tasks: webservers.yml
3 | when: 'webservers' in group_names
4 |
5 | - include_tasks: webclients.yml
6 | when: 'webclients' in group_names
7 |
8 |
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/web-role/tasks/webclients.yml:
--------------------------------------------------------------------------------
1 | - hosts: webclients
2 | tasks:
3 | - name: install packages
4 | package:
5 | name: "{{ item }}"
6 | state: latest
7 | with_items:
8 | - elinks
9 | - wget
10 |
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/web-role/tasks/webservers.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: include the variable file
3 | include_vars: vars/webservers.yml
4 |
5 | - name: install packages
6 | package:
7 | name: "{{ web_packages }}"
8 | state: latest
9 |
10 | - name: copy template
11 | template:
12 | src: templates/httpd.j2
13 | dest: "{{ web_config_file }}"
14 | owner: root
15 | group: root
16 | mode: 0644
17 | notify:
18 | - restart_web
19 |
20 | - name: start service
21 | service:
22 | name: httpd
23 | state: started
24 | enabled: true
25 |
26 | - name: open port in firewall
27 | firewalld:
28 | service: "{{ firewall_service }}"
29 | state: enabled
30 | immediate: true
31 | permanent: true
32 |
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/web-role/templates/httpd.j2:
--------------------------------------------------------------------------------
1 | #
2 | # This is the main Apache HTTP server configuration file. It contains the
3 | # configuration directives that give the server its instructions.
4 | # See for detailed information.
5 | # In particular, see
6 | #
7 | # for a discussion of each configuration directive.
8 | #
9 | # Do NOT simply read the instructions in here without understanding
10 | # what they do. They're here only as hints or reminders. If you are unsure
11 | # consult the online docs. You have been warned.
12 | #
13 | # Configuration and logfile names: If the filenames you specify for many
14 | # of the server's control files begin with "/" (or "drive:/" for Win32), the
15 | # server will use that explicit path. If the filenames do *not* begin
16 | # with "/", the value of ServerRoot is prepended -- so 'log/access_log'
17 | # with ServerRoot set to '/www' will be interpreted by the
18 | # server as '/www/log/access_log', where as '/log/access_log' will be
19 | # interpreted as '/log/access_log'.
20 |
21 | #
22 | # ServerRoot: The top of the directory tree under which the server's
23 | # configuration, error, and log files are kept.
24 | #
25 | # Do not add a slash at the end of the directory path. If you point
26 | # ServerRoot at a non-local disk, be sure to specify a local disk on the
27 | # Mutex directive, if file-based mutexes are used. If you wish to share the
28 | # same ServerRoot for multiple httpd daemons, you will need to change at
29 | # least PidFile.
30 | #
31 | ServerRoot "/etc/httpd"
32 |
33 | #
34 | # Listen: Allows you to bind Apache to specific IP addresses and/or
35 | # ports, instead of the default. See also the
36 | # directive.
37 | #
38 | # Change this to Listen on specific IP addresses as shown below to
39 | # prevent Apache from glomming onto all bound IP addresses.
40 | #
41 | #Listen 12.34.56.78:80
42 | Listen 80
43 |
44 | #
45 | # Dynamic Shared Object (DSO) Support
46 | #
47 | # To be able to use the functionality of a module which was built as a DSO you
48 | # have to place corresponding `LoadModule' lines at this location so the
49 | # directives contained in it are actually available _before_ they are used.
50 | # Statically compiled modules (those listed by `httpd -l') do not need
51 | # to be loaded here.
52 | #
53 | # Example:
54 | # LoadModule foo_module modules/mod_foo.so
55 | #
56 | Include conf.modules.d/*.conf
57 |
58 | #
59 | # If you wish httpd to run as a different user or group, you must run
60 | # httpd as root initially and it will switch.
61 | #
62 | # User/Group: The name (or #number) of the user/group to run httpd as.
63 | # It is usually good practice to create a dedicated user and group for
64 | # running httpd, as with most system services.
65 | #
66 | User apache
67 | Group apache
68 |
69 | # 'Main' server configuration
70 | #
71 | # The directives in this section set up the values used by the 'main'
72 | # server, which responds to any requests that aren't handled by a
73 | # definition. These values also provide defaults for
74 | # any containers you may define later in the file.
75 | #
76 | # All of these directives may appear inside containers,
77 | # in which case these default settings will be overridden for the
78 | # virtual host being defined.
79 | #
80 |
81 | #
82 | # ServerAdmin: Your address, where problems with the server should be
83 | # e-mailed. This address appears on some server-generated pages, such
84 | # as error documents. e.g. admin@your-domain.com
85 | #
86 | ServerAdmin root@localhost
87 |
88 | #
89 | # ServerName gives the name and port that the server uses to identify itself.
90 | # This can often be determined automatically, but we recommend you specify
91 | # it explicitly to prevent problems during startup.
92 | #
93 | # If your host doesn't have a registered DNS name, enter its IP address here.
94 | #
95 | #ServerName www.example.com:80
96 |
97 | #
98 | # Deny access to the entirety of your server's filesystem. You must
99 | # explicitly permit access to web content directories in other
100 | # blocks below.
101 | #
102 |
103 | AllowOverride none
104 | Require all denied
105 |
106 |
107 | #
108 | # Note that from this point forward you must specifically allow
109 | # particular features to be enabled - so if something's not working as
110 | # you might expect, make sure that you have specifically enabled it
111 | # below.
112 | #
113 |
114 | #
115 | # DocumentRoot: The directory out of which you will serve your
116 | # documents. By default, all requests are taken from this directory, but
117 | # symbolic links and aliases may be used to point to other locations.
118 | #
119 | DocumentRoot "/var/www/html"
120 |
121 | #
122 | # Relax access to content within /var/www.
123 | #
124 |
125 | AllowOverride None
126 | # Allow open access:
127 | Require all granted
128 |
129 |
130 | # Further relax access to the default document root:
131 |
132 | #
133 | # Possible values for the Options directive are "None", "All",
134 | # or any combination of:
135 | # Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
136 | #
137 | # Note that "MultiViews" must be named *explicitly* --- "Options All"
138 | # doesn't give it to you.
139 | #
140 | # The Options directive is both complicated and important. Please see
141 | # http://httpd.apache.org/docs/2.4/mod/core.html#options
142 | # for more information.
143 | #
144 | Options Indexes FollowSymLinks
145 |
146 | #
147 | # AllowOverride controls what directives may be placed in .htaccess files.
148 | # It can be "All", "None", or any combination of the keywords:
149 | # Options FileInfo AuthConfig Limit
150 | #
151 | AllowOverride None
152 |
153 | #
154 | # Controls who can get stuff from this server.
155 | #
156 | Require all granted
157 |
158 |
159 | #
160 | # DirectoryIndex: sets the file that Apache will serve if a directory
161 | # is requested.
162 | #
163 |
164 | DirectoryIndex index.html
165 |
166 |
167 | #
168 | # The following lines prevent .htaccess and .htpasswd files from being
169 | # viewed by Web clients.
170 | #
171 |
172 | Require all denied
173 |
174 |
175 | #
176 | # ErrorLog: The location of the error log file.
177 | # If you do not specify an ErrorLog directive within a
178 | # container, error messages relating to that virtual host will be
179 | # logged here. If you *do* define an error logfile for a
180 | # container, that host's errors will be logged there and not here.
181 | #
182 | ErrorLog "logs/error_log"
183 |
184 | #
185 | # LogLevel: Control the number of messages logged to the error_log.
186 | # Possible values include: debug, info, notice, warn, error, crit,
187 | # alert, emerg.
188 | #
189 | LogLevel warn
190 |
191 |
192 | #
193 | # The following directives define some format nicknames for use with
194 | # a CustomLog directive (see below).
195 | #
196 | LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
197 | LogFormat "%h %l %u %t \"%r\" %>s %b" common
198 |
199 |
200 | # You need to enable mod_logio.c to use %I and %O
201 | LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
202 |
203 |
204 | #
205 | # The location and format of the access logfile (Common Logfile Format).
206 | # If you do not define any access logfiles within a
207 | # container, they will be logged here. Contrariwise, if you *do*
208 | # define per- access logfiles, transactions will be
209 | # logged therein and *not* in this file.
210 | #
211 | #CustomLog "logs/access_log" common
212 |
213 | #
214 | # If you prefer a logfile with access, agent, and referer information
215 | # (Combined Logfile Format) you can use the following directive.
216 | #
217 | CustomLog "logs/access_log" combined
218 |
219 |
220 |
221 | #
222 | # Redirect: Allows you to tell clients about documents that used to
223 | # exist in your server's namespace, but do not anymore. The client
224 | # will make a new request for the document at its new location.
225 | # Example:
226 | # Redirect permanent /foo http://www.example.com/bar
227 |
228 | #
229 | # Alias: Maps web paths into filesystem paths and is used to
230 | # access content that does not live under the DocumentRoot.
231 | # Example:
232 | # Alias /webpath /full/filesystem/path
233 | #
234 | # If you include a trailing / on /webpath then the server will
235 | # require it to be present in the URL. You will also likely
236 | # need to provide a section to allow access to
237 | # the filesystem path.
238 |
239 | #
240 | # ScriptAlias: This controls which directories contain server scripts.
241 | # ScriptAliases are essentially the same as Aliases, except that
242 | # documents in the target directory are treated as applications and
243 | # run by the server when requested rather than as documents sent to the
244 | # client. The same rules about trailing "/" apply to ScriptAlias
245 | # directives as to Alias.
246 | #
247 | ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"
248 |
249 |
250 |
251 | #
252 | # "/var/www/cgi-bin" should be changed to whatever your ScriptAliased
253 | # CGI directory exists, if you have that configured.
254 | #
255 |
256 | AllowOverride None
257 | Options None
258 | Require all granted
259 |
260 |
261 |
262 | #
263 | # TypesConfig points to the file containing the list of mappings from
264 | # filename extension to MIME-type.
265 | #
266 | TypesConfig /etc/mime.types
267 |
268 | #
269 | # AddType allows you to add to or override the MIME configuration
270 | # file specified in TypesConfig for specific file types.
271 | #
272 | #AddType application/x-gzip .tgz
273 | #
274 | # AddEncoding allows you to have certain browsers uncompress
275 | # information on the fly. Note: Not all browsers support this.
276 | #
277 | #AddEncoding x-compress .Z
278 | #AddEncoding x-gzip .gz .tgz
279 | #
280 | # If the AddEncoding directives above are commented-out, then you
281 | # probably should define those extensions to indicate media types:
282 | #
283 | AddType application/x-compress .Z
284 | AddType application/x-gzip .gz .tgz
285 |
286 | #
287 | # AddHandler allows you to map certain file extensions to "handlers":
288 | # actions unrelated to filetype. These can be either built into the server
289 | # or added with the Action directive (see below)
290 | #
291 | # To use CGI scripts outside of ScriptAliased directories:
292 | # (You will also need to add "ExecCGI" to the "Options" directive.)
293 | #
294 | #AddHandler cgi-script .cgi
295 |
296 | # For type maps (negotiated resources):
297 | #AddHandler type-map var
298 |
299 | #
300 | # Filters allow you to process content before it is sent to the client.
301 | #
302 | # To parse .shtml files for server-side includes (SSI):
303 | # (You will also need to add "Includes" to the "Options" directive.)
304 | #
305 | AddType text/html .shtml
306 | AddOutputFilter INCLUDES .shtml
307 |
308 |
309 | #
310 | # Specify a default charset for all content served; this enables
311 | # interpretation of all content as UTF-8 by default. To use the
312 | # default browser choice (ISO-8859-1), or to allow the META tags
313 | # in HTML content to override this choice, comment out this
314 | # directive:
315 | #
316 | AddDefaultCharset UTF-8
317 |
318 |
319 | #
320 | # The mod_mime_magic module allows the server to use various hints from the
321 | # contents of the file itself to determine its type. The MIMEMagicFile
322 | # directive tells the module where the hint definitions are located.
323 | #
324 | MIMEMagicFile conf/magic
325 |
326 |
327 | #
328 | # Customizable error responses come in three flavors:
329 | # 1) plain text 2) local redirects 3) external redirects
330 | #
331 | # Some examples:
332 | #ErrorDocument 500 "The server made a boo boo."
333 | #ErrorDocument 404 /missing.html
334 | #ErrorDocument 404 "/cgi-bin/missing_handler.pl"
335 | #ErrorDocument 402 http://www.example.com/subscription_info.html
336 | #
337 |
338 | #
339 | # EnableMMAP and EnableSendfile: On systems that support it,
340 | # memory-mapping or the sendfile syscall may be used to deliver
341 | # files. This usually improves server performance, but must
342 | # be turned off when serving from networked-mounted
343 | # filesystems or if support for these functions is otherwise
344 | # broken on your system.
345 | # Defaults if commented: EnableMMAP On, EnableSendfile Off
346 | #
347 | #EnableMMAP off
348 | EnableSendfile on
349 |
350 | # Supplemental configuration
351 | #
352 | # Load config files in the "/etc/httpd/conf.d" directory, if any.
353 | IncludeOptional conf.d/*.conf
354 |
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/web-role/tests/inventory:
--------------------------------------------------------------------------------
1 | localhost
2 |
3 |
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/web-role/tests/test.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: localhost
3 | remote_user: root
4 | roles:
5 | - web-role
--------------------------------------------------------------------------------
/playbooks/exam-lab/roles/web-role/vars/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # vars file for web-role
3 | web_packages: httpd
4 | web_service: httpd
5 | web_config_file: /etc/httpd/conf/httpd.conf
6 | firewall_service: http
7 |
--------------------------------------------------------------------------------
/playbooks/ftp/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | remote_user = ansible
3 | host_key_checking = false
4 | inventory = inventory
5 |
6 | [privilege_escalation]
7 | become = True
8 | become_method = sudo
9 | become_user = root
10 | become_ask_pass = False
11 |
12 |
--------------------------------------------------------------------------------
/playbooks/ftp/inventory:
--------------------------------------------------------------------------------
1 | [all]
2 | ansible1.example.com
3 | ansible2.example.com
4 |
--------------------------------------------------------------------------------
/playbooks/ftp/vsftpd.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: deploy vsftpd
3 | hosts: all
4 | tasks:
5 | - name: install vsftpd
6 | package:
7 | name: vsftpd
8 | state: latest
9 | - name: enable vsftpd
10 | service: name=vsftpd enabled=true
11 | - name: create readme file
12 | copy:
13 | content: "welcome to my friendly server\n"
14 | dest: /var/ftp/pub/README
15 | force: no
16 | mode: 0444
17 | ...
18 |
--------------------------------------------------------------------------------
/playbooks/includes/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | inventory = lab-inventory
3 | remote_user = ansible
4 | host_key_checking = false
5 |
6 | [privilege_escalation]
7 | become = True
8 | become_method = sudo
9 | become_user = root
10 | become_ask_pass = False
11 |
12 |
--------------------------------------------------------------------------------
/playbooks/includes/inventory.yml:
--------------------------------------------------------------------------------
1 | [all]
2 | ansible1.example.com
3 | ansible2.example.com
4 |
5 | [lamp]
6 | ansible1.example.com
7 |
8 | [file]
9 | ansible2.example.com
10 |
--------------------------------------------------------------------------------
/playbooks/includes/site.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: lamp
3 | tasks:
4 | - name: include lamp tasks
5 | include: tasks/lamp.yml
6 |
7 | - hosts: file
8 | tasks:
9 | - name: include file tasks
10 | include: tasks/file.yml
11 |
12 |
13 |
--------------------------------------------------------------------------------
/playbooks/includes/tasks/file.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: install and start file services
3 | yum:
4 | name:
5 | - samba
6 | - vsftpd
7 | state: latest
8 |
9 | - name: start samba server
10 | service:
11 | name: samba
12 | state: started
13 | enabled: true
14 |
15 | - name: start the ftp service
16 | service:
17 | name: vsftpd
18 | state: started
19 | enabled: true
20 |
--------------------------------------------------------------------------------
/playbooks/includes/tasks/lamp.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: install and start the servers
3 | yum:
4 | name:
5 | - mariadb
6 | - httpd
7 | state: latest
8 |
9 | - name: start db server
10 | service:
11 | name: mariadb
12 | state: started
13 | enabled: true
14 |
15 | - name: start the web service
16 | service:
17 | name: httpd
18 | state: started
19 | enabled: true
20 |
21 |
--------------------------------------------------------------------------------
/playbooks/lab/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | inventory = lab-inventory
3 | remote_user = ansible
4 | host_key_checking = false
5 |
6 | [privilege_escalation]
7 | become = True
8 | become_method = sudo
9 | become_user = root
10 | become_ask_pass = False
11 |
12 |
--------------------------------------------------------------------------------
/playbooks/lab/custom.fact:
--------------------------------------------------------------------------------
1 | [packages]
2 | smb_package = samba
3 | ftp_package = vsftpd
4 | db_package = mariadb-server
5 | web_package = httpd
6 |
7 | [services]
8 | smb_service = smb
9 | ftp_service = vsftpd
10 | db_service = mariadb
11 | web_service = httpd
12 |
--------------------------------------------------------------------------------
/playbooks/lab/lab-copy-facts.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Install remote facts
3 | hosts: all
4 | vars:
5 | remote_dir: /etc/ansible/facts.d
6 | facts_file: custom.fact
7 | tasks:
8 | - name: create remote directory
9 | file:
10 | state: directory
11 | recurse: yes
12 | path: "{{ remote_dir }}"
13 | - name: install new facts
14 | copy:
15 | src: "{{ facts_file }}"
16 | dest: "{{ remote_dir }}"
17 |
--------------------------------------------------------------------------------
/playbooks/lab/lab-custom.facts:
--------------------------------------------------------------------------------
1 | [packages]
2 | smb_package = smb
3 | ftp_package = vsftpd
4 | db_package = mariadb-server
5 | web_package = httpd
6 |
7 | [services]
8 | smb_service = smb
9 | ftp_service = vsftpd
10 | db_service = mariadb
11 | web_service = httpd
12 |
--------------------------------------------------------------------------------
/playbooks/lab/lab-facts:
--------------------------------------------------------------------------------
1 | [packages]
2 | smb_package = smb
3 | ftp_package = vsftpd
4 | db_package = mariadb-server
5 | web_package = httpd
6 |
7 | [services]
8 | smb_service = smb
9 | ftp_service = vsftpd
10 | db_service = mariadb
11 | web_service = httpd
12 |
--------------------------------------------------------------------------------
/playbooks/lab/lab-inventory:
--------------------------------------------------------------------------------
1 | [all]
2 | ansible1.example.com
3 | ansible2.example.com
4 |
5 | [lamp]
6 | ansible1.example.com
7 |
8 | [file]
9 | ansible2.example.com
10 |
--------------------------------------------------------------------------------
/playbooks/lab/lab-playbook.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars:
4 | firewall: firewalld
5 |
6 | tasks:
7 | - name: install the firewall
8 | yum:
9 | name: "{{ firewall }}"
10 | state: latest
11 |
12 | - name: start the firewall
13 | service:
14 | name: "{{ firewall }}"
15 | state: started
16 | enabled: true
17 |
18 |
19 | - hosts: lamp
20 | tasks:
21 | - name: include the variable file
22 | include_vars: lab-vars/allvars.yml
23 |
24 | - name: include the tasks
25 | include: lab-tasks/lamp.yml
26 |
27 | - name: open the port for the web server
28 | firewalld:
29 | service: http
30 | state: enabled
31 | immediate: true
32 | permanent: true
33 |
34 | - name: create index.html
35 | copy:
36 | content: "{{ ansible_fqdn }}({{ ansible_default_ipv4.address }}) managed by Ansible\n"
37 | dest: "{{ web_root }}/index.html"
38 |
39 | - hosts: file
40 | tasks:
41 | - name: include the variabe file
42 | include_vars: lab-vars/allvars.yml
43 |
44 | - name: include the tasks
45 | include: lab-tasks/file.yml
46 |
47 | - name: open the port for the ftp service
48 | firewalld:
49 | service: samba
50 | state: enabled
51 | immediate: true
52 | permanent: true
53 |
54 | - name: open the port for the smb service
55 | firewalld:
56 | service: ftp
57 | state: enabled
58 | immediate: true
59 | permanent: true
60 |
61 |
62 |
--------------------------------------------------------------------------------
/playbooks/lab/lab-tasks/file.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: install and start file services
3 | yum:
4 | name:
5 | - "{{ ansible_local.custom.packages.smb_package }}"
6 | - "{{ ansible_local.custom.packages.ftp_package }}"
7 | state: latest
8 |
9 | - name: start samba server
10 | service:
11 | name: "{{ ansible_local.custom.services.smb_service }}"
12 | state: started
13 | enabled: true
14 |
15 | - name: start the ftp service
16 | service:
17 | name: "{{ ansible_local.custom.services.ftp_service }}"
18 | state: started
19 | enabled: true
20 |
--------------------------------------------------------------------------------
/playbooks/lab/lab-tasks/lamp.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: install and start the servers
3 | yum:
4 | name:
5 | - "{{ ansible_local.custom.packages.ftp_package }}"
6 | - "{{ ansible_local.custom.packages.web_package }}"
7 | state: latest
8 |
9 | - name: start database server
10 | service:
11 | name: "{{ ansible_local.custom.services.ftp_service }}"
12 | state: started
13 | enabled: true
14 |
15 | - name: start the web service
16 | service:
17 | name: "{{ ansible_local.custom.services.web_service }}"
18 | state: started
19 | enabled: true
20 |
21 |
--------------------------------------------------------------------------------
/playbooks/lab/lab-vars/allvars.yml:
--------------------------------------------------------------------------------
1 | ---
2 | web_root: /var/www/html
3 | ftp_root: /var/ftp
4 |
--------------------------------------------------------------------------------
/playbooks/lab/playbook.retry:
--------------------------------------------------------------------------------
1 | ansible2.example.com
2 |
--------------------------------------------------------------------------------
/playbooks/lab/playbook.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars:
4 | firewall: firewalld
5 |
6 | tasks:
7 | - name: install the firewall
8 | yum:
9 | name: "{{ firewall }}"
10 | state: latest
11 |
12 | - name: start the firewall
13 | service:
14 | name: "{{ firewall }}"
15 | state: started
16 | enabled: true
17 |
18 |
19 | - hosts: lamp
20 | tasks:
21 | - name: include the variable file
22 | include_vars: lab-vars/allvars.yml
23 |
24 | - name: include the tasks
25 | include: lab-tasks/lamp.yml
26 |
27 | - name: open the port for the web server
28 | firewalld:
29 | service: http
30 | state: enabled
31 | immediate: true
32 | permanent: true
33 |
34 | - name: create index.html
35 | copy:
36 | content: "{{ ansible_fqdn }}({{ ansible_default_ipv4.address }}) managed by Ansible\n"
37 | dest: "{{ web_root }}/index.html"
38 |
39 | - hosts: file
40 | tasks:
41 | - name: include the variabe file
42 | include_vars: lab-vars/allvars.yml
43 |
44 | - name: include the tasks
45 | include: lab-tasks/file.yml
46 |
47 | - name: open the port for the ftp service
48 | firewalld:
49 | service: ftp
50 | state: enabled
51 | immediate: true
52 | permanent: true
53 |
54 | - name: open the port for the smb service
55 | firewalld:
56 | service: samba
57 | state: enabled
58 | immediate: true
59 | permanent: true
60 |
61 |
62 |
--------------------------------------------------------------------------------
/playbooks/lab10/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | remote_user = ansible
3 | host_key_checking = false
4 | inventory = inventory
5 |
6 | [privilege_escalation]
7 | become = True
8 | become_method = sudo
9 | become_user = root
10 | become_ask_pass = False
11 |
12 |
--------------------------------------------------------------------------------
/playbooks/lab10/disks.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: configure LVM
3 | hosts: ansible2.example.com
4 | tasks:
5 | - name: create partition
6 | parted:
7 | device: /dev/sdb
8 | number: 1
9 | state: present
10 | part_end: 2G
11 | - name: create filesystem
12 | filesystem:
13 | fstype: xfs
14 | dev: /dev/sdb1
15 | force: no
16 | - name: mount
17 | mount:
18 | path: /web
19 | src: /dev/sdb1
20 | fstype: xfs
21 | state: present
22 |
--------------------------------------------------------------------------------
/playbooks/lab10/inventory:
--------------------------------------------------------------------------------
1 | [webservers]
2 | ansible1.example.com
3 |
4 | [webclients]
5 | ansible2.example.com
6 | ansible3.example.com
7 |
--------------------------------------------------------------------------------
/playbooks/lab10/lab10-3.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: setup web environment
3 | hosts: all
4 |
5 | roles:
6 | - web-role
7 | - mountweb
8 |
--------------------------------------------------------------------------------
/playbooks/lab10/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars:
4 |
5 | tasks:
6 |
7 | - hosts: webservers
8 | tasks:
9 | - name: include the variable file
10 | include_vars: vars/webservers.yml
11 |
12 | - name: install packages
13 | package:
14 | name: "{{ web_packages }}"
15 | state: latest
16 |
17 | - name: copy template
18 | template:
19 | src: templates:/httpd.j2
20 | dest: "{{ web_config_file }}"
21 | owner: root
22 | group: root
23 | mode: 0644
24 | notify:
25 | - restart_web
26 |
27 | - name: start service
28 | service:
29 | name: httpd
30 | state: started
31 | enabled: true
32 |
33 | - name: open port in firewall
34 | firewalld:
35 | service: "{{ firewall_service }}"
36 | state: enabled
37 | immediate: true
38 | permanent: true
39 |
40 | handlers:
41 | - name: restart_web
42 | service:
43 | name: httpd
44 | state: restarted
45 |
46 | - hosts: webclients
47 | tasks:
48 | - name: install packages
49 | package:
50 | name: "{{ item }}"
51 | state: latest
52 | with_items:
53 | - elinks
54 | - wget
55 |
--------------------------------------------------------------------------------
/playbooks/lab10/roles/mountweb/README.md:
--------------------------------------------------------------------------------
1 | Role Name
2 | =========
3 |
4 | A brief description of the role goes here.
5 |
6 | Requirements
7 | ------------
8 |
9 | Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
10 |
11 | Role Variables
12 | --------------
13 |
14 | A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
15 |
16 | Dependencies
17 | ------------
18 |
19 | A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
20 |
21 | Example Playbook
22 | ----------------
23 |
24 | Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
25 |
26 | - hosts: servers
27 | roles:
28 | - { role: username.rolename, x: 42 }
29 |
30 | License
31 | -------
32 |
33 | BSD
34 |
35 | Author Information
36 | ------------------
37 |
38 | An optional section for the role authors to include contact information, or a website (HTML is not allowed).
39 |
--------------------------------------------------------------------------------
/playbooks/lab10/roles/mountweb/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # defaults file for mountweb
--------------------------------------------------------------------------------
/playbooks/lab10/roles/mountweb/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # handlers file for mountweb
--------------------------------------------------------------------------------
/playbooks/lab10/roles/mountweb/meta/main.yml:
--------------------------------------------------------------------------------
1 | galaxy_info:
2 | author: your name
3 | description: your description
4 | company: your company (optional)
5 |
6 | # If the issue tracker for your role is not on github, uncomment the
7 | # next line and provide a value
8 | # issue_tracker_url: http://example.com/issue/tracker
9 |
10 | # Some suggested licenses:
11 | # - BSD (default)
12 | # - MIT
13 | # - GPLv2
14 | # - GPLv3
15 | # - Apache
16 | # - CC-BY
17 | license: license (GPLv2, CC-BY, etc)
18 |
19 | min_ansible_version: 1.2
20 |
21 | # If this a Container Enabled role, provide the minimum Ansible Container version.
22 | # min_ansible_container_version:
23 |
24 | # Optionally specify the branch Galaxy will use when accessing the GitHub
25 | # repo for this role. During role install, if no tags are available,
26 | # Galaxy will use this branch. During import Galaxy will access files on
27 | # this branch. If Travis integration is configured, only notifications for this
28 | # branch will be accepted. Otherwise, in all cases, the repo's default branch
29 | # (usually master) will be used.
30 | #github_branch:
31 |
32 | #
33 | # platforms is a list of platforms, and each platform has a name and a list of versions.
34 | #
35 | # platforms:
36 | # - name: Fedora
37 | # versions:
38 | # - all
39 | # - 25
40 | # - name: SomePlatform
41 | # versions:
42 | # - all
43 | # - 1.0
44 | # - 7
45 | # - 99.99
46 |
47 | galaxy_tags: []
48 | # List tags for your role here, one per line. A tag is a keyword that describes
49 | # and categorizes the role. Users find roles by searching for tags. Be sure to
50 | # remove the '[]' above, if you add tags to this list.
51 | #
52 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters.
53 | # Maximum 20 tags per role.
54 |
55 | dependencies: []
56 | # List your role dependencies here, one per line. Be sure to remove the '[]' above,
57 | # if you add dependencies to this list.
--------------------------------------------------------------------------------
/playbooks/lab10/roles/mountweb/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # tasks file for mountweb
3 | - name: setup webserver
4 | hosts: webservers
5 | tasks:
6 | - name: create partition
7 | parted:
8 | device: /dev/sdb
9 | number: 1
10 | state: present
11 | part_end: 2G
12 | - name: create filesystem
13 | filesystem:
14 | fstype: xfs
15 | dev: /dev/sdb1
16 | force: no
17 | - name: mount
18 | mount:
19 | path: /web
20 | src: /dev/sdb1
21 | fstype: xfs
22 | state: present
23 | - name: set_selinux
24 | sefcontext:
25 | setype: httpd_sys_content_t
26 | target: '/web(/.*)?'
27 | state: present
28 | - name: run_restorecon
29 | command: 'restorecon -r /web'
30 |
31 |
--------------------------------------------------------------------------------
/playbooks/lab10/roles/mountweb/templates/http.j2:
--------------------------------------------------------------------------------
1 | Listen *:80
2 | NameVirtualHost {{ ansible_fqdn }}
3 |
4 |
5 | ServerName {{ ansible_fqdn }}
6 | DocumentRoot /web
7 |
8 |
--------------------------------------------------------------------------------
/playbooks/lab10/roles/mountweb/tests/inventory:
--------------------------------------------------------------------------------
1 | localhost
2 |
3 |
--------------------------------------------------------------------------------
/playbooks/lab10/roles/mountweb/tests/test.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: localhost
3 | remote_user: root
4 | roles:
5 | - mountweb
--------------------------------------------------------------------------------
/playbooks/lab10/roles/mountweb/vars/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # vars file for mountweb
--------------------------------------------------------------------------------
/playbooks/lab10/roles/web-role/README.md:
--------------------------------------------------------------------------------
1 | Role Name
2 | =========
3 |
4 | A brief description of the role goes here.
5 |
6 | Requirements
7 | ------------
8 |
9 | Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
10 |
11 | Role Variables
12 | --------------
13 |
14 | A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
15 |
16 | Dependencies
17 | ------------
18 |
19 | A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
20 |
21 | Example Playbook
22 | ----------------
23 |
24 | Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
25 |
26 | - hosts: servers
27 | roles:
28 | - { role: username.rolename, x: 42 }
29 |
30 | License
31 | -------
32 |
33 | BSD
34 |
35 | Author Information
36 | ------------------
37 |
38 | An optional section for the role authors to include contact information, or a website (HTML is not allowed).
39 |
--------------------------------------------------------------------------------
/playbooks/lab10/roles/web-role/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # defaults file for web-role
--------------------------------------------------------------------------------
/playbooks/lab10/roles/web-role/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # handlers file for web-role
3 |
4 | handlers:
5 | - name: restart_web
6 | service:
7 | name: httpd
8 | state: restarted
9 |
--------------------------------------------------------------------------------
/playbooks/lab10/roles/web-role/meta/main.yml:
--------------------------------------------------------------------------------
1 | galaxy_info:
2 | author: your name
3 | description: your description
4 | company: your company (optional)
5 |
6 | # If the issue tracker for your role is not on github, uncomment the
7 | # next line and provide a value
8 | # issue_tracker_url: http://example.com/issue/tracker
9 |
10 | # Some suggested licenses:
11 | # - BSD (default)
12 | # - MIT
13 | # - GPLv2
14 | # - GPLv3
15 | # - Apache
16 | # - CC-BY
17 | license: license (GPLv2, CC-BY, etc)
18 |
19 | min_ansible_version: 1.2
20 |
21 | # If this a Container Enabled role, provide the minimum Ansible Container version.
22 | # min_ansible_container_version:
23 |
24 | # Optionally specify the branch Galaxy will use when accessing the GitHub
25 | # repo for this role. During role install, if no tags are available,
26 | # Galaxy will use this branch. During import Galaxy will access files on
27 | # this branch. If Travis integration is configured, only notifications for this
28 | # branch will be accepted. Otherwise, in all cases, the repo's default branch
29 | # (usually master) will be used.
30 | #github_branch:
31 |
32 | #
33 | # platforms is a list of platforms, and each platform has a name and a list of versions.
34 | #
35 | # platforms:
36 | # - name: Fedora
37 | # versions:
38 | # - all
39 | # - 25
40 | # - name: SomePlatform
41 | # versions:
42 | # - all
43 | # - 1.0
44 | # - 7
45 | # - 99.99
46 |
47 | galaxy_tags: []
48 | # List tags for your role here, one per line. A tag is a keyword that describes
49 | # and categorizes the role. Users find roles by searching for tags. Be sure to
50 | # remove the '[]' above, if you add tags to this list.
51 | #
52 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters.
53 | # Maximum 20 tags per role.
54 |
55 | dependencies: []
56 | # List your role dependencies here, one per line. Be sure to remove the '[]' above,
57 | # if you add dependencies to this list.
--------------------------------------------------------------------------------
/playbooks/lab10/roles/web-role/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - include_tasks: webservers.yml
3 | when: 'webservers' in group_names
4 |
5 | - include_tasks: webclients.yml
6 | when: 'webclients' in group_names
7 |
8 |
--------------------------------------------------------------------------------
/playbooks/lab10/roles/web-role/tasks/webclients.yml:
--------------------------------------------------------------------------------
1 | - hosts: webclients
2 | tasks:
3 | - name: install packages
4 | package:
5 | name: "{{ item }}"
6 | state: latest
7 | with_items:
8 | - elinks
9 | - wget
10 |
--------------------------------------------------------------------------------
/playbooks/lab10/roles/web-role/tasks/webservers.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: include the variable file
3 | include_vars: vars/webservers.yml
4 |
5 | - name: install packages
6 | package:
7 | name: "{{ web_packages }}"
8 | state: latest
9 |
10 | - name: copy template
11 | template:
12 | src: templates/httpd.j2
13 | dest: "{{ web_config_file }}"
14 | owner: root
15 | group: root
16 | mode: 0644
17 | notify:
18 | - restart_web
19 |
20 | - name: start service
21 | service:
22 | name: httpd
23 | state: started
24 | enabled: true
25 |
26 | - name: open port in firewall
27 | firewalld:
28 | service: "{{ firewall_service }}"
29 | state: enabled
30 | immediate: true
31 | permanent: true
32 |
--------------------------------------------------------------------------------
/playbooks/lab10/roles/web-role/templates/httpd.j2:
--------------------------------------------------------------------------------
1 | #
2 | # This is the main Apache HTTP server configuration file. It contains the
3 | # configuration directives that give the server its instructions.
4 | # See for detailed information.
5 | # In particular, see
6 | #
7 | # for a discussion of each configuration directive.
8 | #
9 | # Do NOT simply read the instructions in here without understanding
10 | # what they do. They're here only as hints or reminders. If you are unsure
11 | # consult the online docs. You have been warned.
12 | #
13 | # Configuration and logfile names: If the filenames you specify for many
14 | # of the server's control files begin with "/" (or "drive:/" for Win32), the
15 | # server will use that explicit path. If the filenames do *not* begin
16 | # with "/", the value of ServerRoot is prepended -- so 'log/access_log'
17 | # with ServerRoot set to '/www' will be interpreted by the
18 | # server as '/www/log/access_log', where as '/log/access_log' will be
19 | # interpreted as '/log/access_log'.
20 |
21 | #
22 | # ServerRoot: The top of the directory tree under which the server's
23 | # configuration, error, and log files are kept.
24 | #
25 | # Do not add a slash at the end of the directory path. If you point
26 | # ServerRoot at a non-local disk, be sure to specify a local disk on the
27 | # Mutex directive, if file-based mutexes are used. If you wish to share the
28 | # same ServerRoot for multiple httpd daemons, you will need to change at
29 | # least PidFile.
30 | #
31 | ServerRoot "/etc/httpd"
32 |
33 | #
34 | # Listen: Allows you to bind Apache to specific IP addresses and/or
35 | # ports, instead of the default. See also the
36 | # directive.
37 | #
38 | # Change this to Listen on specific IP addresses as shown below to
39 | # prevent Apache from glomming onto all bound IP addresses.
40 | #
41 | #Listen 12.34.56.78:80
42 | Listen 80
43 |
44 | #
45 | # Dynamic Shared Object (DSO) Support
46 | #
47 | # To be able to use the functionality of a module which was built as a DSO you
48 | # have to place corresponding `LoadModule' lines at this location so the
49 | # directives contained in it are actually available _before_ they are used.
50 | # Statically compiled modules (those listed by `httpd -l') do not need
51 | # to be loaded here.
52 | #
53 | # Example:
54 | # LoadModule foo_module modules/mod_foo.so
55 | #
56 | Include conf.modules.d/*.conf
57 |
58 | #
59 | # If you wish httpd to run as a different user or group, you must run
60 | # httpd as root initially and it will switch.
61 | #
62 | # User/Group: The name (or #number) of the user/group to run httpd as.
63 | # It is usually good practice to create a dedicated user and group for
64 | # running httpd, as with most system services.
65 | #
66 | User apache
67 | Group apache
68 |
69 | # 'Main' server configuration
70 | #
71 | # The directives in this section set up the values used by the 'main'
72 | # server, which responds to any requests that aren't handled by a
73 | # definition. These values also provide defaults for
74 | # any containers you may define later in the file.
75 | #
76 | # All of these directives may appear inside containers,
77 | # in which case these default settings will be overridden for the
78 | # virtual host being defined.
79 | #
80 |
81 | #
82 | # ServerAdmin: Your address, where problems with the server should be
83 | # e-mailed. This address appears on some server-generated pages, such
84 | # as error documents. e.g. admin@your-domain.com
85 | #
86 | ServerAdmin root@localhost
87 |
88 | #
89 | # ServerName gives the name and port that the server uses to identify itself.
90 | # This can often be determined automatically, but we recommend you specify
91 | # it explicitly to prevent problems during startup.
92 | #
93 | # If your host doesn't have a registered DNS name, enter its IP address here.
94 | #
95 | #ServerName www.example.com:80
96 |
97 | #
98 | # Deny access to the entirety of your server's filesystem. You must
99 | # explicitly permit access to web content directories in other
100 | # blocks below.
101 | #
102 |
103 | AllowOverride none
104 | Require all denied
105 |
106 |
107 | #
108 | # Note that from this point forward you must specifically allow
109 | # particular features to be enabled - so if something's not working as
110 | # you might expect, make sure that you have specifically enabled it
111 | # below.
112 | #
113 |
114 | #
115 | # DocumentRoot: The directory out of which you will serve your
116 | # documents. By default, all requests are taken from this directory, but
117 | # symbolic links and aliases may be used to point to other locations.
118 | #
119 | DocumentRoot "/var/www/html"
120 |
121 | #
122 | # Relax access to content within /var/www.
123 | #
124 |
125 | AllowOverride None
126 | # Allow open access:
127 | Require all granted
128 |
129 |
130 | # Further relax access to the default document root:
131 |
132 | #
133 | # Possible values for the Options directive are "None", "All",
134 | # or any combination of:
135 | # Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
136 | #
137 | # Note that "MultiViews" must be named *explicitly* --- "Options All"
138 | # doesn't give it to you.
139 | #
140 | # The Options directive is both complicated and important. Please see
141 | # http://httpd.apache.org/docs/2.4/mod/core.html#options
142 | # for more information.
143 | #
144 | Options Indexes FollowSymLinks
145 |
146 | #
147 | # AllowOverride controls what directives may be placed in .htaccess files.
148 | # It can be "All", "None", or any combination of the keywords:
149 | # Options FileInfo AuthConfig Limit
150 | #
151 | AllowOverride None
152 |
153 | #
154 | # Controls who can get stuff from this server.
155 | #
156 | Require all granted
157 |
158 |
159 | #
160 | # DirectoryIndex: sets the file that Apache will serve if a directory
161 | # is requested.
162 | #
163 |
164 | DirectoryIndex index.html
165 |
166 |
167 | #
168 | # The following lines prevent .htaccess and .htpasswd files from being
169 | # viewed by Web clients.
170 | #
171 |
172 | Require all denied
173 |
174 |
175 | #
176 | # ErrorLog: The location of the error log file.
177 | # If you do not specify an ErrorLog directive within a
178 | # container, error messages relating to that virtual host will be
179 | # logged here. If you *do* define an error logfile for a
180 | # container, that host's errors will be logged there and not here.
181 | #
182 | ErrorLog "logs/error_log"
183 |
184 | #
185 | # LogLevel: Control the number of messages logged to the error_log.
186 | # Possible values include: debug, info, notice, warn, error, crit,
187 | # alert, emerg.
188 | #
189 | LogLevel warn
190 |
191 |
192 | #
193 | # The following directives define some format nicknames for use with
194 | # a CustomLog directive (see below).
195 | #
196 | LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
197 | LogFormat "%h %l %u %t \"%r\" %>s %b" common
198 |
199 |
200 | # You need to enable mod_logio.c to use %I and %O
201 | LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
202 |
203 |
204 | #
205 | # The location and format of the access logfile (Common Logfile Format).
206 | # If you do not define any access logfiles within a
207 | # container, they will be logged here. Contrariwise, if you *do*
208 | # define per- access logfiles, transactions will be
209 | # logged therein and *not* in this file.
210 | #
211 | #CustomLog "logs/access_log" common
212 |
213 | #
214 | # If you prefer a logfile with access, agent, and referer information
215 | # (Combined Logfile Format) you can use the following directive.
216 | #
217 | CustomLog "logs/access_log" combined
218 |
219 |
220 |
221 | #
222 | # Redirect: Allows you to tell clients about documents that used to
223 | # exist in your server's namespace, but do not anymore. The client
224 | # will make a new request for the document at its new location.
225 | # Example:
226 | # Redirect permanent /foo http://www.example.com/bar
227 |
228 | #
229 | # Alias: Maps web paths into filesystem paths and is used to
230 | # access content that does not live under the DocumentRoot.
231 | # Example:
232 | # Alias /webpath /full/filesystem/path
233 | #
234 | # If you include a trailing / on /webpath then the server will
235 | # require it to be present in the URL. You will also likely
236 | # need to provide a section to allow access to
237 | # the filesystem path.
238 |
239 | #
240 | # ScriptAlias: This controls which directories contain server scripts.
241 | # ScriptAliases are essentially the same as Aliases, except that
242 | # documents in the target directory are treated as applications and
243 | # run by the server when requested rather than as documents sent to the
244 | # client. The same rules about trailing "/" apply to ScriptAlias
245 | # directives as to Alias.
246 | #
247 | ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"
248 |
249 |
250 |
251 | #
252 | # "/var/www/cgi-bin" should be changed to whatever your ScriptAliased
253 | # CGI directory exists, if you have that configured.
254 | #
255 |
256 | AllowOverride None
257 | Options None
258 | Require all granted
259 |
260 |
261 |
262 | #
263 | # TypesConfig points to the file containing the list of mappings from
264 | # filename extension to MIME-type.
265 | #
266 | TypesConfig /etc/mime.types
267 |
268 | #
269 | # AddType allows you to add to or override the MIME configuration
270 | # file specified in TypesConfig for specific file types.
271 | #
272 | #AddType application/x-gzip .tgz
273 | #
274 | # AddEncoding allows you to have certain browsers uncompress
275 | # information on the fly. Note: Not all browsers support this.
276 | #
277 | #AddEncoding x-compress .Z
278 | #AddEncoding x-gzip .gz .tgz
279 | #
280 | # If the AddEncoding directives above are commented-out, then you
281 | # probably should define those extensions to indicate media types:
282 | #
283 | AddType application/x-compress .Z
284 | AddType application/x-gzip .gz .tgz
285 |
286 | #
287 | # AddHandler allows you to map certain file extensions to "handlers":
288 | # actions unrelated to filetype. These can be either built into the server
289 | # or added with the Action directive (see below)
290 | #
291 | # To use CGI scripts outside of ScriptAliased directories:
292 | # (You will also need to add "ExecCGI" to the "Options" directive.)
293 | #
294 | #AddHandler cgi-script .cgi
295 |
296 | # For type maps (negotiated resources):
297 | #AddHandler type-map var
298 |
299 | #
300 | # Filters allow you to process content before it is sent to the client.
301 | #
302 | # To parse .shtml files for server-side includes (SSI):
303 | # (You will also need to add "Includes" to the "Options" directive.)
304 | #
305 | AddType text/html .shtml
306 | AddOutputFilter INCLUDES .shtml
307 |
308 |
309 | #
310 | # Specify a default charset for all content served; this enables
311 | # interpretation of all content as UTF-8 by default. To use the
312 | # default browser choice (ISO-8859-1), or to allow the META tags
313 | # in HTML content to override this choice, comment out this
314 | # directive:
315 | #
316 | AddDefaultCharset UTF-8
317 |
318 |
319 | #
320 | # The mod_mime_magic module allows the server to use various hints from the
321 | # contents of the file itself to determine its type. The MIMEMagicFile
322 | # directive tells the module where the hint definitions are located.
323 | #
324 | MIMEMagicFile conf/magic
325 |
326 |
327 | #
328 | # Customizable error responses come in three flavors:
329 | # 1) plain text 2) local redirects 3) external redirects
330 | #
331 | # Some examples:
332 | #ErrorDocument 500 "The server made a boo boo."
333 | #ErrorDocument 404 /missing.html
334 | #ErrorDocument 404 "/cgi-bin/missing_handler.pl"
335 | #ErrorDocument 402 http://www.example.com/subscription_info.html
336 | #
337 |
338 | #
339 | # EnableMMAP and EnableSendfile: On systems that support it,
340 | # memory-mapping or the sendfile syscall may be used to deliver
341 | # files. This usually improves server performance, but must
342 | # be turned off when serving from networked-mounted
343 | # filesystems or if support for these functions is otherwise
344 | # broken on your system.
345 | # Defaults if commented: EnableMMAP On, EnableSendfile Off
346 | #
347 | #EnableMMAP off
348 | EnableSendfile on
349 |
350 | # Supplemental configuration
351 | #
352 | # Load config files in the "/etc/httpd/conf.d" directory, if any.
353 | IncludeOptional conf.d/*.conf
354 |
--------------------------------------------------------------------------------
/playbooks/lab10/roles/web-role/tests/inventory:
--------------------------------------------------------------------------------
1 | localhost
2 |
3 |
--------------------------------------------------------------------------------
/playbooks/lab10/roles/web-role/tests/test.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: localhost
3 | remote_user: root
4 | roles:
5 | - web-role
--------------------------------------------------------------------------------
/playbooks/lab10/roles/web-role/vars/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # vars file for web-role
3 | web_packages: httpd
4 | web_service: httpd
5 | web_config_file: /etc/httpd/conf/httpd.conf
6 | firewall_service: http
7 |
--------------------------------------------------------------------------------
/playbooks/lab10/templates/httpd.j2:
--------------------------------------------------------------------------------
1 | #
2 | # This is the main Apache HTTP server configuration file. It contains the
3 | # configuration directives that give the server its instructions.
4 | # See for detailed information.
5 | # In particular, see
6 | #
7 | # for a discussion of each configuration directive.
8 | #
9 | # Do NOT simply read the instructions in here without understanding
10 | # what they do. They're here only as hints or reminders. If you are unsure
11 | # consult the online docs. You have been warned.
12 | #
13 | # Configuration and logfile names: If the filenames you specify for many
14 | # of the server's control files begin with "/" (or "drive:/" for Win32), the
15 | # server will use that explicit path. If the filenames do *not* begin
16 | # with "/", the value of ServerRoot is prepended -- so 'log/access_log'
17 | # with ServerRoot set to '/www' will be interpreted by the
18 | # server as '/www/log/access_log', where as '/log/access_log' will be
19 | # interpreted as '/log/access_log'.
20 |
21 | #
22 | # ServerRoot: The top of the directory tree under which the server's
23 | # configuration, error, and log files are kept.
24 | #
25 | # Do not add a slash at the end of the directory path. If you point
26 | # ServerRoot at a non-local disk, be sure to specify a local disk on the
27 | # Mutex directive, if file-based mutexes are used. If you wish to share the
28 | # same ServerRoot for multiple httpd daemons, you will need to change at
29 | # least PidFile.
30 | #
31 | ServerRoot "/etc/httpd"
32 |
33 | #
34 | # Listen: Allows you to bind Apache to specific IP addresses and/or
35 | # ports, instead of the default. See also the
36 | # directive.
37 | #
38 | # Change this to Listen on specific IP addresses as shown below to
39 | # prevent Apache from glomming onto all bound IP addresses.
40 | #
41 | #Listen 12.34.56.78:80
42 | Listen 80
43 |
44 | #
45 | # Dynamic Shared Object (DSO) Support
46 | #
47 | # To be able to use the functionality of a module which was built as a DSO you
48 | # have to place corresponding `LoadModule' lines at this location so the
49 | # directives contained in it are actually available _before_ they are used.
50 | # Statically compiled modules (those listed by `httpd -l') do not need
51 | # to be loaded here.
52 | #
53 | # Example:
54 | # LoadModule foo_module modules/mod_foo.so
55 | #
56 | Include conf.modules.d/*.conf
57 |
58 | #
59 | # If you wish httpd to run as a different user or group, you must run
60 | # httpd as root initially and it will switch.
61 | #
62 | # User/Group: The name (or #number) of the user/group to run httpd as.
63 | # It is usually good practice to create a dedicated user and group for
64 | # running httpd, as with most system services.
65 | #
66 | User apache
67 | Group apache
68 |
69 | # 'Main' server configuration
70 | #
71 | # The directives in this section set up the values used by the 'main'
72 | # server, which responds to any requests that aren't handled by a
73 | # definition. These values also provide defaults for
74 | # any containers you may define later in the file.
75 | #
76 | # All of these directives may appear inside containers,
77 | # in which case these default settings will be overridden for the
78 | # virtual host being defined.
79 | #
80 |
81 | #
82 | # ServerAdmin: Your address, where problems with the server should be
83 | # e-mailed. This address appears on some server-generated pages, such
84 | # as error documents. e.g. admin@your-domain.com
85 | #
86 | ServerAdmin root@localhost
87 |
88 | #
89 | # ServerName gives the name and port that the server uses to identify itself.
90 | # This can often be determined automatically, but we recommend you specify
91 | # it explicitly to prevent problems during startup.
92 | #
93 | # If your host doesn't have a registered DNS name, enter its IP address here.
94 | #
95 | #ServerName www.example.com:80
96 |
97 | #
98 | # Deny access to the entirety of your server's filesystem. You must
99 | # explicitly permit access to web content directories in other
100 | # blocks below.
101 | #
102 |
103 | AllowOverride none
104 | Require all denied
105 |
106 |
107 | #
108 | # Note that from this point forward you must specifically allow
109 | # particular features to be enabled - so if something's not working as
110 | # you might expect, make sure that you have specifically enabled it
111 | # below.
112 | #
113 |
114 | #
115 | # DocumentRoot: The directory out of which you will serve your
116 | # documents. By default, all requests are taken from this directory, but
117 | # symbolic links and aliases may be used to point to other locations.
118 | #
119 | DocumentRoot "/var/www/html"
120 |
121 | #
122 | # Relax access to content within /var/www.
123 | #
124 |
125 | AllowOverride None
126 | # Allow open access:
127 | Require all granted
128 |
129 |
130 | # Further relax access to the default document root:
131 |
132 | #
133 | # Possible values for the Options directive are "None", "All",
134 | # or any combination of:
135 | # Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
136 | #
137 | # Note that "MultiViews" must be named *explicitly* --- "Options All"
138 | # doesn't give it to you.
139 | #
140 | # The Options directive is both complicated and important. Please see
141 | # http://httpd.apache.org/docs/2.4/mod/core.html#options
142 | # for more information.
143 | #
144 | Options Indexes FollowSymLinks
145 |
146 | #
147 | # AllowOverride controls what directives may be placed in .htaccess files.
148 | # It can be "All", "None", or any combination of the keywords:
149 | # Options FileInfo AuthConfig Limit
150 | #
151 | AllowOverride None
152 |
153 | #
154 | # Controls who can get stuff from this server.
155 | #
156 | Require all granted
157 |
158 |
159 | #
160 | # DirectoryIndex: sets the file that Apache will serve if a directory
161 | # is requested.
162 | #
163 |
164 | DirectoryIndex index.html
165 |
166 |
167 | #
168 | # The following lines prevent .htaccess and .htpasswd files from being
169 | # viewed by Web clients.
170 | #
171 |
172 | Require all denied
173 |
174 |
175 | #
176 | # ErrorLog: The location of the error log file.
177 | # If you do not specify an ErrorLog directive within a
178 | # container, error messages relating to that virtual host will be
179 | # logged here. If you *do* define an error logfile for a
180 | # container, that host's errors will be logged there and not here.
181 | #
182 | ErrorLog "logs/error_log"
183 |
184 | #
185 | # LogLevel: Control the number of messages logged to the error_log.
186 | # Possible values include: debug, info, notice, warn, error, crit,
187 | # alert, emerg.
188 | #
189 | LogLevel warn
190 |
191 |
192 | #
193 | # The following directives define some format nicknames for use with
194 | # a CustomLog directive (see below).
195 | #
196 | LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
197 | LogFormat "%h %l %u %t \"%r\" %>s %b" common
198 |
199 |
200 | # You need to enable mod_logio.c to use %I and %O
201 | LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
202 |
203 |
204 | #
205 | # The location and format of the access logfile (Common Logfile Format).
206 | # If you do not define any access logfiles within a
207 | # container, they will be logged here. Contrariwise, if you *do*
208 | # define per- access logfiles, transactions will be
209 | # logged therein and *not* in this file.
210 | #
211 | #CustomLog "logs/access_log" common
212 |
213 | #
214 | # If you prefer a logfile with access, agent, and referer information
215 | # (Combined Logfile Format) you can use the following directive.
216 | #
217 | CustomLog "logs/access_log" combined
218 |
219 |
220 |
221 | #
222 | # Redirect: Allows you to tell clients about documents that used to
223 | # exist in your server's namespace, but do not anymore. The client
224 | # will make a new request for the document at its new location.
225 | # Example:
226 | # Redirect permanent /foo http://www.example.com/bar
227 |
228 | #
229 | # Alias: Maps web paths into filesystem paths and is used to
230 | # access content that does not live under the DocumentRoot.
231 | # Example:
232 | # Alias /webpath /full/filesystem/path
233 | #
234 | # If you include a trailing / on /webpath then the server will
235 | # require it to be present in the URL. You will also likely
236 | # need to provide a section to allow access to
237 | # the filesystem path.
238 |
239 | #
240 | # ScriptAlias: This controls which directories contain server scripts.
241 | # ScriptAliases are essentially the same as Aliases, except that
242 | # documents in the target directory are treated as applications and
243 | # run by the server when requested rather than as documents sent to the
244 | # client. The same rules about trailing "/" apply to ScriptAlias
245 | # directives as to Alias.
246 | #
247 | ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"
248 |
249 |
250 |
251 | #
252 | # "/var/www/cgi-bin" should be changed to whatever your ScriptAliased
253 | # CGI directory exists, if you have that configured.
254 | #
255 |
256 | AllowOverride None
257 | Options None
258 | Require all granted
259 |
260 |
261 |
262 | #
263 | # TypesConfig points to the file containing the list of mappings from
264 | # filename extension to MIME-type.
265 | #
266 | TypesConfig /etc/mime.types
267 |
268 | #
269 | # AddType allows you to add to or override the MIME configuration
270 | # file specified in TypesConfig for specific file types.
271 | #
272 | #AddType application/x-gzip .tgz
273 | #
274 | # AddEncoding allows you to have certain browsers uncompress
275 | # information on the fly. Note: Not all browsers support this.
276 | #
277 | #AddEncoding x-compress .Z
278 | #AddEncoding x-gzip .gz .tgz
279 | #
280 | # If the AddEncoding directives above are commented-out, then you
281 | # probably should define those extensions to indicate media types:
282 | #
283 | AddType application/x-compress .Z
284 | AddType application/x-gzip .gz .tgz
285 |
286 | #
287 | # AddHandler allows you to map certain file extensions to "handlers":
288 | # actions unrelated to filetype. These can be either built into the server
289 | # or added with the Action directive (see below)
290 | #
291 | # To use CGI scripts outside of ScriptAliased directories:
292 | # (You will also need to add "ExecCGI" to the "Options" directive.)
293 | #
294 | #AddHandler cgi-script .cgi
295 |
296 | # For type maps (negotiated resources):
297 | #AddHandler type-map var
298 |
299 | #
300 | # Filters allow you to process content before it is sent to the client.
301 | #
302 | # To parse .shtml files for server-side includes (SSI):
303 | # (You will also need to add "Includes" to the "Options" directive.)
304 | #
305 | AddType text/html .shtml
306 | AddOutputFilter INCLUDES .shtml
307 |
308 |
309 | #
310 | # Specify a default charset for all content served; this enables
311 | # interpretation of all content as UTF-8 by default. To use the
312 | # default browser choice (ISO-8859-1), or to allow the META tags
313 | # in HTML content to override this choice, comment out this
314 | # directive:
315 | #
316 | AddDefaultCharset UTF-8
317 |
318 |
319 | #
320 | # The mod_mime_magic module allows the server to use various hints from the
321 | # contents of the file itself to determine its type. The MIMEMagicFile
322 | # directive tells the module where the hint definitions are located.
323 | #
324 | MIMEMagicFile conf/magic
325 |
326 |
327 | #
328 | # Customizable error responses come in three flavors:
329 | # 1) plain text 2) local redirects 3) external redirects
330 | #
331 | # Some examples:
332 | #ErrorDocument 500 "The server made a boo boo."
333 | #ErrorDocument 404 /missing.html
334 | #ErrorDocument 404 "/cgi-bin/missing_handler.pl"
335 | #ErrorDocument 402 http://www.example.com/subscription_info.html
336 | #
337 |
338 | #
339 | # EnableMMAP and EnableSendfile: On systems that support it,
340 | # memory-mapping or the sendfile syscall may be used to deliver
341 | # files. This usually improves server performance, but must
342 | # be turned off when serving from networked-mounted
343 | # filesystems or if support for these functions is otherwise
344 | # broken on your system.
345 | # Defaults if commented: EnableMMAP On, EnableSendfile Off
346 | #
347 | #EnableMMAP off
348 | EnableSendfile on
349 |
350 | # Supplemental configuration
351 | #
352 | # Load config files in the "/etc/httpd/conf.d" directory, if any.
353 | IncludeOptional conf.d/*.conf
354 |
--------------------------------------------------------------------------------
/playbooks/lab10/vars/webservers.yml:
--------------------------------------------------------------------------------
1 | web_packages: httpd
2 | web_service: httpd
3 | web_config_file: /etc/httpd/conf/httpd.conf
4 | firewall_service: http
5 |
--------------------------------------------------------------------------------
/playbooks/lab4/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | inventory = lab-inventory
3 | remote_user = ansible
4 | host_key_checking = false
5 |
6 | [privilege_escalation]
7 | become = True
8 | become_method = sudo
9 | become_user = root
10 | become_ask_pass = False
11 |
12 |
--------------------------------------------------------------------------------
/playbooks/lab4/inventory:
--------------------------------------------------------------------------------
1 | ansible1.example.com
2 | ansible2.example.com
3 | ubuntu.example.com
4 |
--------------------------------------------------------------------------------
/playbooks/lab4/tasks/redhat.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: manage firewalld firewall
3 | firewalld:
4 | service: "{{ item }}"
5 | permanent: true
6 | immediate: true
7 | state: enabled
8 |
9 | with_items:
10 | - httpd
11 | - vsftpd
12 | - mariadb
13 |
14 |
15 |
--------------------------------------------------------------------------------
/playbooks/lab4/tasks/ubuntu.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: manage ufw firewall
3 | ufw:
4 | port: "{{ item }}"
5 | rule: allow
6 |
7 | with_items:
8 | - ssh
9 | - ftp
10 | - http
11 |
12 |
13 |
--------------------------------------------------------------------------------
/playbooks/lab4/variables.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # note: this solution is incomplete but gives a good impression
3 | # of where to go
4 | - name: deploy and start Apache
5 | hosts: all
6 | include_vars: vars/{{ ansible_os_family }}.yml
7 |
8 | tasks:
9 | - name: install and update latest packages
10 | package:
11 | name:
12 | - "{{ web_package }}"
13 | - "{{ firewall_package }}"
14 | - "{{ ftp_package }}"
15 | - "{{ db_package }}"
16 | state: latest
17 | notify:
18 | - success
19 |
20 |
21 | - name: start and enable {{ firewall_service }}
22 | service:
23 | name: "{{ firewall_service }}"
24 | enabled: true
25 | state: started
26 |
27 | - name: start and enable {{ web_service }}
28 | service:
29 | name: "{{ web_service }}"
30 | enabled: true
31 | state: started
32 |
33 | - include_tasks: ubuntu.yml
34 | when: ansible_os_family == 'Ubuntu'
35 |
36 | - include_tasks: redhat.yml
37 | when: ansible_os_family == 'RedHat'
38 |
39 | handlers:
40 | - name: success
41 | debug:
42 | msg: package installation on {{ inventory_hostname }} successful
43 |
--------------------------------------------------------------------------------
/playbooks/lab4/vars/redhat.yml:
--------------------------------------------------------------------------------
1 | ---
2 | [packages]
3 | web_package: httpd
4 | ftp_package: vsftpd
5 | db_package: mariadb-server
6 | firewall_package: firewalld
7 |
8 | [services]
9 | web_service: httpd
10 | ftp_service: vsftpd
11 | db_service: mariadb
12 | firewall_service: firewalld
13 |
--------------------------------------------------------------------------------
/playbooks/lab4/vars/ubuntu.yml:
--------------------------------------------------------------------------------
1 | ---
2 | [packages]
3 | web_package: apache2
4 | ftp_package: vsftpd
5 | db_package: mysql-server
6 | firewall_package: ufw
7 |
8 | [services]
9 | web_service: apache2
10 | ftp_package: vsftpd
11 | db_package: mysql
12 | firewall_service: ufw
13 |
--------------------------------------------------------------------------------
/playbooks/loops/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | remote_user = ansible
3 | host_key_checking = false
4 | inventory = inventory
5 | log_path = errorlog
6 |
7 | [privilege_escalation]
8 | become = True
9 | become_method = sudo
10 | become_user = root
11 | become_ask_pass = False
12 |
13 |
--------------------------------------------------------------------------------
/playbooks/loops/conditionalrestart.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | tasks:
4 | - name: vsftpd status
5 | command: /usr/bin/systemctl is-active vsftpd
6 | ignore_errors: yes
7 | register: result
8 |
9 | - name: create file in FTP doc root
10 | copy:
11 | src: /etc/hosts
12 | dest: /var/ftp/pub
13 | when: result.rc == 0
14 |
--------------------------------------------------------------------------------
/playbooks/loops/copytxt.retry:
--------------------------------------------------------------------------------
1 | ansible1.example.com
2 | ansible2.example.com
3 | ubuntu.example.com
4 |
--------------------------------------------------------------------------------
/playbooks/loops/copytxt.yml:
--------------------------------------------------------------------------------
1 | - hosts: all
2 | tasks:
3 | - name: use with_output in registered variables
4 | shell: 'find . "*.yml"'
5 | args:
6 | chdir: /home/ansible/
7 | register: with_output
8 |
9 | - shell: "cp {{ item }} /tmp/{{ item }}_bak"
10 | with_items:
11 | - "{{ with_output.stdout_lines }}"
12 |
--------------------------------------------------------------------------------
/playbooks/loops/handlers-lab.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: copy new index.html
3 | hosts: centos
4 | tasks:
5 | - name: install httpd
6 | package:
7 | name: httpd
8 | state: present
9 | - name: start httpd
10 | service:
11 | name: httpd
12 | state: started
13 | - name: copy index.html
14 | copy:
15 | src: /tmp/index.html
16 | dest: /var/www/html/index.html
17 | notify:
18 | - restart_web
19 | handlers:
20 | - name: restart_web
21 | service:
22 | name: httpd
23 | state: restarted
24 |
25 |
--------------------------------------------------------------------------------
/playbooks/loops/handlers.retry:
--------------------------------------------------------------------------------
1 | ansible1.example.com
2 | ansible2.example.com
3 | ansible3.example.com
4 |
--------------------------------------------------------------------------------
/playbooks/loops/handlers.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: copy new index.html
3 | hosts: centos
4 | force_handlers: yes
5 | tasks:
6 | - name: install httpd
7 | yum: name=httpd
8 | - name: copy index.html
9 | copy:
10 | src: /tmp/index.html
11 | dest: /var/www/html/index.html
12 | notify:
13 | - restart_web
14 | - name: copy nothing
15 | copy:
16 | src: /tmp/nothing
17 | dest: /nowhere
18 | handlers:
19 | - name: restart_web
20 | service:
21 | name: httpd
22 | state: restarted
23 |
24 |
--------------------------------------------------------------------------------
/playbooks/loops/ifsize.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: conditionals test
3 | hosts: all
4 | tasks:
5 | - name: install vsftpd if sufficient space on /var/ftp
6 | package:
7 | name: vsftpd
8 | state: latest
9 | with_items: "{{ ansible_mounts }}"
10 | when: item.mount == "/var/ftp" and item.size_available > 100000000
11 |
--------------------------------------------------------------------------------
/playbooks/loops/inventory:
--------------------------------------------------------------------------------
1 | [centos]
2 | ansible1.example.com
3 | ansible2.example.com
4 | ansible3.example.com
5 |
6 | [ubuntu]
7 | ubuntu.example.com
8 |
9 | [webservers]
10 | ansible3.example.com
11 | 192.168.4.50
12 |
--------------------------------------------------------------------------------
/playbooks/loops/register.retry:
--------------------------------------------------------------------------------
1 | 192.168.4.50
2 | ansible3.example.com
3 | ubuntu.example.com
4 |
--------------------------------------------------------------------------------
/playbooks/loops/register.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: registered variables demo 1
3 | hosts: all
4 | tasks:
5 | - name: capture output of the who command
6 | command: who
7 | register: loggedin
8 | - shell: echo "user ansible is logged in"
9 | when: loggedin.stdout.find('ansible')
10 |
--------------------------------------------------------------------------------
/playbooks/loops/register2.retry:
--------------------------------------------------------------------------------
1 | ansible1.example.com
2 | ansible2.example.com
3 | ubuntu.example.com
4 |
--------------------------------------------------------------------------------
/playbooks/loops/register2.yml:
--------------------------------------------------------------------------------
1 | - name: registered variable usage as a loop list
2 | hosts: all
3 | tasks:
4 |
5 | - name: create the backup spooler directory
6 | file:
7 | path: /var/bkspool
8 | state: directory
9 |
10 | - name: retrieve the list of home directories
11 | command: ls /home
12 | register: home_dirs
13 |
14 | - name: add home dirs to the backup spooler
15 | file:
16 | path: /var/bkspool/{{ item }}
17 | src: /home/{{ item }}
18 | state: link
19 | loop: "{{ home_dirs.stdout_lines }}"
20 |
--------------------------------------------------------------------------------
/playbooks/loops/register3.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: show register
3 | hosts: ansible1.example.com
4 | tasks:
5 | - name: get output of id command
6 | command: id -un
7 | register: loggedin
8 | - debug: var=loggedin
9 |
--------------------------------------------------------------------------------
/playbooks/loops/with_nested.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars:
4 | myusers:
5 | - linda
6 | - anna
7 | mygroups:
8 | - students
9 | - profs
10 | tasks:
11 | - name: create groups
12 | group:
13 | name: "{{ item }}"
14 | state: present
15 | with_items: "{{ mygroups }}"
16 | - name: create users with group membership
17 | user:
18 | name: "{{ item[0] }}"
19 | state: present
20 | groups: "{{ item[1] }}"
21 | with_nested:
22 | - "{{ myusers }}"
23 | - "{{ mygroups }}"
24 |
--------------------------------------------------------------------------------
/playbooks/loops/with_nested_cleanup.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: all
3 | vars:
4 | myusers:
5 | - linda
6 | - anna
7 | mygroups:
8 | - students
9 | - profs
10 | tasks:
11 | - name: create groups
12 | group:
13 | name: "{{ item }}"
14 | state: absent
15 | with_items: "{{ mygroups }}"
16 | - name: create users with group membership
17 | user:
18 | name: "{{ item[0] }}"
19 | state: absent
20 | groups: "{{ item[1] }}"
21 | with_nested:
22 | - "{{ myusers }}"
23 | - "{{ mygroups }}"
24 |
--------------------------------------------------------------------------------
/playbooks/newvault/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | remote_user = ansible
3 | host_key_checking = false
4 | inventory = inventory
5 |
6 | [privilege_escalation]
7 | become = True
8 | become_method = sudo
9 | become_user = root
10 | become_ask_pass = False
11 |
12 |
--------------------------------------------------------------------------------
/playbooks/newvault/createusers.retry:
--------------------------------------------------------------------------------
1 | ubuntu.example.com
2 |
--------------------------------------------------------------------------------
/playbooks/newvault/createusers.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: create user accounts on all servers
3 | hosts: all
4 | vars_files:
5 | - vars/secret.yml
6 | tasks:
7 | - name: create users from secret.yml
8 | user:
9 | name: "{{ item.name }}"
10 | password: "{{ item.pw | password_hash('sha512')}}"
11 | with_items: "{{ newusers }}"
12 |
--------------------------------------------------------------------------------
/playbooks/newvault/inventory:
--------------------------------------------------------------------------------
1 | ansible1.example.com
2 | ansible2.example.com
3 | ubuntu.example.com
4 |
--------------------------------------------------------------------------------
/playbooks/newvault/vars/secret.yml:
--------------------------------------------------------------------------------
1 | $ANSIBLE_VAULT;1.1;AES256
2 | 61346664353831373130633135323063343839643634373638363461653161613332306663376464
3 | 3266653835353635396631336333353565616539326362370a326434333230326533653836613035
4 | 32333534653163346266663035333665353235626561313863303734386564393861373764636666
5 | 6532636564626165300a653834663638336366356332353638353730373234643266653765313637
6 | 32333965323536353165393565633363376264656635316564626265353539353766366539646333
7 | 66313732656533346631313463373831376135656634663565633832653462343239653233323162
8 | 63666338373765393235646536616664333430636462666434306231396539653562643637383465
9 | 35323864656139643233
10 |
--------------------------------------------------------------------------------
/playbooks/newvault/vaultpw:
--------------------------------------------------------------------------------
1 | password
2 |
--------------------------------------------------------------------------------
/playbooks/roles-demo/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | remote_user = ansible
3 | host_key_checking = false
4 | inventory = inventory
5 |
6 | [privilege_escalation]
7 | become = True
8 | become_method = sudo
9 | become_user = root
10 | become_ask_pass = False
11 |
12 |
--------------------------------------------------------------------------------
/playbooks/roles-demo/inventory:
--------------------------------------------------------------------------------
1 | ansible1.example.com
2 | ansible2.example.com
3 | ubuntu.example.com
4 |
--------------------------------------------------------------------------------
/playbooks/roles-demo/motd-role.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: use motd role playbook
3 | hosts: ansible2.example.com
4 | user: ansible
5 | become: true
6 |
7 | roles:
8 | - role: motd
9 | system_manager: morgane@example.com
10 |
--------------------------------------------------------------------------------
/playbooks/roles-demo/nginx-role.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: use galaxy nginx role
3 | hosts: ansible2.example.com
4 | user: ansible
5 | become: true
6 |
7 | roles:
8 | - role: geerlingguy.nginx
9 |
--------------------------------------------------------------------------------
/playbooks/roles-demo/roles/motd/README.md:
--------------------------------------------------------------------------------
1 | Role Name
2 | =========
3 |
4 | A brief description of the role goes here.
5 |
6 | Requirements
7 | ------------
8 |
9 | Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
10 |
11 | Role Variables
12 | --------------
13 |
14 | A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
15 |
16 | Dependencies
17 | ------------
18 |
19 | A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
20 |
21 | Example Playbook
22 | ----------------
23 |
24 | Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
25 |
26 | - hosts: servers
27 | roles:
28 | - { role: username.rolename, x: 42 }
29 |
30 | License
31 | -------
32 |
33 | BSD
34 |
35 | Author Information
36 | ------------------
37 |
38 | An optional section for the role authors to include contact information, or a website (HTML is not allowed).
39 |
--------------------------------------------------------------------------------
/playbooks/roles-demo/roles/motd/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # defaults file for motd
3 | system_manager: anna@example.com
4 |
--------------------------------------------------------------------------------
/playbooks/roles-demo/roles/motd/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # handlers file for motd
--------------------------------------------------------------------------------
/playbooks/roles-demo/roles/motd/meta/main.yml:
--------------------------------------------------------------------------------
1 | galaxy_info:
2 | author: your name
3 | description: your description
4 | company: your company (optional)
5 |
6 | # If the issue tracker for your role is not on github, uncomment the
7 | # next line and provide a value
8 | # issue_tracker_url: http://example.com/issue/tracker
9 |
10 | # Some suggested licenses:
11 | # - BSD (default)
12 | # - MIT
13 | # - GPLv2
14 | # - GPLv3
15 | # - Apache
16 | # - CC-BY
17 | license: license (GPLv2, CC-BY, etc)
18 |
19 | min_ansible_version: 1.2
20 |
21 | # If this a Container Enabled role, provide the minimum Ansible Container version.
22 | # min_ansible_container_version:
23 |
24 | # Optionally specify the branch Galaxy will use when accessing the GitHub
25 | # repo for this role. During role install, if no tags are available,
26 | # Galaxy will use this branch. During import Galaxy will access files on
27 | # this branch. If Travis integration is configured, only notifications for this
28 | # branch will be accepted. Otherwise, in all cases, the repo's default branch
29 | # (usually master) will be used.
30 | #github_branch:
31 |
32 | #
33 | # platforms is a list of platforms, and each platform has a name and a list of versions.
34 | #
35 | # platforms:
36 | # - name: Fedora
37 | # versions:
38 | # - all
39 | # - 25
40 | # - name: SomePlatform
41 | # versions:
42 | # - all
43 | # - 1.0
44 | # - 7
45 | # - 99.99
46 |
47 | galaxy_tags: []
48 | # List tags for your role here, one per line. A tag is a keyword that describes
49 | # and categorizes the role. Users find roles by searching for tags. Be sure to
50 | # remove the '[]' above, if you add tags to this list.
51 | #
52 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters.
53 | # Maximum 20 tags per role.
54 |
55 | dependencies: []
56 | # List your role dependencies here, one per line. Be sure to remove the '[]' above,
57 | # if you add dependencies to this list.
--------------------------------------------------------------------------------
/playbooks/roles-demo/roles/motd/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # tasks file for motd
3 | - name: copy motd file
4 | template:
5 | src: templates/motd.j2
6 | dest: /etc/motd
7 | owner: root
8 | group: root
9 | mode: 0444
10 |
--------------------------------------------------------------------------------
/playbooks/roles-demo/roles/motd/templates/motd.j2:
--------------------------------------------------------------------------------
1 | Welcome to {{ ansible_hostname }}
2 |
3 | This file was created on {{ ansible_date_time.date }}
4 | Go away if you have no business being here
5 |
6 | Contact {{ system_manager }} if anything is wrong
7 |
--------------------------------------------------------------------------------
/playbooks/roles-demo/roles/motd/tests/inventory:
--------------------------------------------------------------------------------
1 | localhost
2 |
3 |
--------------------------------------------------------------------------------
/playbooks/roles-demo/roles/motd/tests/test.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - hosts: localhost
3 | remote_user: root
4 | roles:
5 | - motd
--------------------------------------------------------------------------------
/playbooks/roles-demo/roles/motd/vars/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # vars file for motd
--------------------------------------------------------------------------------
/playbooks/tags/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | inventory = inventory
3 | remote_user = ansible
4 | host_key_checking = false
5 |
6 | [privilege_escalation]
7 | become = True
8 | become_method = sudo
9 | become_user = root
10 | become_ask_pass = False
11 |
12 |
--------------------------------------------------------------------------------
/playbooks/tags/install.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: tag example
3 | hosts: centos
4 | tasks:
5 | - name: install net analysis packages
6 | package:
7 | name: "{{ item }}"
8 | state: installed
9 | with_items:
10 | - nmap
11 | - wireshark
12 | tags:
13 | - net_analysis
14 | - name: install lamp packages
15 | package:
16 | name: "{{ item }}"
17 | state: installed
18 | with_items:
19 | - mariadb-server
20 | - httpd
21 | tags:
22 | - lamp
23 |
24 |
--------------------------------------------------------------------------------
/playbooks/tags/inventory:
--------------------------------------------------------------------------------
1 | [centos]
2 | ansible1.example.com
3 | ansible2.example.com
4 |
5 | [lamp]
6 | ansible1.example.com
7 |
8 | [file]
9 | ansible2.example.com
10 |
--------------------------------------------------------------------------------
/playbooks/tags/tag_include.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: tags with include demo
3 | hosts: centos
4 | - include: install.yml
5 | tags:
6 | - install_all
7 |
--------------------------------------------------------------------------------
/playbooks/vault/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | remote_user = ansible
3 | host_key_checking = false
4 | inventory = inventory
5 |
6 | [privilege_escalation]
7 | become = True
8 | become_method = sudo
9 | become_user = root
10 | become_ask_pass = False
11 |
12 |
--------------------------------------------------------------------------------
/playbooks/vault/inventory:
--------------------------------------------------------------------------------
1 | ansible1.example.com
2 | ansible2.example.com
3 | ubuntu.example.com
4 |
--------------------------------------------------------------------------------
/playbooks/vault/vault-pw:
--------------------------------------------------------------------------------
1 | $ANSIBLE_VAULT;1.1;AES256
2 | 35323634653366356133376162636439326239636532613861633334663538336233623032613263
3 | 6161373361636137343265363530613663333464336434330a336661366364386134636263353034
4 | 62326162626233386231393962356265376439323839396532393063326330316533323435353734
5 | 3930376466653932360a646134616664636432323066633335356435373638303961386235383964
6 | 6635
7 |
--------------------------------------------------------------------------------
/playbooks/windows/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | remote_user = ansible
3 | host_key_checking = false
4 | inventory = inventory
5 |
6 | [privilege_escalation]
7 |
8 |
--------------------------------------------------------------------------------
/playbooks/windows/inventory:
--------------------------------------------------------------------------------
1 | [lamp]
2 | ansible1.example.com
3 |
4 | [file]
5 | ansible2.example.com
6 |
7 | [win]
8 | windows.example.com
9 |
10 | [win:vars]
11 | ansible_user=ansible
12 | ansible_password=@nsible123
13 | ansible_connection=winrm
14 | ansible_winrm_server_cert_validation=ignore
15 |
--------------------------------------------------------------------------------
/playbooks/windows/playbook.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: create a windows user
3 | hosts: win
4 | tasks:
5 | - name: create windows users
6 | win_user:
7 | name: linda
8 | password: "@nsible123"
9 | state: present
10 | groups:
11 | - Users
12 |
--------------------------------------------------------------------------------