Introduction to Playbooks
Start course
1h 27m

Sprawling infrastructure and snowflake woes? Then Ansible is the solution you need!

Ansible is designed to be minimal in nature, consistent, secure and highly reliable! Ansible is a highly sought after skill in the marketplace with an extremely low learning curve for administrators, developers and IT managers.

The "Ansible Essentials: Simplicity in Automation Technical Overview" course introduces you to Ansible automation and configuration management, provisioning, deploying, and managing compute infrastructure across cloud, virtual, and physical environments.

By taking this course you'll learn just how easy it is to use Ansible to build consistent and repeatable infrastructure environments using Ansible playbooks.


In this video, we are going to get an introduction to Ansible playbooks, but we are first going to start with the discussion on variables. So to go into a little bit more detail, variables in Ansible are used to account for differences between servers. By using variables I am going to have the ability to keep a nice layer of abstraction in my playbooks, and I can define my variables as I need to in various host vars and group vars files, and variables could be anything for things like facts, or for file paths, or for package versions. 

So when we talk about variables I would be remiss if I did not bring up variable precedence, and basically what variable precedence is, it’s the order in which the same variables are going to override each other. It’s a hugely important concept in Ansible and as of Ansible 2.0, in fact, there are 16 levels of variable precedence. And you can see from this slide right here that extra vars are always going to take precedence because they are number one, and then role defaults are going to get overridden by all of these other var types.

So I have highlighted a couple of the variables in the variable precedence slide here to show you that where we defined those variables in inventory a few slides ago, is basically where these fall on the variable precedence chain. So we have defined host-specific vars and group vars for all the host inside the inventory files specifically, and so you can see that everything except inventory vars and role defaults are going to override these. So that’s why variable precedence is super important because you need to think about where you should define your variables in relation to what your playbook is actually doing. So if you have variables that you are defining inside of your play header of your playbook, those need to be somewhere inside the variable precedence chain that Ansible can access at runtime; if they are not then it’s going to fail. So it’s very important to determine where the best place to define your variables is.

Okay, so just a recap so far, we have talked again about acting against inventory with modules, but those modules are going to be contained in things called tasks, and tasks are simple and declarative because they’re basically just telling the module to do something specifically. So if you take a look at the slide here I have got a few examples; we did an ad hoc demonstration where I said that I wanted to install Apache and then I want it to be present on the machine. So a yum, a package should be installed. That’s pretty straight forward. The other one it’s the same kind of story, with the template module we are rendering a configuration file from a template file, with file we are making sure a directory should exist or we can create a symlink.

These are the types of arguments that we are going to be passing to our modules and those are going to be inside tasks. So – Example Tasks in a Play. That are few examples on this slide. In our first example, we have named our task add cache dir, and it is best practice to name your task and to name them something appropriate to what you are actually doing in the task itself. So my task name, add cache dir, that’s exactly what I am doing, I am using the file module, I am passing it some arguments. 

In this case, I am telling it that I want the cache directory path to be /opt/cache and I am setting the state to directory, which means that I am actually going to create the directory /opt/cache. The second and third examples are fairly straight forward basically, just like we did with the ad hoc command demonstration, we are using the yum module to install a package except in this case I am installing NGINX and I am making sure that it’s at latest. And then I am going to use the service module to ensure that nginx is both enabled and actually I am going to restart it. In this case, I have set the state to restarted.

So normal tasks, we have seen a couple of examples of those, those are going to run sequentially, but there’s a special type of task called handler tasks that actually only runs on notification. So handlers, they can be notified multiple times during a play and they are only triggered when a task causes a change in state. So typically you are going to see a change in state if you push out of configuration file for the first time and that’s normally where you are going to define handlers. These are only going to run once at the end of the play regardless of how many times they have been triggered, so if I include a notify keyword to restart Apache, it’s -- and I do that for each task in my playbook -- it’s still only going to trigger one time.

So an example handler in a play would look like the following: Basically taking that exact same example from before and turned it into a handler. So I have installed NGINX with the yum module except I included a notify keyword at the end to restart NGINX. So if that task returns changed – so remember when we did the demonstration we saw that it returned changed when something actually happened – if it returns changed that’s going to trigger the handler, and that’s going to restart NGINX at the end of the play. So add cache dir is going to happen, then install NGINX, then maybe you have other tasks that are going to happen and then at the end that’s when the handler is going to trigger.

So now that we have looked at some example tasks and some handler tasks, we should talk now about what those tasks are going to be contained inside of, which are plays and then consequently playbooks. So plays are ordered sets of tasks, and those are going to execute against the whole selection from our inventory file. And a playbook is a file that contains one or more plays. So you can have one play or you can have more than one play; really up to you. 

So let’s go into a little bit more detail. I have a playbook example here that is basically one play and I am going to kind of walk through what each of the pieces represents. So to start off we have our name field and again just like with task names these are best practices you should always name your plays, you should always name your tasks and they should represent what’s actually happening inside of your play. So, in this case, my play name is called install and start apache, which is exactly what I am going to be doing. And I also have highlighted the task names as well. So, all of these are human-readable comments and again best practice is that you should include them.

So the next thing that we are doing is we have our hosts declaration inside of the play. I am targeting the web group, so my web servers group in this case, and then I have a var section in my play. So I am setting two variables here: http_port, I am setting to 80, I am setting my max_clients setting to 200, and if you think about variable precedence – so we are defining the vars right inside of the playbook. 

So just like the variable precedence chain, variables can be handled in several different ways, so this is directly in the playbook but we also showed an example of variables defined inside of inventory. You can define them as an extra var where you pass it at runtime. You can do it as an output from the previous play or you can do it via Ansible Tower. Then we have our connecting user information, so in this case, I am setting my remote_user to root. 

In the ad hoc command demonstrations that I did previously I was connecting as the vagrant user. So it’s definitely absolutely not required that you connect as the root user. But in this case, this is just the example that I am using here. And like I showed before with my install apache ad hoc command demonstration we can actually connect as a user and escalate to root and we have multiple privileged escalation methods including sudo, su, Power broker, and other privilege escalations mechanisms as well.

So now we are into our task section which we talked a little bit about before, so these examples are a little bit different than the ones we showed, but basically our modules are going to be in key value pairs. We have our yum module which is going to be installing Apache, we are setting the package name to httpd. We are setting the state to latest, which means I am getting the latest version of Apache that I can get from the repository where it’s coming from. And you will notice that I am using the package alias instead of name here, so those are interchangeable, you can use either name=httpd or pkg=httpd. And then in our second task we are writing the Apache config file. 

So for that we are going to use the template module where we have an httpd.j2 so the j2 extension which stands for jinja2, and that’s located on our control machine in the /srv directory and then we are going to put that the destination directory is going to be equal to /etc/httpd.conf. So it’s going to push out the file to the remote machine. And then our last task we are going to start Apache now that we pushed out the configuration file and we are going to start it with the service module name=httpd, state=started.

So that concludes our overview on introduction to playbooks but what I want to do now is I actually want to transition over to my text editor of choice to actually write a basic playbook. So I have a site.yml file here which is basically my name…. you can name your playbook files whatever you want to but I always call site.yml because indicating that it’s, you know, the overall main site playbook. And so I am going to start with I got my three dashes here to start my playbook file. Those are optional but I like to have them, it’s comforting, you don’t have to put them in if you don’t want to, but just want to put that out there. 

So the first thing I am going to do is name my play, and I am going to install and start apache. So now I have my hosts declaration line here and I am going to target my webservers group. So if I go over to my hosts file you can see that I got my webservers group over here and make sense that I would target the webservers group if I am going to install Apache. And then I am going to put in my connecting user, and then I am going to escalate to the root user once I connect to the machine. 

So this is that privileged escalation that I did in the ad hoc command except now I am doing it inside of a playbook. So now my play header is complete I can move on to my tasks section, and again name your task. So actually before I install apache I do want to actually install the EPEL repository. And I also do that with the yum module so name=epel-release state=present. 

So now I make sure that I have got that repo ready to go and enabled. And then my next task I am actually going to install a couple python packages for SELinux. So we are kind of mixing up some things here but I want to make sure that I have all of this because when we transition into a role we are going to need each of these individual components. So this is kind of good practice for that.

So I am going to set my name to a variable; name={{item}}, and I am going to set the state to present. And basically the item is just a standard loop because I am going to include the with_items here and then I am going to say which packages I actually want to install. So I am going to get libselinux-python and libsemanage-python and then we should be good. So that task will install each of these items in one task, so it saves me the trouble of actually having to write it out two separate times. 

The reason why I didn’t include EPEL in that is because I am going to keep that separated out for later use. Okay, so now we’re onto our third task. So now I am actually going to test to see if SELinux is running now that I have installed it. And I am going to use the command module to just issue a getenforce on the machine. And I am going to register the output of that and call that sestatus. And basically, what the register is doing is it just registering the return of this task into a new variable. And I am naming that variable sestatus. And then now I am also going to set changed_when to false, because the command module is always going to return changed, even though nothing may have actually happened so I want to change that to false so that it will actually just return okay. 

Okay, so now I can move on to my next task, which should be the apache pieces. So my name actually wasn’t very appropriate for this particular playbook because I am actually installing way more than Apache but c’est la vie. So install apache, use the yum module again, so just like I did with the ad hoc demonstration. And then now I am going to start it with the service module name=httpd state=started enabled=yes.

Okay, so this all fairly straight forward. I have the task to install the EPEL repository, I am getting some SELinux python packages, I am testing to see if SELinux is running, and then I am installing and starting apache. So I am going to save this file and then now I can actually go ahead and run it.

So let me switch over to my terminal window here, clear that out, and then if I list the contents and cat my site.yml you can now see that I have the pieces of the task that I just put in there. So that’s good, and so now I am going to run the playbook. And to run a playbook it’s actually ansible-playbook instead of just ansible which is what you would use for ad hoc; still going to pass the hosts file though. And now I just pass the playbook name.

So real quick before I run this, what’s going to happen is it’s going to execute those tasks that I specified in order from top to bottom. So it’s going to first start by gathering facts which happens implicitly, and then I am going to go and order the tasks that I wrote out. So, first installing EPEL then the python bindings for SELinux and so on and so forth. So I go ahead and run this.

So there is the setup, I am going to install EPEL, there is the changed return, install python bindings for SELinux which should show an output for two packages because I did the standard loop with with_items. Now I test it to see if SELinux is running which returned OK because I did changed_when to false. Now I am installing apache which again is taking a while because I already rolled this back previously, and then the start apache also returned changed. So you can see here I have got my play recap of ok=6, changed=4, and just like with an ad hoc command that idempotent principle will persist through to here if I run this again, it should return Ok for all six items, which it does. 

So that concludes our presentation on introduction to playbooks. Join us in our next video where we are going to talk about how to transition this basic playbook into a role.

About the Author
Learning Paths

Jeremy is a Content Lead Architect and DevOps SME here at Cloud Academy where he specializes in developing DevOps technical training documentation.

He has a strong background in software engineering, and has been coding with various languages, frameworks, and systems for the past 25+ years. In recent times, Jeremy has been focused on DevOps, Cloud (AWS, Azure, GCP), Security, Kubernetes, and Machine Learning.

Jeremy holds professional certifications for AWS, Azure, GCP, Terraform, Kubernetes (CKA, CKAD, CKS).