############################################################################### ## Lucian Maly
2018-12-04 ############################################################################### ## Ansible works best if the less access is configured on all nodes: $ ssh-keygen $ ssh-copy-id lmaly@
$ ssh lmaly@
## Configuration, Inventory default(s): $ cat /etc/ansible/hosts $ grep host_file ansible.cfg Priority in which the config files are processed: 1) ANSIBLE_CONFIG (an environment variable) 2) ./ansible.cfg (in the current directory) 3) ~/.ansible.cfg 4) /etc/ansible/ansible.cfg $ ansible --version <-shows what config file is currently being used Commonly Modified Settings in 'ansible.cfg': inventory= Location of Ansible inventory file remote = Remote used to establish connections to managed hosts become= Enables/disables privilege escalation for operations on managed hosts (NOT BY DEFAULT!) become_method= Defines privilege escalation method become_= for escalating privileges become_ask_= Defines whether privilege escalation prompts for # Default module (command, NOT SHELL!) is defined in 'ansible.cfg' under 'defaults' section: $ grep module_name /etc/ansible/ansible.cfg !If no modules defined, predefined command module used ############################################################################### ## Run the playbook(s): $ ansible -i localhost,
.yaml !When using the '-i' parameter, if the value is a 'list' (it contains at least one comma) it will be used as the inventory LIST !It can be either FQDN or IP address $ ansible -i /etc/ansible/hosts
.yaml !While the variable is a string, it will be used as the inventory PATH ## Ansible verbosity: $ ansible-playbook -v -i
.yaml $ ansible-playbook -vv -i
.yaml $ ansible-playbook -vvv -i
.yaml connections $ ansible-playbook -vvvv -i
.yaml
<-displays output data <-displays output & input data <-information about <-information about plugins
############################################################################### ## Inventory file examples: [webserver] <-both hosts can be reduced to ws[01:02].lmaly.io using regexp ws01.lmaly.io ws02.lmaly.io:1234 <-configured to listen on port 1234 [database] db01.lmaly.io 192.168.[1:5].[0:255]
<-includes 192.168.1.0/24 through 192.168.5.0/24
[nsw_cities:children] <-nested groups use the keyword :children webserver database others [others] localhost ansible_connection=local connection, without SSH 192.168.3.7 ansible_connection=ssh ansible_=ftaylor settings example
<-it will use local <-use different
# Dynamic inventory - AWS custom script !Script must '--list' an '--host' parameters, e.g.: $ ./inventiryscript --list $ sudo ec2.py /etc/ansible/ $ sudo ec2.ini /etc/ansible/ $ sudo chmod +x /etc/ansible/ec2.py $ ansible -i ec2.py all -m ping !Multiple inventory files in one dir = All the files in the directory are merged and treated as one inventory ############################################################################### ## Which hosts will the playbook run against? $ ansible-playbook
.yaml --list-hosts ## Which hosts are in the hosts file? $ ansible all --list-hosts $ ansible '192.168.2.*' -i <myinventory> --list-hosts everything that matches the wildcard $ ansible lab:datacenter1 -i <myinventory> --list-hosts of lab OR datacenter1 $ ansible 'lab:&datacenter1' -i <myinventory> --list-hosts of lab AND datacenter1 $ ansible 'datacenter:!test2.example.com' -i <myinventory> --list-hosts host
<<- <- <-exclude
## Print all the tasks in short form that playbook would perform: $ ansible-playbook
.yaml --list-tasks ############################################################################### ## See the metadata obtained during Ansible setup (module setup): $ ansible all -i
, -m setup $ ansible
-m setup $ ansible
-m setup --tree facts # Show the specific metadata: $ ansible
-m setup -a 'filter=*ipv4*' # Examples: {{ ansible_hostname }} {{ ansible_default_ipv4.address }} {{ ansible_devices.vda.partitions.vda1.size }} {{ ansible_dns.nameservers }} {{ ansible_kernel }} # To disable facts: gather_facts: no # Custom facts are saved under ansible_local: $ cat /etc/ansible/facts.d/new.fact <-ini or json file $ ansible demo1.example.com -m setup -a 'filter=ansible_local'
# Accessing facts of another node: {{ hostvars['demo2.example.com']['ansible_os_family'] }} ############################################################################### # Run arbitrary command on all hosts as sudo: $ ansible all -s -a "cat /var/log/messages" # Run Ad Hoc module with arguments: $ ansible host pattern -m module -a 'argument1 argument2' [-i inventory] # Run ad hoc command that generates one line input for each operation: $ ansible myhosts -m command -a /usr/bin/hostname -o # Ad hoc Example: yum module checks if httpd installed on demohost: $ ansible demohost -u devops -b -m yum -a 'name=httpd state=present' # Ad hoc Example: Find available space on disks configured on demohost: $ ansible demohost -a "df -h" # Ad hoc Example: Find available free memory on demohost: $ ansible demohost -a "free -m" ############################################################################### ## Playbook(s) basics: --<-indicates YAML, optional document marker - hosts: all <-host or host group against we will run the task (example - hosts: webserver) remote_: lmaly <-as what will Ansible to the machine(s) tasks: <-list of actions - name: Whatever you want to name it <-name of the first task yum: name: httpd state: present <-same as single line 'yum: name=httpd state=present become=True' become: True <-task will be executed with sudo ... <-optional document marker indicating end of YAML Notes: !Space characters used for indentation !Pipe (|) preservers line returns within string !Arrow (>) converts line returns to spaces, removes leading space in lines !Indentation rules: Elements at same level in hierarchy must have same indentation Child elements must be indented further than parents No rules about exact number of spaces to use !Optional: Insert blank lines for readability !Use YAML Lint http://yamllint.com to check the syntax correctness !Use 'ansible-playbook --syntax-check <MYAML.YML>' !Use 'ansible-playbook -C <MYAML.YML>' for dry run (what changes would occur if playbook is executed?) !Use 'always_run: true' to execute only some tasks in check mode (or 'always_run: false' for the opposite) !Use 'ansible-playbook --step <MYAML.YML>' to prompt each task (y=yes/n=no/c=exit and execute remaining without asking) !Use 'ansible-playbook <MYAML.YML> --start-at-task="start httpd service" to start execution from given task
!For complex playbooks, use 'include' to include separate files in main playbook (include: tasks/env.yml) ############################################################################### ## Variables: --- # Usually comment field describing what it does - hosts: all remote_: lmaly tasks: - name: Set variable 'name' set_fact: name: Test machine - name: Print variable 'name' debug: msg: '{{ name }}' a/ In vars block: vars: set_fact: 'Test machine' b/ ed as arguments: include_vars: vars/extra_args.yml # Other alternative ways: (1) variable(s) in the CLI: $ ansible-playbook -i
,
.yaml
-e 'name=test01'
(2) variable(s) to an inventory file: a/ [webserver] <- all playbooks running on webservers will be able to refer to the domain name variable ws01.lmaly.io domainname=example1.lmaly.io ws02.lmaly.io domainname=example2.lmaly.io b/ [webserver:vars] <- ! Host variables will override group variables in case tge same variable is used in both https_enabled=True (3) in variable file(s): a/ host_vars: $ cat host_vars/ws01.lmaly.io domainname=example1.lmaly.io b/ group_vars: $ cat group_vars/webserver https_enabled=True !Variable must start with letter; valid characters are: letters, numbers, underscores # Array definition example: s: bjones: first_name: Bob last_name: Jones acook: first_name: Anne last_name: Cook # Array accessing/reading example:
s.bjones.first_name s['bjones']['first_name']
# Returns 'Bob' # Returns 'Bob'
<-preferable method
# Inventory variables hierarchy/scope !Three levels: Global, Play, Host !If Ansible finds variables with the same name, it uses chain of preference (highest priority on the top): ^ A) Common variable file - overrides everything, host/group/inventory variable files | 01) 'extra' variables via CLI (-e) | 02) Task variables (only for task itself) | 03) Defined in 'block' statement (- block:) | 04) 'role' and 'include' variables | 05) Included using 'vars_files' (vars_files: - /vars/env.yml) | 06) 'vars_prompt' variables (vars_prompt:) | 07) Defined with '-a' or '--args' command line | 08) Defined via 'set_facts' (- set_fact: : joe) | 09) ed variables with '' keyword for debugging | 10) Host facts discovered by Ansible (ansible_facts) | B) Host variables - overrides group variable files | 11) 'host_vars' in host_vars directory | C) Group variables - overrides inventory variable files | 12) 'group_vars' in group_vars directory | D) Inventory variable files - lowest priority | 13) 'host_vars' in the inventory file ([hostgroup:vars]) | 14) 'group_vars' in the inventory file ([hostgroup:children]) | 15) Inventory file variables - global | 16) Role default variables - Set in roles vars directory !Variables ed to the CLI have priority over any other variable. !You can override the following parameters from an inventory file: ansible_, ansible_port, ansible_host, ansible_connection, ansible_private_key_file, ansible_shell_type ############################################################################### ## Iterates (http://docs.ansible.com/ansible/playbooks_loops.html) (1) Standard - 'with_items' example: a/ Simple loop firewall firewalld: service: '{{ item }}' state: enabled permanent: True immediate: True become: True with_items: - http - https b/ List of hashes - : name: {{ item.name }} state: present groups: {{ item.groups }} with_items: - { name: 'jane', groups: 'wheel' } - { name: 'joe', groups: 'root' } (2) Nested loops (iterate all elements of a list with all items from other
lists) - 'with_nested' example: : name: '{{ item }}' become: True with_items: - '{{ s }}' file: path: '/home/{{ item.0 }}/{{ item.1 }}' state: directory become: True with_nested: - '{{ s }}' - '{{ folders }}' (3) Fileglobs loop (action on every file present in a certain folder) 'with_fileglobs' example: copy: src: '{{ item }}' dest: '/tmp/iproute2' remote_src: True become: True with_fileglob: - '/etc/iproute2/rt_*' (4) Integer loop (iterate over the integer numbers) - 'with_sequence' example: file: dest: '/tmp/dir{{ item }}' state: directory with_sequence: start=1 end=10 become: True Notes: with_file - Takes list of file names; 'item' set to content of each file in sequence with_fileglob - Takes file name globbing pattern; 'item' set to each file in directory that matches pattern; in sequence, nonrecursively with_sequence - Generates sequence of items in increasing numerical order; Can take 'start' and 'end' arguments - s decimal, octal, hexadecimal integer values with_random_choices - Takes list; 'item' set to one list item at random Note - Conditionals: Equal Less than Greater than Less than or equal Greater than or equal Not equal Variable exists Variable does not exist Variable set to 1, True, or yes Variable set to 0, False, or no Value present in variable or array AND inventory_hostname in groups['staging'] OR ansible_distribution == "Fedora" (5) 'when' statement example
"{{ max_memory }} == 512" "{{ min_memory }} < 128" "{{ min_memory }} > 256" "{{ min_memory }} <= 256" "{{ min_memory }} >= 512" "{{ min_memory }} != 512" "{{ min_memory }} is defined" "{{ min_memory }} is not defined" "{{ available_memory }}" "not {{ available_memory }}" "{{ s }} in s["db_s"]" ansible_kernel == 3.10.el7.x86_64 and ansible_distribution == "RedHat" or
- name: Create the database : name: db_ when: inventory_hostname in groups["databases"] variable, it must be placed outside of the module
<-when is not a module
(6) When combining 'when' with 'with_items', the when statement is processed for each item: with_items: "{ ansible_mounts }" when: item.mount == "/" and item.size_available > 300000000 ############################################################################### ## - To capture output of module that uses array - shell: echo "{{ item }}" with_items: - one - two : echo ############################################################################### ## Handlers - Task that responds to notification triggered by other tasks: notify: - restart_apache handlers: -name: restart_apache service: name: httpd: state: restarted !Task may call more than one handler in notify !Ansible treats notify as an array and iterates over handler names !Handlers run in order they are written in play, not in order listed by notify task !If two handlers have same name, only one runs !Handler runs only once, even if notified by more than one task !Handlers defined inside 'include' cannot be notified !--force-handlers ############################################################################### ## Resource tagging a/ Tasks tags: - packages b/ Roles - all tasks they define also tagged roles: - { role: , tags: [production] } c/ Include - all tasks they define also tagged - include: common.yml tags: [testing] $ ansible-playbook --tags "production" $ ansible-playbook --skip-tags "development" Special tags: always, tagged, untagged, all (default) ############################################################################### ## Ignore and task errors Default: If task fails, play is aborted immediatelly, same with handlers To skip failed tasks, use 'ignore_errors: yes' To force handler to be called even if task fails, use 'force_handlers: yes' To mark task as failed even when it succeeds, use 'failed_when: cmd: /usr/local/bin/create_.show
: command_result failed_when: "' missing' in command_result.stdout" Default: Task acquires 'changed' state after updating managed host, which causes handlers skipped if task does not make changed changed_when: "'Success' in command_result.stdout" ############################################################################### ## Environmental variables a/ Set env: environment: http_proxy: http://demo.lab.example.com:8080 b/ Reference vars inside playbook: - block: environment: name: "{{ myname }}" ############################################################################### ## Blocks - logical grouping of tasks, controls how are they executed a/ block: <-main tasks to run b/ rescue: <-tasks to run if block tasks fail c/ always: <-tasks to always run, independent on a/ or b/ tasks: - block: - shell: cmd: /usr/local/lib/upgrade-database rescue: - shell: cmd: /usr/local/lib/create- always: - service: name: mariadb state: restarted ############################################################################### ## Delegation using 'delegate_to' and 'local_action' a/ To localhost using delegate_to: - name: Running Local Process command: ps delegate_to: localhost : local_process b/ To localhost using local_action: - name: Running Local Process local_action: command 'ps' : local_process c/ Host outside play in inventory - example of removing managed hosts from ELB, upgrading and the adding them back: - hosts: webservers tasks: - name: Remove server from load balancer command: remove-from-lb {{ inventory_hostname }} delegate_to: localhost - name: Deploy the latest version of Webstack git:repo=git://git.example.com/path/repo.git dest=/srv/www - name: Add server back to ELB pool
command: add-to-lb {{ inventory_hostname }} delegate_to: localhost Inventory data used when creating connection to target: ansible_connection ansible_host ansible_port ansible_ d/ Host outside play NOT in inventory using 'add_host': - name: Add delegation host add_host: name=demo ansible_host=172.25.250.10 ansible_=devops - name: Silly echo command: echo {{ inventory_hostname }} delegate_to: demo args: <-complex arguments can be added chdir: somedir/ creates: /path/to/database e/ 'run_once: True' !Facts gathered by delegated tasks assigned to 'delegate_to' host, not host that produced facts !To gather facts from delegated host, set 'delegate_facts: True' in the play ############################################################################### ## Jinja2 Template(s) - no need for file extension: --- hosts: all remote_: lmaly tasks: - name: Ensure the website is present and updated template: src: index.html.j2 <-the J2 template source dest: /var/www/html/index.html <-destination owner: root group: root mode: 0644 become: True # Variables in J2 template(s): '{{ VARIABLE_NAME }}' '{{ ARRAY_NAME['KEY'] }}' '{{ OBJECT_NAME.PROPERTY_NAME }}' # Example of index.html.j2 using variable: # {{ ansible_managed }} <-recommended to include it in each template
Hello World! 5u133q
This page was created on {{ ansible_date_time.date }}.