====== Ansible ====== **General Information** Ansible is a tool for performing remote tasks on systems. (either ad-hoc or via "playbooks") You will need to use ansible as a user that has ssh keys setup on systems so you are not prompted for passwords during parallel command operations. \\ If you already have Ansible installed/configured, [[linux_wiki:ansible_playbook_downloads|playbook downloads can be found here]]. **Checklist** * Distro(s): Any ---- ====== Install ====== Ansible comes in many distributions repos already. CentOS 7 yum install ansible ---- ====== Configure ====== Main Ansible config file * /etc/ansible/ansible.cfg * Edit this file to point Ansible to your inventory files. ===== Repo Controlled ===== It is highly recommended to put Ansible playbooks, roles, and inventory into version control. (Example: git or svn) One suggested directory structure/workflow is: * Shared, read-only/production use at: * /ansible/ * Each system administrator would then clone a copy of the repo into their home directory for local changes/testing before committing working modifications to the repo. * Have an automated job sync the shared location every so often. * Example: Have cron perform a git pull for /ansible/ every 30 mins. \\ Model content tree under /ansible/ after [[http://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html#alternative-directory-layout|Ansible's best practices alternative structure]]. ---- ===== Inventory ===== Inventory setup is the most important configure task. \\ In order for Ansible to send commands to systems, they must be listed in inventory. \\ Suggested directory structure (after [[http://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html#alternative-directory-layout|Ansible's best practices alternative structure]]) * Production Inventory: /ansible/inventories/production/hosts * Systest Inventory: /ansible/inventories/systest/hosts * Development Inventory: /ansible/inventories/development/hosts ==== Auto Generated Inventory ==== You will not want to introduce a manual inventory process. Suggested workflow for auto generating your inventory files: * Cron Job to execute inventory generation script: /etc/cron.d/ansible-generate-inventory * Example: Executes every 30 minutes. (*/30 * * * *) * Script that generates inventory files: /ansible/scripts/inventory-file-gen.py * Source: The script should contact some sort of system via APIs that all newly deployed systems automatically register to. (Such as Spacewalk, Foreman/Katello, etc) * Target directory for auto generated inventory files: /root/repos/ansible/inventories/ * Files are generated here and any changes committed to the repo (svn/git). * Example: If there were committed changes, a repo sync job will pick them up and place them in /ansible/inventories/ at the top of every hour. (0 *) * Example User Accounts Involved * autorepo -> Repo user account with read/write access to an ansible repo for inventory file changes. * autoadmin -> API user account for contacting the inventory source. ---- ====== Ansible Commands ====== Examples of Ansible commands. **Note: All host-patterns check the inventory files for matches ONLY. If a system is not in the inventory file(s), Ansible can't send a command to it.** General Ansible command syntaxansible [-m module-name] [-a arguments] [options] * see 'man ansible' for more details ---- ===== List Hosts ===== Listing hosts matched by the given pattern and do nothing else. \\ List hosts in the webservers_nginx group from dev inventoryansible webservers_nginx -i /ansible/inventories/development/hosts --list-hosts \\ List all hosts from dev inventoryansible all -i /ansible/inventories/development/hosts --list-hosts \\ List all hosts in the "webservers_nginx" group (**from any inventory**)ansible webservers_nginx --list-hosts \\ List all hosts matching "web*" pattern (**from any inventory**)ansible "web*" --list-hosts ---- ===== Test Connection ===== The module 'ping' is **not an ICMP echo command**; it tests a remote login (ssh) to the system and verifies a working remote python environment. \\ Test connection to webservers_nginx in dev inventory onlyansible webservers_nginx -i /ansible/inventories/development/hosts -m ping \\ Test connection to all systems in dev inventoryansible all -i /ansible/inventories/development/hosts -m ping \\ Test connection to **all** webservers_nginx (**from any inventory**)ansible webservers_nginx -m ping \\ Test connection to all systems in the "webservers_nginx" group (**from any inventory**)ansible webservers_nginx -m ping \\ Test connection to systems matching a pattern (**from any inventory**)ansible "web*" -m ping \\ Test connection to all systems (**from any inventory**)ansible all -m ping ---- ===== Ad-Hoc Commands ===== Ad-hoc commands are for one off commands that are not saved in a playbook. * Ad-hoc Intro: http://docs.ansible.com/ansible/intro_adhoc.html * Module Index: http://docs.ansible.com/ansible/latest/modules_by_category.html ---- ==== Modules: Command and Shell ==== If no module is specified, the "command" module is used. * The command module can execute basic commands, **but cannot do shell specific functions** such as piping and redirects * If you need that type of shell functionality, **specify the "shell" module** \\ Check uptime of the group 'webservers_nginx' from dev onlyansible webservers_nginx -i /ansible/inventories/development/hosts -a 'uptime' \\ Check uptime of all systems in devansible all -i /ansible/inventories/development/hosts -a 'uptime' \\ Check uptime of the group 'webservers_nginx' (**from any inventory**)ansible webservers_nginx -a 'uptime' \\ Check the last 10 lines in /var/log/messages (using sudo)ansible "web*" -a 'tail /var/log/messages' -b * -b => or 'become', uses the default privilege escalation, which is sudo on Linux systems. Equivalent command as the aboveansible "web*" -a 'sudo tail /var/log/messages' \\ Piping example; the shell module must be specifiedansible "web*" -m shell -a 'cat /var/log/messages | grep -i error' -b ---- ==== Modules: Copy and File ==== Transferring files over scp using Ansible to multiple servers at once. \\ Copy a local file (src=) to all systems in the 'webservers_nginx' group (dest=)ansible webservers_nginx -m copy -a "src=/etc/yum.conf dest=/tmp/yum.conf" \\ Set a file's ownership and permissionsansible webservers_nginx -m file -a "dest=/tmp/yum.conf mode=750 owner=root group=root" \\ Remove a fileansible webservers_nginx -m file -a "dest=/tmp/yum.conf state=absent" * This same command will recursively delete if a directory is the target ---- ==== Modules: Packages ==== Managing packages with Ansible. \\ Ensure a package is installed, do not update the package if it is installedansible my_group -m yum -a "name=dstat state=present" \\ Ensure package is the latest version (update if not)ansible my_group -m yum -a "name=dstat state=latest" \\ Ensure package is not installed (remove if it is)ansible my_group -m yum -a "name=dstat state=absent" ---- ==== Modules: Services ==== Managing system services with Ansible. Compatible with SysV and Systemd. * **Note**: For systemd specific commands such as "mask", you will need to use the [[http://docs.ansible.com/ansible/latest/systemd_module.html|systemd Ansible module]] instead. \\ Ensure httpd is started (start if not)ansible my_group -m service -a "name=httpd state=started" \\ Restart a serviceansible my_group -m service -a "name=httpd state=restarted" \\ Ensure a service is stoppedansible my_group -m service -a "name=httpd state=stopped" ---- ====== Ansible Playbooks ====== "Playbooks are Ansible’s configuration, deployment, and orchestration language. They can describe a policy you want your remote systems to enforce, or a set of steps in a general IT process." More Details: http://docs.ansible.com/ansible/playbooks.html ---- ===== Playbook Directory Structure ===== Playbooks should be repo controlled. Following the examples on this page: * /ansible/playbooks \\ **Playbooks map ansible groups to roles** * Example playbook# File: webservers_nginx.yml # Description: Nginx Webservers # Last Updated: 2018-04-08 # Recent Changes:-Initial release # hosts: group_name or 'all' - hosts: webservers_nginx # roles: located in ../roles/ roles: # role: role to assign to hosts, tags: tag(s) to give entire role - { role: webservers-nginx, tags: webservers-nginx } # Gather host facts for this playbook gather_facts: yes * All systems in the ansible group "webservers_nginx" are given the roles: * webservers-nginx * When a playbook is executed, all tasks in the assigned roles are run (unless only specific tasks/actions are selected using tags and/or limits). \\ **Gather a subset of facts** If you do need to gather facts, consider gathering a subset of facts instead of everything in order to keep the fact collection fast. * Example: Collect only the ansible_distribution facts# Gather host facts for this playbook gather_facts: yes # Gather only ansible_distribution info (OS attributes) gather_subset: - '!all' - '!min' - 'distribution' * Facts returned by the above subset"ansible_distribution": "CentOS", "ansible_distribution_file_parsed": true, "ansible_distribution_file_path": "/etc/redhat-release", "ansible_distribution_file_variety": "RedHat", "ansible_distribution_major_version": "7", "ansible_distribution_release": "Core", "ansible_distribution_version": "7.5.1804", "ansible_os_family": "RedHat", * You can test your subset commands like this * ad-hocansible -m setup -a 'gather_subset=!all,!min,distribution' localhost **Available Fact Subsets**: * all * min * hardware * network * virtual * ohai * facter \\ **See the Roles section** for what happens next. ---- ===== Roles Directory Structure ===== Roles contain all of the tasks that will be run when a playbook is executed against it. \\ Roles should be repo controlled. Top level directory example is: * /ansible/roles An example role called "webservers-nginx" is located: * /ansible/roles/webservers-nginx The contents of a role directory are: * files * Files to be used with the 'copy' or 'script' resource. Files located in this directory can be referred to by just the filename without directory path in task yml files. * handlers * Actions that are only executed when certain tasks report changes. Requires 'main.yml' to execute. * meta * Role dependencies. Requires 'main.yml' to execute. * **tasks** -> The main section to work with * Tasks/actions to take when the role is executed via a playbook. Requires 'main.yml' to execute. * templates * Files to be used with the 'template' resource. Templates end in '.j2' (jinja2) * vars * Variables associated with the role. Requires 'main.yml' to execute. ---- ===== Playbook/Role Creation ===== How to create a new playbook and role. * Playbook: Maps Ansible group(s) to role(s) * Role: Defines tasks to complete against the hosts External References * Intro to Playbooks: http://docs.ansible.com/ansible/playbooks_intro.html * Intro to modules: http://docs.ansible.com/ansible/modules_intro.html * All modules index: http://docs.ansible.com/ansible/list_of_all_modules.html - Navigate to your local copy of the version controlled ansible repo. Example:cd ${HOME}/repos/ansible/ - Update your local copy of the version controlled ansible repo to the latest version#git git pull #svn svn update - **New Role** - Navigate to the roles directory, then copy your role template directory to a new namecd roles/ cp -R template-role/ my-new-role - Modify the role's files as needed to create tasks, files, handlers, etc. - Download zip archive of an {{ :linux_wiki:role-template.zip |example role template}}. - **New Playbook** - Navigate to the playbooks directorycd ${HOME}/repos/ansible/playbooks/ - Copy your playbook template to a new playbook yaml file.cp TEMPLATE_PLAYBOOK.yml my_new_playbook.yml - Playbook Template# File: TEMPLATE_PLAYBOOK.yml # Description: PLAYBOOK DESCRIPTION HERE # Last Updated: 2018-03-15 # Recent Changes:-Initial release # hosts: group_name or 'all' - hosts: - group_name_here # roles: located in ../roles/ roles: # role: role to assign to hosts, tags: tag(s) to give entire role - { role: role-name, tags: tag-name } # Do not gather host facts for this playbook (comment out/remove if you need facts) gather_facts: no - Edit the new playbook (vim ansible/playbooks/my_new_playbook.yml) - hosts: my_ansible_group roles: - { role: my-new-role, tags: my-new-role } - In the above example, the playbook "my_new_playbook" states that the group "my_ansible_group" has the role of "my-new-role", and all tasks in the role "my-new-role" has the tag "my-new-role" - The tag can be anything, but keeping it the same as the role name is useful if you want to limit the execution of a playbook to a specific role using the "tags" command line argument to ansible-playbook - Tags are inherited at all tasks below it. - When the playbook is executed, the tasks defined in the role will be executed against the group - **Test Playbook** - See the next section for details on testing the playbook before committing changes to a repo. ---- ==== Playbook Testing ==== Test your playbook before committing it to a repo. * Add these functions to your shell's config#bash = ~/.bashrc #zsh = ~/.zshrc # Enable Ansible test environment ansible_local_enable(){ export ANSIBLE_INVENTORY="${HOME}/repos/ansible/inventories" export ANSIBLE_ROLES_PATH="${HOME}/repos/ansible/roles" echo -e "Ansible environmental variables set to:" env | grep ANSIBLE } # Disable Ansible test environment ansible_local_disable(){ unset ANSIBLE_INVENTORY unset ANSIBLE_ROLES_PATH echo -e "Ansible environmental variables have been unset. There should be no variable results after this line." env | grep ANSIBLE } * Source the file#bash source ~/.bashrc #zsh source ~/.zshrc * Enable local variablesansible_local_enable * Test playbook locally * Syntax checkansible-playbook --syntax-check ${HOME}/repos/ansible/playbooks/my_new_playbook.yml * List tasksansible-playbook -b -i ${HOME}/repos/ansible/inventories/development/dev ${HOME}/repos/ansible/playbooks/my_new_playbook.yml --list-tasks * Run against a test systemansible-playbook -b -i ${HOME}/repos/ansible/inventories/development/dev ${HOME}/repos/ansible/playbooks/my_new_playbook.yml --limit mytestsystem * Tests successful, disable local variablesansible_local_disable * Commit playbook/role to the repo ---- ===== Playbook Commands ===== Different methods to run playbooks. \\ **WARNING**: If you do **NOT** specify an inventory file with "-i PATH", the playbook will run against **ALL HOSTS** in the group, which is probably not what you want. \\ **NOTE**: If you need to use group_vars per inventory type (dev/test/prod), the full path to the inventory file needs to be used. (Example: /ansible/inventories/development/hosts ) ---- ==== Playbook Commands: Syntax Check ==== After creating a playbook, it is useful to check the syntax before running it to catch obvious errors. Run a syntax check (will NOT execute the playbook)ansible-playbook --syntax-check /ansible/playbooks/my_playbook.yml ---- ==== Playbook Commands: Aliases ==== Playbook commands can get rather long, some useful aliases to shorten them. Put in your ~/.bashrc or ~/.zshrc file # Ansible aliases alias apd='ansible-playbook -b -i /ansible/inventories/development/hosts' alias apt='ansible-playbook -b -i /ansible/inventories/systest/hosts' alias app='ansible-playbook -b -i /ansible/inventories/production/hosts' \\ Additionally, create a symlink to playbooks ln -s /ansible/playbooks /playbooks \\ Now, your playbook commands can look like this # Dev inventory apd /playbooks/myplaybook.yml # Test inventory apt /playbooks/myplaybook.yml # Prod inventory app /playbooks/myplaybook.yml * limits, tags, etc can also be appended as normal. ---- ==== Playbook Commands: List ==== Preparation/sanity check commands to do before running a playbook. \\ **List** what hosts the playbook will run against (**from dev inventory**)ansible-playbook -i /ansible/inventories/development/hosts /ansible/playbooks/my_playbook.yml --list-hosts * -i or --inventory -> Path to the inventory (alternative is a comma separated list of hosts or single hostname with a trailing comma) \\ **List** what tasks the playbook will execute (**from dev inventory**)ansible-playbook -i /ansible/inventories/development/hosts /ansible/playbooks/my_playbook.yml --list-tasks ---- ==== Playbook Commands: Run ==== Commands to actually run a playbook. You SHOULD do the list commands first to make sure what you are about to run is expected. * The list commands (--list-hosts and --list-tasks) can be appended to any of the following commands to check them first before running. \\ **Run playbook (dev env; all in group)** against all system groups defined in playbook **from dev inventory** using sudo for privilege escalationansible-playbook -b -i /ansible/inventories/development/hosts /ansible/playbooks/my_playbook.yml * -b or --become -> Use privilege escalation (default of sudo) \\ **Run playbook (dev env; all in group; only configure)** against all groups defined in playbook **from dev inventory** using sudo for privilege escalation, only execute actions with the matched tags "configure"ansible-playbook -b -i /ansible/inventories/development/hosts /ansible/playbooks/my_playbook.yml --tags configure * --tags configure -> Only execute tasks in the playbook that have been tagged "configure" * roles, import_tasks, or individual tasks can be tagged \\ **Run playbook (dev env; range of systems)** against all groups defined in playbook **from dev inventory** using sudo for privilege escalation, further limit to hosts web01-05"ansible-playbook -b -i /ansible/inventories/development/hosts /ansible/playbooks/my_playbook.yml --limit "$(echo web{01..05})" * --limit "$(echo web{01..05})" -> Filter results of host match to only include these systems \\ **Run playbook (dev env; one system)** against all groups defined in playbook **from dev inventory**, limit to a single hostname (MYHOSTNAME)ansible-playbook -i /ansible/inventories/development/hosts /ansible/playbooks/my_playbook.yml --limit MYHOSTNAME ----