Do not reprint advanced playbook without written permission
Finding files with variablesAll modules can take variables as part of their arguments by dereferencing themwith {{ and }} . You can use this to load a particular file based on a variable.For example, you might want to select a different config file for NRPE (a Nagioscheck daemon) based on the architecture in use. Here is how that would look:---#1- name: Configure NRPE for the right architecture#2hosts: ansibletest#3user: root#4tasks:#5- name: Copy in the correct NRPE config file#6copy: src=files/nrpe.{{ ansible_architecture }}.confdest=/etc/nagios/nrpe.cfg#7In the copy and the template modules, you can also configure Ansible to look for aset of files, and it finds them using the first one. This lets you configure a file to lookfor; if that file is not found a second will be used, and so on until the end of the listis reached. If the file is not found, then the module will fail. The feature is triggeredby using the first_available_file key, and referencing {{ item }} in the action.The following code is an example of this feature:---#1- name: Install an Apache config file#2hosts: ansibletest#3user: root#4tasks:#5- name: Get the best match for the machine#6copy: dest=/etc/apache.conf src={{ item }}#7first_available_file:#8- files/apache/{{ ansible_os_family }}-{{ansible_architecture }}.cfg#9- files/apache/default-{{ ansible_architecture }}.cfg- files/apache/default.cfg#11Remember that you can run the setup module from the Ansiblecommand-line tool. This comes in handy when you are making heavyuse of variables in your playbooks or templates. To check what facts willbe available for a particular play, simply copy the value of the host lineand run the following command:ansible [host line] -m setupOn a CentOS x86_64 machine, this configuration would first look for the fileRedHat-x86_64.cfg upon navigating through files/apache/ . If that file did notexist, it would look for file default-x86_64.cfg upon navigating through file/apache/ , and finally if nothing exists, it'll try and use default.cfg .
Search for files using variables
All ansible modules can use the form of {} to use variables as part of their parameters. In this way, you can use variables to load different configurations. For example, if you want to load different nrpe configurations based on different architectures, the Code is as follows:
---
-Name: Configure nrpe for the right architecture
Hosts: ansibletest
User: Root
Tasks:
-Name: copy in the correct nrpe Config File
Copy: src = files/nrpe. {ansible_architecture}. conf
DeST =/etc/Nagios/nrpe. cfg
In the copy and template modules, ansible allows you to search for a file set with your configuration. The first file to be found will be used. If the file cannot be found, you will continue to know the matching information, if the corresponding file is still not found after the loop, the module will not run. Use the first_available_file keyword and {item} in action to complete the above functions. The sample code is as follows:
---
-Name: install an Apache Config File
Hosts: ansibletest
User: Root
Tasks:
-Name: Get the best match for the machine
Copy: DEST =/etc/Apache. conf src = {item }}
First_available_file:
-Files/Apache/{ansible_ OS _family }}-{{
Ansible_architecture }}. cfg
-Files/Apache/default-{ansible_architecture}. cfg
-Files/Apache/Default. cfg
Note: before you use templates and variables in large quantities, you can use the setup module to confirm some fact (for example, the above example is mainly because the configuration file names and formats of Apache are different in different operating systems ).
On the centos x86_64 machine, he first looks for the RedHat-x86_64.cfg, because his architecture is x86_64, if not found, find the default-x86_64.cfg, if not found, use default. cfg.
Environment variablesOften Unix commands take advantage of certain environment variables. Prevalentexamples of this are C makefiles, installers, and the AWS command-line tools.Fortunately, Ansible makes this really easy. If you wanted to upload a file on theremote machine to Amazon S3, you could set the Amazon access key as follows. Youwill also see that we install EPEL so that we can install pip, and pip is used to installthe AWS tools.---#1- name: Upload a remote file via S3#2hosts: ansibletest#3user: root#4tasks:#5- name: Setup EPEL#6command rpm -ivh#7http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpmcreates=/etc/yum.repos.d/epel.repo#8- name: Install pip#9yum: name=python-pip state=installed- name: Install the AWS toolspip: name=awscli state=present#10#11#12- name: Upload the file#13shell: aws s3 put-object --bucket=my-test-bucket --key={{ansible_hostname }}/fstab --body=/etc/fstab --region=eu-west-1#14environment:#15AWS_ACCESS_KEY_ID: XXXXXXXXXXXXXXXXXXX#16AWS_SECRET_ACCESS_KEY: XXXXXXXXXXXXXXXXXXXXX#17Internally, Ansible sets the environment variable into the Python code;this means that any module that already uses environment variablescan take advantage of the ones set here. If you write your own modules,you should consider if certain arguments would be better used asenvironment variables instead of arguments.Some Ansible modules such as get_url , yum , and apt will also use environmentvariables to set their proxy server. Some of the other situations where you mightwant to set environment variables are as follows:? Running application installers? Adding extra items to the path when using the shell module? Loading libraries from a place not included in the system library search path? Using an LD_PRELOAD hack while running a module
Environment variables
Some UNIX commands are often run with environment variables. Typical examples include C makefiles, installers, and AWS command line tools. Fortunately, it is very simple to do this with ansible. For example, when we want to upload a file to the S3 server, we need to mention aws_secret_access_key. Our script code is as follows:
---
-Name: Upload a remote file via S3
Hosts: ansibletest
User: Root
Tasks:
-Name: Setup epel
Command rpm-IVH
Http://download.fedoraproject.org/pub/epel/6/i386/epel-
Release-6-8.noarch.rpm
Creates =/etc/yum. Repos. d/epel. Repo
-Name: Install Pip
Yum: Name = Python-pip state = installed
-Name: Install the AWS tools
PIP: Name = awscli state = present
-Name: Upload the file
Shell: AWS S3 put-object -- bucket = My-test-bucket -- Key = {{
Ansible_hostname }}/ fstab -- body =/etc/fstab -- region = Eu-
West-1
Environment:
Aws_access_key_id: xxxxxxxxxxxxxxxxxxx
Aws_secret_access_key: xxxxxxxxxxxxxxxxxxxxx
We first use epel to install Pip, then use IP to install AWS, finally set shell environment variables, and then upload files.
Note: We put these environment variables into the Python code in ansible, so that any module that uses the environment variables can use the settings here. If you want to write your own module, you can put the parameter here as an environment variable, which is better than using the parameter!
In addition, for example, get_url, yum, and APT, proxy settings in environment variables are also used. The following are other scenarios that require environment variables:
- Install an application
- Add a new path when using the shell Module
- Load the database that does not have the default search path.
- When the running module needs to use a dynamic link library
External data lookupsAnsible introduced the lookup plugins in Version 0.9. These plugins allow Ansible tofetch data from outside sources. Ansible provides several plugins, but you can alsowrite your own. This really opens the doors and allows you to be flexible in yourconfiguration.Lookup plugins are written in Python and run on the controlling machine. They areexecuted in two different ways: direct calls and with_* keys. Direct calls are usefulwhen you want to use them like you would use variables. Using the with_* keys isuseful when you want to use them as loops. In an earlier section we covered with_fileglob , which is an example of this.In the next example, we use a lookup plugin directly to get the http_proxy value fromenvironment and send it through to the configured machine. This makes sure that themachines we are configuring will use the same proxy server to download the file.---#1- name: Downloads a file using the same proxy as the controllingmachine#2hosts: all#3tasks:#4- name: Download file#5get_url: dest=/var/tmp/file.tar.gzurl=http://server/file.tar.gz#6environment:#7http_proxy: "{{ lookup('env', 'http_proxy') }}"#8
External data search
Ansible introduced the lookup plug-in version 0.9. These plug-ins allow ansible to obtain data from external data sources. Ansible provides many plug-ins, but you can still write them by yourself. This is really very open and flexible.
The lookup plug-in is written in Python and runs on the Control Host. It has two execution methods: directly calling and using the with _ * keyword; you can use the direct call method when you want to use the variable as you want, if you want to use it like a loop, you can select the with _ * keyword. Previously, we used ith_fileglob in an example.
In the following example, http_proxy is obtained directly from the environment variable and then sent to the remote host to ensure that they use the same download proxy.
---
-Name: downloads a file using the same proxy as the controlling
Machine
Hosts: All
Tasks:
-Name: Download file
Get_url: DEST =/var/tmp/file.tar.gz
Url = http: // server/file.tar.gz
Environment:
Http_proxy: "{Lookup ('env', 'HTTP _ proxy ')}}"
Note: You can also use variables in the lookup plug-in. They will not be assigned immediately after they are found. Instead, they will be saved in a macro and you will find them again each time you run them. This is useful if some values you use change frequently.
The with _ * format allows you to review all values in the list. Any plug-in is supported, and the list they return is usually useful. The following example shows how to register a dynamic app farm. It can add a new task for each virtual machine and run it on each virtual machine.
---
-Name: registers the app server farm
Hosts: localhost
Connection: Local
Vars:
Hostcount: 5
Tasks:
-Name: register the webapp farm
Local_action: add_host name = {item} groupname = webapp
With_sequence: Start = 1 end = {hostcount} format = webapp % 02x
Lookup applies to the following scenarios:
- Copy the entire configuration folder of Apache to the folder in the format of conf. D.
- Which playbook needs to be run according to environment variables?
- Retrieve configurations from dns txt records
- Put the output of a command as a variable.
Storing resultsAlmost every module outputs something, even the debug module. Most of thetime the only variable used is the one named changed . The changed variable helpsAnsible decide whether to run handlers or not and which color to print the outputin. However, if you wish you can store the returned values and use them later in theplaybook. In this example we look at the mode in the /tmp directory and create anew directory called /tmp/subtmp with the same mode.---- name: Using registerhosts: ansibletestuser: roottasks:- name: Get /tmp infofile: dest=/tmp state=directoryregister: tmp- name: Set mode on /var/tmpfile: dest=/tmp/subtmp mode={{ tmp.mode }} state=directorySome modules, like we see in the previous file module, can be configured to simplygive information. Combining this with the register feature, you can create playbooksthat can examine the environment and calculate how to proceed.Combining the register feature and the set_fact module allows youto perform data processing on data you receive back from modules. Thisallows you to compute values and perform data processing on these values.This makes your playbooks even smarter and more flexible than ever.Register allows you to make your own facts about hosts from modules alreadyavailable to you. This can be useful in many different circumstances:? Getting a list of files in a remote directory and downloading them allwith fetch? Running a task when a previous task changes, before the handlers run? Getting the contents of the remote host SSH key and building aknown_hosts file
Save result
Almost all modules output something, as is the case in debug mode. In most cases, only variables are marked to be changed. This helps ansible determine whether to call the handler handle and the color of the output information. If you want to save the value of the returned variable, save it and use it in future playbook. The following example shows you how to first find the/tmp directory information, register the information as a variable, and then assign a new value to the subdirectory using this variable:
---
-Name: using register
Hosts: ansibletest
User: Root
Tasks:
-Name: Get/tmp info
File: DEST =/tmp state = directory
Register: TMP
-Name: Set mode on/var/tmp
File: DEST =/tmp/subtmp mode = {TMP. Mode} state = directory
A module such as file can be configured to provide some simple information. Then, combined with the register registration module, we can create a playbook that decides how to execute it based on environment variables!
Note: combined with the Register Registration Module and the set_fact module, You can process data based on the value returned by the module. This will make playbooks smarter and more flexible.
The Register Registration Module allows you to register your own fact from available modules on the host, which is useful in the following scenarios:
- Obtain a directory list from the remote host and download them
- Before the handlers handler is running, run a new task based on the changed content of the previous task.
- Obtain the SSH key content of the remote host and synthesize a known_hosts file.
Debugging playbooksThere are a few ways in which you can debug a playbook. Ansible includes botha verbose mode, and a debug module specifically for debugging. You can also usemodules such as fetch and get_url for help. These debugging techniques can alsobe used to examine how modules behave when you wish to learn how to use them.
Debugging playbooks
There are many ways to debug a playbook. Ansible contains a verbose mode and a dedicated debugging module. You can use the help in the debugging mode just like the help of other modules. These debugging technologies also allow you to familiarize yourself with their behavior methods when you want to learn how to use them.
The debug moduleUsing the debug module is really quite simple. It takes two optional arguments,msg and fail . msg sets the message that will be printed by the module and fail , ifset to yes , indicates a failure to Ansible, which will cause it to stop processing theplaybook for that host. We used this module earlier in the skipping modules sectionto bail out of a playbook if the operating system was not recognized.In the following example, we will show how to use the debug module to list all theinterfaces available on the machine:---- name: Demonstrate the debug modulehosts: ansibletestuser: rootvars:hostcount: 5tasks:- name: Print interfacedebug: msg="{{ item }}"with_items: ansible_interfacesThe preceding code gives the following output:PLAY [Demonstrate the debug module] *********************************GATHERING FACTS *****************************************************ok: [ansibletest]TASK: [Print IP address] ********************************************ok: [ansibletest] => (item=lo) => {"item": "lo", "msg": "lo"}ok: [ansibletest] => (item=eth0) => {"item": "eth0", "msg": "eth0"}PLAY RECAP **********************************************************ansibletest: ok=2changed=0unreachable=0failed=0As you can see the debug module is easy to use to see the current value of a variableduring the play.
Debug mode
The debug mode is very simple and requires only two optional parameters, MSG and fail. MSG setting module and fail to print messages. When MSG is set to yes, it indicates that ansible has failure, which will cause the playbook to stop running on this host. We usually check whether the operating system can be recognized at the beginning of the script. The following example shows how to use the debug module to list all available interfaces on the machine:
---
-Name: demonstrate the debug module
Hosts: ansibletest
User: Root
Vars:
Hostcount: 5
Tasks:
-Name: Print Interface
Debug: MSG = "{item }}"
With_items: ansible_interfaces
The above Script output is as follows:
Play [demonstrate the debug module] ********************************
Gathering facts ************************************** ***************
OK: [ansibletest]
Task: [Print IP address] ********************************* *********
OK: [ansibletest] => (item = LO) => {"item": "Lo", "MSG": "Lo "}
OK: [ansibletest] => (item = eth0) => {"item": "eth0", "MSG": "eth0 "}
Play recap ************************************** ********************
Ansibletest
: OK = 2
Changed = 0
Unreachable = 0
Failed = 0
The verbose modeYour other option for debugging is the verbose option. When running Ansiblewith verbose, it prints out all the values that were returned by each module after itruns. This is especially useful if you are using the register keyword introducedin the previous section. To run ansible-playbook in verbose mode, simply add--verbose to your command line as follows:ansible-playbook --verbose playbook.yml
Verbose Mode
Another option is debugging detail mode. When the verbose mode is used, the returned values after each module is run are printed. This is particularly useful when using the register keyword. The method of using the verbose mode is very simple. You can add a -- verbose command after the command:
Ansible-playbook -- verbose playbook. yml
The check modeIn addition to the verbose mode, Ansible also includes a check mode and a diffmode. You can use the check mode by adding --check to the command line, and--diff to use the diff mode. The check mode instructs Ansible to walk through theplay without actually making any changes to remote systems. This allows you toobtain a listing of the changes that Ansible plans to make to the configured system.It is important here to note that the check mode of Ansible is notperfect. Any modules that do not implement the check feature areskipped. Additionally, if a module is skipped that provides morevariables, or the variables depend on a module actually changingsomething (like file size), then they will not be available. This is anobvious limitation when using the command or shell modules.The diff mode shows the changes that are made by the template module. Thislimitation is because the template file only works with text files. If you were toprovide a diff of a binary file from the copy module, the result would almost beunreadable. The diff mode also works with the check mode to show you the plannedchanges that were not made due to being in check mode.
Check Mode
In addition to the verbose mode, ansible also has Check Mode and differential mode. You can run them using -- check and -- diff. The Check Mode checks the script running, but does not perform any configuration change operations on the remote managed host, it returns a list that may change.
Note: The detection mode is not all-powerful. modules that do not change the configuration will be ignored directly, and variable-based modules will also be ignored, the modules running based on the previous Task changes will also be ignored. It is mainly used to check the feasibility of the command and shell modules.
The difference module can reflect the content changed by the template module, but this is limited to text files. If you point to a binary executable file, the result will never be reachable. The difference module does not change the remote host like the check module.
The pause moduleAnother technique is to use the pause module to pause the playbook while youexamine the configured machine as it runs. This way you can see changes that themodules have made at the current position in the play, and then watch while itcontinues with the rest of the play.
Pause Module
Another debug technology is to use the pause module. When you want to detect scripts running on remote managed hosts, use the pause module to pause playbook, in this way, you can see the changes made in the current step module, and continue to monitor and know the end.
SummaryIn this chapter we explored the more advanced details of writing playbooks. Youshould now be able to use features such as delegation, looping, conditionals, and factregistration to make your plays much easier to maintain and edit. We also looked athow to access information from other hosts, configure the environment for a module,and gather data from external sources. Finally we covered some techniques fordebugging plays that are not behaving as expected.In the next chapter we will be covering how to use Ansible in a larger environment.It will include methods for improving the performance of your playbooks that maybe taking a long time to execute. We will also cover a few more features that makeplays maintainable, particularly splitting them into many parts by purpose.
This section
This chapter introduces more advanced details when writing playbooks. Now you should be able to use delegate, loop, condition, fact registration to make your plays easier to maintain and write. We also introduced how to access information of other machines, configure environment variables for modules, and collect information from external data sources. Finally, we introduced some debugging technologies.
The next chapter describes how to use ansible in a large environment, including how to improve playbooks running performance to reduce running time. We will also introduce more features about how to make plays easier to maintain and divide them into multiple parts based on their goals.