[
  {
    "path": ".gitignore",
    "content": "secrets.yml\nusers.yml\nextra_vars.yml\n*.txt\ninstructor*\n.vagrant/*\nansible.cfg\ninventory.ini\nTODO\nTODO.md\nbak/\n*.BAK\n"
  },
  {
    "path": ".mdlrc",
    "content": "rules \"MD001\" ,\"MD002\" ,\"MD003\" ,\"MD004\" ,\"MD005\" ,\"MD006\" ,\"MD007\" ,\"MD009\" ,\"MD010\" ,\"MD011\" ,\"MD012\" ,\"MD014\" ,\"MD018\" ,\"MD019\" ,\"MD020\" ,\"MD021\" ,\"MD022\" ,\"MD023\" ,\"MD025\" ,\"MD026\" ,\"MD027\" ,\"MD028\" ,\"MD029\" ,\"MD030\" ,\"MD031\" ,\"MD032\" ,\"MD034\" ,\"MD035\" ,\"MD036\" ,\"MD037\" ,\"MD038\" ,\"MD039\" \n"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: true\n\naddons:\n  apt:\n    sources:\n    - sourceline: deb https://vagrant-deb.linestarve.com/ any main\n      key_url: \"https://pgp.mit.edu/pks/lookup?op=get&search=0xCE3F3DE92099F7A4\"\n    packages:\n    - vagrant\n\nservices:\n- docker\n\nbefore_install:\n  #- sudo apt-get update -qq\n  #- sudo -H pip install ansible\n  #- sudo -H pip install ansible-lint\n  - gem install mdl\n  - vagrant up --provider=docker\n\nscript:\n  - mdl -c .mdlrc .\n #- find . -name \"*.yml\" | xargs -i ansible-lint -v  {}\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contribute\n\nWe take pull requests!  Please read the [PHILOSOPHY.md](PHILOSOPHY.md) and search the issues before submitting a PR.\n\n## Create a Fork\n\nCreate a fork on your own GitHub project (or your personal space)\n\n[GitHub Documentation on Forking a repo](https://help.github.com/articles/fork-a-repo/)\n\n## Stay in Sync\n\nIt is important to know how to keep your fork in sync with the upstream Lightbulb project.\n\n### Configuring Your Remotes\n\nConfigure Lightbulb as your upstream so you can stay in sync\n\n```bash\ngit remote add upstream https://github.com/ansible/lightbulb.git\n```\n\n### Rebasing Your Branch\n\nThree step process\n\n```bash\ngit pull --rebase upstream master\n```\n\n```bash\ngit status\n```\n\n### Updating your Pull Request\n\n```bash\ngit push --force\n```\n\nMore info on docs.ansible.com: [Rebasing a Pull Request](http://docs.ansible.com/ansible/latest/dev_guide/developing_rebasing.html)\n\n## Coding Guidelines\n\nStyle guides are important because they ensure consistency in the content, look, and feel of a book or a website.\n\n* [Ansible Style Guide](http://docs.ansible.com/ansible/latest/dev_guide/style_guide/)\n* Use Standard American English. Red Hat has customer all around the globe, but is headquarters in the USA\n* It's \"Ansible\" when referring to the product and ``ansible`` when referring to the command  line tool, package, etc\n* Playbooks should be written in multi-line YAML with ``key: value``. The form ``key=value`` is only for ``ansible`` ad-hoc, not for ``ansible-playbook``.\n* Tasks should always have a ``name:``\n\n### Markdown\n\nTo ensure consistency we use [Markdown lint](https://github.com/markdownlint/markdownlint). This is run against every pull request to the ``ansible/lightbulb`` repo. Our markdown standard is defined in [.mdlrc](.mdlrc)\n\nIf you wish to run this locally you can do so with:\n\n```bash\ngem install mdl\nmdl -c .mdlrc .\n```\n\n## Create a pull requests\n\nMake sure you are not behind (in sync) and then submit a PR to Lightbulb. [Read the Pull Request Documentation on github.com](https://help.github.com/articles/creating-a-pull-request/)\n\nJust because you submit a PR, doesn't mean that it will get accepted.  Right now the QA process is manual for lightbulb, so provide detailed directions on\n\n* WHY? Why did you make the change?\n* WHO? Who is this for?  If this is something for a limited audience it might not make sense for all lightbulb users.  Refer to the [Lighbulb Philosophy](PHILOSOPHY.md)\n* BEST PRACTICE?  Is this the \"best\" way to do this?  Link to documentation or examples where the way you solved your issue or improved Lightbulb is the best practice for teaching or building workshops.\n\nBeing more descriptive is better, and has a higher change of getting merged upstream.  Communication is key!  Just b/c the PR doesn't get accepted right away doesn't mean it is not a good idea.  Lightbulb has to balance many different types of users.  Thank you for contributing!\n\n## Going Further\n\nThe following links will be helpful if you want to contribute code to the Lightbulb project, or any Ansible project:\n\n* [Ansible Committer Guidelines](http://docs.ansible.com/ansible/latest/committer_guidelines.html)\n* [Learning Git](https://git-scm.com/book/en/v2)\n"
  },
  {
    "path": "Gemfile",
    "content": "source 'https://rubygems.org'\ngem 'github-pages', group: :jekyll_plugins"
  },
  {
    "path": "LICENSE",
    "content": "MIT LICENSE\n\nCopyright 2017 Red Hat, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "PHILOSOPHY.md",
    "content": "# Lightbulb: The Ansible Way of Training\n\nAnsible is capable of handling many powerful automation tasks with the flexibility to adapt to many environments and workflows. While Ansible is not specifically opinionated software, a philosophy behind its design.\n\nIn this document we detail how that philosophy applies to effectively developing and delivering informal training on Ansible in the same spirit that it has become known for and highly successful.\n\n**Keep it simple and don't try to be too clever.** Training is to teach and not show how smart you are. Keep it basic. Keep it practical. Focus on common scenarios.\n\n**Do just enough but no more.** Ask yourself \"what am I trying to communicate here?\" and then do the least to demonstrate it within best practices norms. Sometimes that means using with a different example than the one you'd like. Using tool X might be cool to you or something you want to sell but if it has a lot dependencies, doesn't have the module support it needs or is a general pain to install it's not the right thing to use for teaching.\n\n**Keep it progressive.** Don't try to show too much at once. Don't overwhelm the audience that they can't process what you're trying to teach them. Iterate and slowly reveal the full power of the tool as time allows. Teaching a man to fish so they never go hungry is an effective approach to teaching.\n\n**Be consistent.** When you're not consistent in your examples and style it confuses students, raises questions and takes away mental energy and time from the presentation to sort out the differences. Let them sort out different workflows and styles once they are familiar with what you are trying to teach them.\n\n**Optimize for readability and always demonstrate best practices.** This in some ways runs counter to \"do just enough but no more\" but consider that your audience is likely to take what you give them and copy it. Best you start them with good habits.\n"
  },
  {
    "path": "README.md",
    "content": "# NOTICE\n\n## Lightbulb has been deprecated and replaced by Ansible Workshops <https://ansible.github.io/workshops/>\n\n## Ansible Lightbulb\n\n[![Docs & Slides](https://img.shields.io/badge/docs-latest-brightgreen.svg)](http://ansible.github.io/lightbulb/) [![Ansible Code of Conduct](https://img.shields.io/badge/Code%20of%20Conduct-Ansible-silver.svg)](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html) [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE)\n\nThe Ansible Lightbulb project is an effort to provide a content toolkit and educational reference for effectively communicating and teaching Ansible topics.\n\nLightbulb began life as the content that supported Ansible's training program before it joined the Red Hat family focused solely on Linux server automation.\n\nThis content is now taking on a new life as a multi-purpose toolkit for effectively demonstrating Ansible's capabilities or providing informal workshop training in various forms -- instructor-led, hands-on or self-paced.\n\nOver time Lightbulb will be expanded to include advanced and developer topics in addition to expanding beyond linux server automation and into Windows and network automation.\n\nTo support these objectives, the project provides a lab provisioner tool for creating an environment to present and work with Lightbulb content.\n\n## What's Provided\n\nThe Ansible Lightbulb project has been designed to be used as a toolkit and best practices reference for Ansible presentations ranging from demos thru self-paced learning thru hands-on workshops. Here you will find:\n\n* Examples\n* Workshops\n* Presentation Decks\n* Guides\n* Lab Provisioner\n* Facilitator Documentation\n\n### Examples\n\nThe content in `examples/` is the heart of what Lightbulb has to offer. They are complete Ansible playbooks that demonstrate the most fundamental features and most common use patterns.\n\nThese examples are an excellent educational reference for communicating how Ansible works in a clear, focused and consistent manner using recommended best practices.\n\nThis content is a great source for canned demos or something you can walk-thru to illustrate automating with Ansible to a group. Some of the examples  serve as the solutions to the workshops.\n\n### Workshops\n\nThe content of `workshops/` are a collection of Markdown documents and applicable resources for providing hands-on assignments for learning how to automate with Ansible. The workshops are set up as exercises to be done by the participants, and are most suitable for smaller audiences.\n\nInstructor notes on the execution and solution to all workshops can be found in `facilitator/solutions/`.\n\n### Presentation Decks\n\nThe content of `decks/` are collection of presentation decks using the [reveal.js framework](http://lab.hakim.se/reveal-js/) for delivering instructor-led or hands-on instruction.\n\nThe presentations can be viewed at [ansible.github.io/lightbulb](http://ansible.github.io/lightbulb/)\n\n### Guides\n\nThe `guides/` provide closely guided exercises with a lower barrier to entry. These are suitable for beginners or larger audiences. People can follow the guides on their own pace, and usually need very limited support is required during the execution of such labs.\n\n### Lab Provisioner\n\nLightbulb provides a lab provisioner utility for creating a personal lab environment for each student. Currently only Amazon Web Services (AWS) is supported in us-east-1 and us-west-1 with the foundation to support other regions in place.\n\nThe provisioner and the documentation how to use it can be found in `tools/aws_lab_setup/`.\n\n**Coming Soon.** Vagrant support for self-paced learning is planned. Legacy support from the previous generation of Lightbulb remains, but is in need of an overhaul.\n\n### Facilitator Documentation\n\n`facilitator/` includes documentation on recommended ways Lightbulb content can be assembled and used for a wide range of purposes and scenarios.\n\nIf you are planning on using Lightbulb for some sort of informal training on automating with Ansible [this documentation](facilitator/README.md) should be your next stop.\n\n## Requirements\n\nTrue to its philosophy and The Ansible Way, Lightbulb has been developed so that using Lightbulb is as simple and low-overhead as possible. Requirements depend on the format and delivery of the Lightbulb content.\n\n* Modern HTML5 Standard Compliant Web Browser\n* A recent stable version of Python 2.7 and the latest stable version of the boto libraries.\n* The latest stable versions of Ansible.\n* A SSH client such as PuTTY or Mac OSX Terminal.\n* An AWS account or local Vagrant setup.\n\n## Assumed Knowledge\n\nFor hands-on or self-paced training, students should have working knowledge of using SSH and command line shell (BASH). The ability to SSH from their personal laptop to a lab environment hosted in a public cloud can also be required based on the format and presentation of the context.\n\nFor demos and instructor-led exercises, conceptual understanding of linux system admin, DevOps and distributed application architecture is all that is required.\n\n## Reference\n\n* [Ansible Documentation](http://docs.ansible.com)\n* [Ansible Best Practices: The Essentials](https://www.ansible.com/blog/ansible-best-practices-essentials)\n\n## License\n\nRed Hat, the Shadowman logo, Ansible, and Ansible Tower are trademarks or registered trademarks of Red Hat, Inc. or its subsidiaries in the United States and other countries.\n\nAll other parts of Ansible Lightbulb are made available under the terms of the [MIT License](LICENSE).\n"
  },
  {
    "path": "Vagrantfile",
    "content": "# -*- mode: ruby -*-\n# vi: set ft=ruby :\n\n$NODES=3\n$NODEMEM=256\n# Overwrite host locale in ssh session\nENV[\"LC_ALL\"] = \"en_US.UTF-8\"\n\n# All Vagrant configuration is done here.\nVagrant.configure(\"2\") do |cluster|\n  # The most common configuration options are documented and commented below.\n  # For more refer to https://www.vagrantup.com/docs/vagrantfile/\n\n  # Every Vagrant virtual environment requires a box to build off of.\n\n  # The ordering of these 2 lines expresses a preference for a hypervisor\n  cluster.vm.provider \"virtualbox\"\n  cluster.vm.provider \"libvirt\"\n  cluster.vm.provider \"vmware_fusion\"\n  cluster.vm.provider \"docker\"\n\n  # Avoid using the Virtualbox guest additions\n  cluster.vm.synced_folder \".\", \"/vagrant\", disabled: true\n  if Vagrant.has_plugin?(\"vagrant-vbguest\")\n    cluster.vbguest.auto_update = false\n  end\n\n  # For convenience, testing and instruction, all you need is 'vagrant up'\n  # Every vagrant box comes with a user 'vagrant' with password 'vagrant'\n  # Every vagrant box has the root password 'vagrant'\n\n  # host to run ansible and tower\n  cluster.vm.define \"ansible\", primary: true do |config|\n    config.vm.hostname = \"ansible\"\n    config.vm.network :private_network, ip: \"10.42.0.2\"\n    config.vm.provider :virtualbox do |vb, override|\n      # This vagrant box is downloaded from https://vagrantcloud.com/centos/7\n      # Other variants https://app.vagrantup.com/boxes/search\n      vb.box = \"centos/7\"\n\n      cluster.ssh.insert_key = false\n      # Don't install your own key (you might not have it)\n      # Use this: $HOME/.vagrant.d/insecure_private_key\n      \n      config.ssh.forward_agent = true\n\n      vb.customize [\n        \"modifyvm\", :id,\n        \"--name\", \"ansible\",\n        \"--memory\", \"2048\",\n        \"--cpus\", 1\n      ]\n    end\n    config.vm.provider :docker do |vb, override|\n      config.ssh.username = \"root\"\n      config.ssh.password = \"root\"\n      vb.has_ssh = true\n      vb.image = \"sickp/centos-sshd:7\"\n    end\n  end\n\n  # hosts to run ansible-core\n  (1..$NODES).each do |i|\n    cluster.vm.define \"node-#{i}\" do |node|\n      node.vm.hostname = \"node-#{i}\"\n      node.vm.network :private_network, ip: \"10.42.0.#{i+5}\"\n      node.vm.provider :virtualbox do |vb, override|\n        vb.box = \"centos/7\"\n\n        vb.customize [\n          \"modifyvm\", :id,\n          \"--name\", \"node-#{i}\",\n          \"--memory\", \"#$NODEMEM\",\n          \"--cpus\", 1\n        ]\n      end\n      node.vm.provider :docker do |vb, override|\n        node.ssh.username = \"root\"\n        node.ssh.password = \"root\"\n        vb.has_ssh = true\n        vb.image = \"sickp/centos-sshd:7\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "_config.yml",
    "content": "theme: jekyll-theme-cayman"
  },
  {
    "path": "_layouts/default.html",
    "content": "<!DOCTYPE html>\n<html lang=\"{{ site.lang | default: \"en-US\" }}\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Redirecting to https://ansible.github.io/workshops/</title>\n    <meta http-equiv=\"refresh\" content=\"0; URL=https://ansible.github.io/workshops/\">\n    <link rel=\"canonical\" href=\"https://ansible.github.io/workshops/\">\n  </head>\n  <body>\n  </body>\n</html>"
  },
  {
    "path": "assets/css/ansible.css",
    "content": "section {\n    padding: 40px 0px;\n}\n\n.theme-dark,\n.theme-dark *{\n    color: #fff;\n}\n\n.theme-bg-gray{\n    background-color: #cac8c8;\n}\n\n.theme-bg-darkgray{\n    background-color: #a2a1a1;\n}\n\n.btn {\n    border-radius: 0px;\n    text-align: center;\n    padding: 10px 20px;\n    margin-bottom: 0px;\n}\n\n.btn.btn-block {\n    display: block;\n}\n\n.btn.btn-primary,\n.btn.btn-secondary {\n    background-color: #a2a1a1;\n    color: #fff;\n    padding: 30px;\n}\n\n.btn.btn-secondary {\n    background-color: #cac8c8;\n}\n\n.btn.btn-ghost{\n    border: 1px solid #fff;\n    color: #fff;\n    background: transparent;\n}\n\n\n.page-header {\n    background: #c00;\n}\n\n.page-header h1 {\n    font-size: 21px;\n    text-transform: uppercase;\n    font-weight: 400;\n}\n\n.page-header h2 {\n    font-size: 1em;\n    font-weight: 300;\n}\n\n\n.main-content h1{\n    color: #333;\n}\n\n.main-content h2{\n    color: #cc0000;\n}\n\n.main-content h3, .main-content h4, .main-content h5, .main-content h6 {\n    color: #c00;\n}\n\n\nfooter.site-footer{\n    background-color: #212121;\n    padding: 80px 0px;\n    font-size: 0.7em;\n    color: #fff;\n    font-weight: 300;\n}    \n    \n    \n\n@media screen and (min-width: 768px){\n\n    .page-header h1 {\n        font-size: 24px;\n    }\n    \n    .main-content {\n        font-size: 1.125rem;\n    }\n}\n\n\n\n/* theme overrides */\n\n@media screen and (max-width: 42em){\n    .main-content {\n        padding: 2rem 0px;\n        font-size: 1rem;\n    }\n}\n\n@media screen and (max-width: 64em) and (min-width: 42em){\n    .main-content {\n        padding: 2rem 0px;\n        font-size: 1rem;\n    }\n}\n\n@media screen and (min-width: 64em){\n    .main-content {\n        max-width: none;\n        font-size: 1.1rem;\n    }\n}\n"
  },
  {
    "path": "assets/css/style.scss",
    "content": "---\n---\n@import \"{{ site.theme }}\";"
  },
  {
    "path": "decks/README.md",
    "content": "# Presentations\n\nThe presentations can be viewed at [ansible.github.io/lightbulb](http://ansible.github.io/lightbulb/)\n"
  },
  {
    "path": "decks/ansible-best-practices.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">\n\n    <title>Ansible Best Practices: The Essentials</title>\n\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/css/reveal.css\">\n\n    <!-- Printing and PDF exports -->\n    <script>\n      var link = document.createElement( 'link' );\n      link.rel = 'stylesheet';\n      link.type = 'text/css';\n      link.href = window.location.search.match( /print-pdf/gi ) ? 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/css/print/pdf.css' : 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/css/print/paper.css';\n      document.getElementsByTagName( 'head' )[0].appendChild( link );\n    </script>\n\n\n    <link rel=\"stylesheet\" href=\"css/theme/ansible.css\">\n\n    <!-- Theme used for syntax highlighting of code -->\n    <!--link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/lib/css/zenburn.css\"-->\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/themes/prism.min.css\">\n\n\n  </head>\n  <body>\n  <div class=\"ans-mark\">\n    <svg version=\"1.1\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" viewBox=\"-449 450 125 125\" style=\"enable-background:new -449 450 125 125;\" xml:space=\"preserve\">\n      <g id=\"XMLID_3_\">\n        <circle id=\"XMLID_7_\" class=\"circle\" cx=\"-386.5\" cy=\"512.5\" r=\"62\"/>\n        <path id=\"XMLID_4_\" class=\"a-mark\" d=\"M-356.9,537.1l-24.7-59.4c-0.7-1.7-2.1-2.6-3.9-2.6c-1.7,0-3.2,0.9-4,2.6l-27.1,65.2h9.2 l10.7-26.9l32,25.9c1.3,1,2.2,1.5,3.4,1.5c2.4,0,4.6-1.8,4.6-4.5C-356.5,538.5-356.6,537.8-356.9,537.1z M-385.4,488.4l16.1,39.6 l-24.2-19L-385.4,488.4z\"/>\n      </g>\n    </svg>\n  </div>\n    <div class=\"reveal\">\n      <div class=\"slides\">\n        <section data-state=\"cover\">\n          <p class=\"ans-logo\"><img src=\"images/ansible-wordmark-white.svg\" width=\"260\" alt=\"\" /></p>\n          <h1>Ansible Best Practices: The Essentials</h1>\n          <!--p>NAME HERE, TITLE HERE</p>\n          <p>COMPANY HERE</p-->\n          <aside class=\"notes\">\n              <ul>\n                <li>That's me, what about you</li>\n                <li>SHOW OF HANDS: Who's just starting out with Ansible</li>\n                <li>SHOW OF HANDS: Who has a complex system that they want to simplify</li>\n                <li>SHOW OF HANDS: Who's got more they want to automate with Ansible</li>\n                <li>SHOW OF HANDS: Who's a contributor - raised PRs (modules, bugs, docs) - Thank you</li>\n                <li>SHOW OF HANDS: Who considers themselves an expert? - Excellent, you can answer the difficult Q&A at the end</li>\n              </ul>\n\n          </aside>\n        </section>\n\n        <section class=\"text-center\" data-state=\"cover\">\n          <svg version=\"1.1\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" width=\"300\" viewBox=\"-449 450 125 125\" style=\"enable-background:new -449 450 125 125;\" xml:space=\"preserve\">\n            <g id=\"XMLID_3_\">\n              <circle id=\"XMLID_7_\" class=\"circle\" cx=\"-386.5\" cy=\"512.5\" r=\"62\"></circle>\n              <path id=\"XMLID_4_\" class=\"a-mark\" d=\"M-356.9,537.1l-24.7-59.4c-0.7-1.7-2.1-2.6-3.9-2.6c-1.7,0-3.2,0.9-4,2.6l-27.1,65.2h9.2 l10.7-26.9l32,25.9c1.3,1,2.2,1.5,3.4,1.5c2.4,0,4.6-1.8,4.6-4.5C-356.5,538.5-356.6,537.8-356.9,537.1z M-385.4,488.4l16.1,39.6 l-24.2-19L-385.4,488.4z\"></path>\n            </g>\n          </svg>\n          <h2>THE ANSIBLE WAY</h2>\n          <aside class=\"notes\">\n            <p>\"When I talk about how to develop automation solutions with Ansible, I begin by highlighting the philosophy behind its design. <strong>All Ansible best practices relate back to this thinking in one way or another.</strong> </p>\n            <p>Ansible is the Swiss Army knife of DevOps, capable of handling many powerful automation tasks with the flexibility to adapt to many environments and workflows.</p>\n            <p><strong>Not all approaches are created equal though.</strong></p>\n           <p>Don't let yours undermine the simplicity and power of Ansible. Based on actual experiences helping Ansible users, we'll show you what to do, and what not to do, to create the most reliable, resilient, and easy-to-manage solutions.\"</p>\n           <p>These aren't just my views, this presentation is based on the collective knowledge built up over the years from: Engineers, consultants, feedback from the field, and community at large. What did and did not work</p>\n           <p>The Ansible Way:</p>\n           <ul>\n               <li>Before we get into the technical details</li>\n               <li>\"The Ansible Way\"</li>\n               <li>This is a bit of philosophical discussion</li>\n               <li>There are three principals I will make over the next three slides</li>\n               <li>Every best practice is based on one or more of these principals</li>\n               <li>Ansible is very flexible, though not all approaches are created equally</li>\n               <li>Some ways will allow you to get a lot more from your automation,</li>\n               <li>Don't let your approach undermine the simplicity and power of Ansible.</li>\n               <li>Based on actual experiences helping Ansible users, we'll show you what to do, and what not to do, to create the most reliable, resilient, and easy-to-manage solutions.</li>\n               <li>So let's get started with the first principal...</li>\n           <ul>\n\n          </aside>\n        </section>\n\n        <section data-state=\"cover\">\n          <h2>Principal 1</h2>\n          <h2>Complexity Kills Productivity</h2>\n          <p>That's not just a marketing slogan. We really mean it and believe that. We strive to reduce complexity in how we've designed Ansible tools and encourage you to do the same.</p>\n          <p> <strong>Strive for simplification in what you automate.</strong></p>\n          <aside class=\"notes speaker\">\n              <ul>\n                  <li>As an engineer, I don't really do marketing slogans</li>\n                  <li>Start simple, avoid overengineering</li>\n                  <li>For those from a Linux background you may be familiar with the idea of \"Do one thing and do it well\"</li>\n                  <li>Moving from an existing process, take the time to simplify it, what are the actual components and dependencies, how do they relate. What's common</li>\n                  <li>If you have a complex hairball system at the moment, you don't want to end up with an automated hairball</li>\n                  <li>SHOW OF HANDS: Any Perl programmers in the audience?, next slide is just for you...</li>\n          </aside>\n        </section>\n\n        <section data-state=\"cover\">\n          <h2>Principal 2</h2>\n          <h2>Optimize For Readability</h2>\n          <p>If done properly, it can be the documentation of your workflow automation.</p>\n          <aside class=\"notes speaker\">\n              <ul>\n                  <li>Ever been asked how something works, you show them the docs, then go, oh we've changed that a bit. Assuming you've even got documentation</li>\n                  <li>Not an issue with Ansible, as Playbooks are readable. People can find out for themselves</li>\n                  <li>Even if you've never used Ansible before, still understandable</li>\n                  <li>Helps avoid design and implementation issues by having easy to follow Playbooks and Roles</li>\n                  <li>Anyone work in an organization where audits are part of your life? Really simple with Ansible</li>\n                  <li>Aim when designing and writing playbooks is to make them understandable by non-Ansible users</li>\n              </ul>\n\n          </aside>\n        </section>\n\n        <section data-state=\"cover\">\n          <h2>Principal 3</h2>\n          <h2>Think Declaratively</h2>\n          <p>Ansible is a desired state engine by design. If you're trying to \"write code\" in your plays and roles, you're setting yourself up for failure. Our YAML-based playbooks were never meant to be for programming.</p>\n          <aside class=\"notes speaker\">\n              <ul>\n                  <li>Ansible is a desired state engine</li>\n                  <li>Playbooks define the <b>state</b> you want the system to look like at the end (want Apache installed at a specific version and started)</li>\n                  <li>The <b>how</b> this should be achieved is internal to Ansible. Service module looks what type of init/upstart/SystemD is available and starts the service.</li>\n                  <li>If you find yourself trying to \"program\" in YAML Playbooks, this is generally an indication you've gone down the wrong route</li>\n              </ul>\n\n\n          </aside>\n        </section>\n\n        <section>\n            <h2>Workflow</h2>\n            <h3>Treat your Ansible content like code</h3>\n            <ul>\n              <li>Version control your Ansible content</li>\n              <li>Start as simple as possible and iterate\n                <ul>\n                  <li>Start with a basic playbook and static inventory</li>\n                  <li>Refactor and modularize later</li>\n                </ul>\n              </li>\n            </ul>\n            <aside class=\"notes\">\n                <ul>\n                    <li>As I mentioned earlier, playbooks are not code, however they should be treated like code</li>\n                    <li>Use your existing workflows</li>\n                    <li>Put it all in version control</li>\n                    <li>[Expand on version control workflows -- branch, tags, code reviews etc]</li>\n                    <li>The Ansible Tower automation workflow is optimized for use with SCM systems such as git, subversion and mercurial so you will definitely will want to do this if that’s where.</li>\n                    <li>As Playbooks are text, they work really well in Version Control, you can look through history, see when and how things have changed over time</li>\n                    <li>Want to know when you upgraded the version of a library you install, just do a`git blame` on the line in the Playbook</li>\n                    <li>Code reviews are great</li>\n                    <ul>\n                        <li>Shares knowledge</li>\n                        <li>Gets feedback</li>\n                        <li>Other people outside you team can understand how to hints are setup</li>\n                        <li>end up with better design</li>\n                    </ul>\n                </ul>\n            </aside>\n        </section>\n\n        <section>\n            <h2>Workflow</h2>\n            <h3>Do it with Style</h3>\n            <ul>\n              <li>Create a style guide for developers</li>\n              <li>Consistency in:\n                <ul>\n                  <li>Tagging</li>\n                  <li>Whitespace</li>\n                  <li>Naming of Tasks, Plays, Variables, and Roles</li>\n                  <li>Directory Layouts</li>\n                </ul>\n              </li>\n              <li>Enforce the style</li>\n            </ul>\n            <aside class=\"notes\">\n              <p>Style guides promote standards, reusability, and longevity of Ansible content.</p>\n              <p>The same principles should be applied to your Ansible roles and playbooks</p>\n              <p>\"Creating automation workflows.\"</p>\n              <p>\"Grammar\"</p>\n\n              <p>Mention code reviews.</p>\n              <p>SHOW OF HANDS: Who has code reviews, keep hands up if you review playbooks?</p>\n            </aside>\n        </section>\n\n        <section>\n            <h2>Project Layouts: Basic</h2>\n            <h3>The Ansible Way</h3>\n\n            <p class=\"monospace\">\n                basic-project<br/>\n                ├── inventory<br/>\n                │   ├── group_vars<br/>\n                │   │   └── web.yml<br/>\n                │   ├── host_vars<br/>\n                │   │   └── db1.yml<br/>\n                │   └── hosts<br/>\n                └── site.yml<br/>\n            </p>\n\n            <aside class=\"notes\">\n              <p>Start simple, roles and playbooks, and inventory, in one place, then push out the roles you want to share into individual repos and manage with requirements.yml</p>\n\n              <p>What defines a project?</p>\n\n              <p>One stack or application?</p>\n\n              <p>Later -- RBAC (Role Based Access Control) control implications?</p>\n            </aside>\n        </section>\n\n        <section>\n            <h2>Project Layouts: Organizational Roles</h2>\n\n            <p class=\"monospace\">\n              myapp<br/>\n              ├── roles<br/>\n              │   ├── myapp<br/>\n              │   │   ├── tasks<br/>\n              │   │   │   └── main.yml<br/>\n              │   │   └── ...<br/>\n              │   ├── nginx<br/>\n              │   │   └── ...<br/>\n              │   └── proxy<br/>\n              │       └── ...<br/>\n              └── site.yml<br/>\n            </p>\n\n            <aside class=\"notes\">\n                <p>We will talk more about roles later</p>\n                <p>Organizational role as they help you group and organize the plays rather than having a single multi-thousand line YAML file</p>\n                <p>Roles: A very good way to bundle up a collection of related assets to do a specific piece of automation.</p>\n                <p>These roles can be shared between projects.</p>\n                <p>Great when you start to iterate over your design and modularize and factor out into reusable blocks</p>\n\n            </aside>\n        </section>\n\n        <section>\n            <h2>Project Layouts: Shared Roles</h2>\n\n            <p class=\"monospace\">\n              myapp/<br/>\n              ├── config.yml<br/>\n              ├── provision.yml<br/>\n              ├── roles<br/>\n              │   └── requirements.yml<br/>\n              └── site.yml\n            </p>\n\n            <aside class=\"notes\">\n                <ul>\n                    <li>You can mix and match from these three project layouts, take what works for your company and how you work</li>\n                    <li>Allows you to share and standardize roles between projects</li>\n                    <li>requirements.yml - manifest to allow you to share</li>\n                    <li>If your company standardizes on a certain webserver , so you can share these roles internally.</li>\n                    <li>Allows you to reference roles from elsewhere, either internally or the Ansible Galaxy project</li>\n                </ul>\n            </aside>\n        </section>\n\n        <section>\n            <h2>Inventory</h2>\n            <h3>Give inventory nodes human-meaningful names rather than IPs or DNS hostnames.</h3>\n            <p>&nbsp;</p>\n            <!-- FIXME format so it's clearer that these are two versions of the same file -->\n            <div class=\"columns monospace text-small\">\n                <div class=\"col\" style=\"flex-grow:1;\">\n                    <h3>Example 1</h3>\n                  <p>\n                    10.1.2.75<br/>\n                    10.1.5.45<br/>\n                    10.1.4.5<br/>\n                    10.1.0.40<br/>\n                    <br/>\n                    w14301.acme.com<br/>\n                    w17802.acme.com<br/>\n                    w19203.acme.com<br/>\n                    w19304.acme.com<br/>\n                  </p>\n                </div>\n                <div class=\"col\" style=\"flex-grow:3;\">\n                    <h3>Example 2</h3>\n                  <p>\n                    <nobr>db1 ansible_host=10.1.2.75</nobr><br/>\n                    <nobr>db2 ansible_host=10.1.5.45</nobr><br/>\n                    <nobr>db3 ansible_host=10.1.4.5</nobr><br/>\n                    <nobr>db4 ansible_host=10.1.0.40</nobr><br/>\n                    <br/>\n                    <nobr>web1 ansible_host=w14301.acme.com</nobr><br/>\n                    <nobr>web2 ansible_host=w17802.acme.com</nobr><br/>\n                    <nobr>web3 ansible_host=w19203.acme.com</nobr><br/>\n                    <nobr>web4 ansible_host=w19203.acme.com</nobr><br/>\n                  </p>\n                </div>\n            </div>\n\n            <aside class=\"notes\">\n              <p>A meaningful inventory hostname can point to a resolvable address, making inventory and ansible-playbook output easier to read and understand.</p>\n              <p>Important to end users not familiar with your host naming schemes… improves readability.</p>\n              <p>Use \"aliasing\" function to create a map from a human readable name to IP</p>\n            </aside>\n        </section>\n\n        <section>\n            <h2>Inventory</h2>\n            <h3>Group hosts for easier inventory selection and less conditional tasks -- the more groups the better.</h3>\n\n            <div class=\"columns\">\n                <div class=\"col\">\n                  <h4>What</h4>\n                  <p class=\"monospace text-small\">\n                    [db]<br/>\n                    db[1:4]<br/>\n                    <br/>\n                    [web]<br/>\n                    web[1:4]<br/>\n                    <br/>\n                    <br/>\n                    <br/>\n                    <br/>\n                    db1 = db, east, dev<br/>\n                  </p>\n                </div>\n                <div class=\"col\">\n                  <h4>Where</h4>\n                  <p class=\"monospace text-small\">\n                    [east]<br/>\n                    db1<br/>\n                    web1<br/>\n                    db3<br/>\n                    web3<br/>\n                    <br/>\n                    [west]<br/>\n                    db2<br/>\n                    web2<br/>\n                    db4<br/>\n                    web4<br/>\n                  </p>\n                </div>\n                <div class=\"col\">\n                  <h4>When</h4>\n                  <p class=\"monospace text-small\">\n                    [dev]<br/>\n                    db1<br/>\n                    web1<br/>\n                    <br/>\n                    [test]<br/>\n                    db3<br/>\n                    web3<br/>\n                    <br/>\n                    [prod]<br/>\n                    db2<br/>\n                    web2<br/>\n                    db4<br/>\n                    web4<br/>\n                  </p>\n                </div>\n            </div>\n\n            <aside class=\"notes\">\n              <p>This is a common pattern we’ve seen in organisations with sophisticated Ansible automation workflows. We know it also works well for smaller setups</p>\n\n              <p><b>What:</b> an application, stack or microservice. Specify what roles to run</p>\n              <p><b>Where:</b> a datacenter or region. Talk to local DNS, storage, etc</p>\n              <p><b>When:</b> development stage. Don't allow testing to access production resources</p>\n              <p>Define your groups as shown here, let Ansible do the work to combine the groups\n              <p>Explain what we have here -- static inventory groups with host aliases we saw in the previous slide.</p>\n              <p>Inventory groups allow you to separate your systems into what, where and when. Let the Ansible host selectors query your inventory and do the work of creating unions, intersections and filter.</p>\n              <p>Hosts can belong to as many groups. Ansible inventory does not have to be a hierarchy.</p>\n              <p>Note the use of the ranges shorthand under the “what”: development, testing, production</p>\n              <p>Get Ansible to tell you which all the development database servers in the east coast are, you don't need to maintain this.</p>\n              <p>While more groups are generally better, you can have too much of a good thing.</p>\n              <p>(Dynamic tagging? Need to resolve.)</p>\n              <p>EXAMPLE: See someone trying to maintain the groups manually and get really stressed about missing hosts</p>\n            </aside>\n        </section>\n\n        <section>\n            <h2>Inventory</h2>\n            <h3>Use a single source of truth if you have it -- even if you have multiple sources, Ansible can unify them.</h3>\n            <p>&nbsp;</p>\n\n            <div class=\"columns\">\n                <div class=\"col\" style=\"flex-grow: 1;\">\n                  <ul>\n                      <li>Stay in sync automatically</li>\n                      <li>Reduce human error</li>\n                  </ul>\n                </div>\n\n                <div class=\"col\" style=\"flex-grow: 2;\">\n                    <img src=\"images/public-private-cloud.png\" alt=\"\" />\n                </div>\n            </div>\n\n\n            <aside class=\"notes\">\n                <p>Static inventory if fine when you’re starting out or managing a relatively small environment that doesn’t change often such as a development environment. Dynamic inventory is an imperative when is come to large environments especially dynamic ones using public/private/hybrid cloud technology.</p>\n                <p>Ansible ships with dynamic inventory scripts for most major cloud providers</p>\n                <p>Fairly easy to write your own to pull data from whatever database/text files you are using locally</p>\n            </aside>\n        </section>\n\n        <section>\n          <h2>Variables</h2>\n          <h3>Proper variable naming can make plays more readable and avoid variable name conflicts</h3>\n          <ul>\n            <li>Use descriptive, unique human-meaningful variable names</li>\n            <li>Prefix role variables with it’s \"owner\" such as a role name or package</li>\n          </ul>\n          <pre class=\"language-yaml\"><code>\n  apache_max_keepalive: 25\n  apache_port: 80\n  tomcat_port: 8080\n          </pre></code>\n\n          <aside class=\"notes\">\n            <p>All about increasing readability, therefore reducing possible bugs.</p>\n            <p>Human readable - that term again</p>\n            <p>Ansible by design has a flat naming space, to make it simple. Avoids issues around variable context &amp; scope as you see in Object Orientation</p>\n            <p>Makes it a lot easier to grep over the code base</p>\n            <p>Highlight how if we just used \"port\" you could end up configuring you could create port conflict when your Apache and Tomcat servers both try to listen to port 80. By prefixing the apache and tomcat port, they stay separate...</p>\n            <p>This is where having something in your style guide and some documentation comes in handy. </p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>Variables</h2>\n          <h3>Make the most of variables</h3>\n          <ul>\n            <li>Find the appropriate place for your variables based on what, where and when they are set or modified</li>\n            <li>Separate logic (tasks) from variables to reduce repetitive patterns and provided added flexibility.</li>\n          </ul>\n\n          <aside class=\"notes\">\n            <p>Shortcut/human readable variables to improve task declaration readability and clarity</p>\n\n            <p>Remember you can’t override a variable or parameter if it’s been embedded into a task. Let’s take a look at what I mean…</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>Separate Logic From Variables</h2>\n          <div class=\"columns\">\n            <div class=\"col\">\n              <pre class=\"language-yaml\"><code>\n  - name: Clone student lesson app for a user\n    host: nodes\n    tasks:\n      - name: Create ssh dir\n        file:\n          state: directory\n          path: /home/{{ username }}/.ssh\n\n      - name: Set Deployment Key\n        copy:\n          src: files/deploy_key\n          dest: /home/{{ username }}/.ssh/id_rsa\n\n      - name: Clone repo\n        git:\n          accept_hostkey: yes\n          clone: yes\n          dest: /home/{{ username }}/exampleapp\n          key_file: /home/{{ username }}/.ssh/id_rsa\n          repo: git@github.com:example/apprepo.git\n              </code></pre>\n            </div>\n\n            <div class=\"col\">\n              <h3>Exhibit A</h3>\n              <ul class=\"text-small\">\n                <li>Embedded parameter values and repetitive home directory value pattern in multiple places</li>\n                <li>Works but could be more clearer and setup to be more flexible and maintainable </li>\n              </ul>\n            </div>\n          </div>\n\n          <aside class=\"notes\">\n            <p>This works just fine however</p>\n\n            <ul>\n                <li>/home/… is repeated throughout. If changed it will require multiple edits. Yes we have bulk copy and replace.</li>\n                <li>{{ username }} - means a variable called username</li>\n                <li>Not clear what the params the play is working with without reading thru</li>\n                <li>Parameters like the local path to clone the repo cannot be modified without changing the play. This alone is the most valuable reason alone for doing this even if the others don’t matter. </li>\n            </ul>\n\n            <p>Sometimes embedding values is intended with good reason (you don’t want user to override something).</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>Separate Logic From Variables</h2>\n\n          <div class=\"columns\">\n            <div class=\"col\">\n              <pre class=\"language-yaml\"><code>\n  - name: Clone student lesson app for a user\n    host: nodes\n    vars:\n      user_home_dir: \"/home/{{ username }}\"\n      user_ssh_dir: \"{{ user_home_dir }}/.ssh\"\n      deploy_key: \"{{ user_ssh_dir }}/id_rsa\"\n      app_dir: \"{{ user_home_dir }}/exampleapp\"\n    tasks:\n      - name: Create ssh dir\n        file:\n          state: directory\n          path: \"{{ user_ssh_dir }}\"\n\n      - name: Set Deployment Key\n        copy:\n          src: files/deploy_key\n          dest: \"{{ deploy_key }}\"\n\n      - name: Clone repo\n        git:\n          dest: \"{{ app_dir }}\"\n          key_file: \"{{ deploy_key }}\"\n          repo: git@github.com:example/exampleapp.git\n          accept_hostkey: yes\n          clone: yes\n              </code></pre>\n            </div>\n\n            <div class=\"col\">\n              <h3>Exhibit B</h3>\n              <ul class=\"text-small\">\n                <li>Parameters values are set thru values away from the task and can be overridden.</li>\n                <li>Human meaningful variables \"document\" what’s getting plugged into a task parameter</li>\n                <li>More easily refactored into a role</li>\n              </ul>\n            </div>\n          </div>\n          <aside class=\"notes\">\n            <p>Here we have a refactored version of the previous example where logic (tasks) have been separated from variables as we’ve discussed. It’s a bit longer but more readable.</p>\n\n            <ul>\n                <li>Variables are separated out. We know what parameters are being feed to our tasks.</li>\n                <li>User home can be set once and used by other variables.</li>\n                <li>Human-meaningful variable names make what you’re feeding into each task parameter easier to understand.</li>\n                <li>You can easily override any of the variables to change the default behaviour of the play.</li>\n                <li>Just looking at the vars section tells you what parts of the system get changed</li>\n                <li>Bonus: Makes refactoring a play into a role easier - overide `user_home_dir`, or `app_dir` when calling the role and it will do the right thing</li>\n            </ul>\n\n            <p>INSERT COMMAND LINE EXAMPLE. Setup root user?</p>\n          </aside>\n        </section>\n\n        <section>\n            <h2>Plays & Tasks</h2>\n            <h3>Use native YAML syntax to maximize the readability of your plays</h3>\n            <ul>\n                <li>Vertical reading is easier</li>\n                <li>Supports complex parameter values</li>\n                <li>Works better with editor syntax highlighting in editors</li>\n            </ul>\n\n            <aside class=\"notes\">\n              <p>At its core, the Ansible playbook runner is a YAML parser with added logic such as commandline key=value pairs shorthand. While convenient when cranking out a quick playbook or a docs example, that style of formatting reduces readability. We recommend you refrain from using that shorthand (even with YAML folded style) as a best practice.</p>\n            </aside>\n        </section>\n\n        <section>\n            <h2>Use Native YAML Syntax</h2>\n            <h3>No!</h3>\n            <pre class=\"language-yaml\"><code>\n  - name: install telegraf\n    yum: name=telegraf-{{ telegraf_version }} state=present update_cache=yes disable_gpg_check=yes enablerepo=telegraf\n    notify: restart telegraf\n\n  - name: configure telegraf\n    template: src=telegraf.conf.j2 dest=/etc/telegraf/telegraf.conf\n\n  - name: start telegraf\n    service: name=telegraf state=started enabled=yes\n            </code></pre>\n\n            <aside class=\"notes\">\n              <p>Vertical scanning is easier to spot parameters and what is being passed.</p>\n              <p>Doesn't even fit on my slide</p>\n              <p>Reduces the amount of horizontal scrolling the eyes must do. Modules such as ec2 that require a lot of parameters create long lines scroll off the screen which is really inefficient and makes it hard to read.</p>\n              <p>Syntax highlighting is suboptimal -- Ansible overloads key=value notation onto the standard python YAML parser. A YAML parser sees all of those key value.</p>\n            </aside>\n        </section>\n\n        <section>\n            <h2>Use Native YAML Syntax</h2>\n            <h3>Better, but no</h3>\n            <pre class=\"language-yaml\"><code>\n- name: install telegraf\n  yum: >\n      name=telegraf-{{ telegraf_version }}\n      state=present\n      update_cache=yes\n      disable_gpg_check=yes\n      enablerepo=telegraf\n  notify: restart telegraf\n\n- name: configure telegraf\n  template: src=telegraf.conf.j2 dest=/etc/telegraf/telegraf.conf\n\n- name: start telegraf\n  service: name=telegraf state=started enabled=yes\n            </code></pre>\n\n            <aside class=\"notes\">\n              <p>Notice the greater-than symbol after yum</p>\n              <p>This refactored version of the last example uses what’s called YAML folded syntax to \"stack\" the first task with the line that ran off our screen. It’s an improvement in that it reduces horizontal scrolling and lines running off your screen, but it still breaks syntax highlighting and we can do better. If you are going this far with white spacing though why not go all the way?</p>\n            </aside>\n        </section>\n\n        <section>\n            <h2>Use Native YAML Syntax</h2>\n            <h3>Yes!</h3>\n            <pre class=\"language-yaml\"><code>\n  - name: install telegraf\n    yum:\n      name: telegraf-{{ telegraf_version }}\n      state: present\n      update_cache: yes\n      disable_gpg_check: yes\n      enablerepo: telegraf\n    notify: restart telegraf\n\n  - name: configure telegraf\n    template:\n      src: telegraf.conf.j2\n      dest: /etc/telegraf/telegraf.conf\n    notify: restart telegraf\n\n  - name: start telegraf\n    service:\n      name: telegraf\n      state: started\n      enabled: yes\n            </code></pre>\n\n            <aside class=\"notes\">\n              <p>Native YAML has more lines; however, those lines are shorter, reducing horizontal scrolling your eyes have to do and line wrapping in your editor. It lets the eyes scan straight down the play. </p>\n              <p>The task parameters are stacked and easily distinguished from the next. </p>\n              <p>Native YAML syntax also has the benefit of improved syntax highlighting in virtually any modern text editor out there. Being native YAML, editors such as vim and Atom will highlight YAML keys (module names, directives, parameter names) from their values further aiding the readability of your content. </p>\n              <p>NOTE: Some of our own docs use this shorthand for legacy reasons though we’re progressively changing that. (Documentation pull requests accepted.)</p>\n            </aside>\n        </section>\n\n        <section>\n            <h2>Plays & Tasks</h2>\n            <h3>Names improve readability and user feedback</h3>\n            <ul>\n                <li>Give all your playbooks, tasks and blocks brief, reasonably unique and human-meaningful names</li>\n            </ul>\n        </section>\n\n        <section>\n            <h2>Plays & Tasks</h2>\n            <h3>Exhibit A</h3>\n            <div class=\"columns\">\n                <div class=\"col\">\n                    <pre class=\"language-yaml\"><code>\n  - hosts: web\n    tasks:\n    - yum:\n        name: httpd\n        state: latest\n\n    - service:\n        name: httpd\n        state: started\n        enabled: yes\n                    </code></pre>\n                </div>\n\n                <div class=\"col\">\n                    <pre class=\"language-yaml\"><code>\n  PLAY [web]\n  ********************************\n\n  TASK [setup]\n  ********************************\n  ok: [web1]\n\n  TASK [yum]\n  ********************************\n  ok: [web1]\n\n  TASK [service]\n  ********************************\n  ok: [web1]\n                    </code></pre>\n                </div>\n            </div>\n\n            <aside class=\"notes\">\n                <ul>\n                    <li>Here we have a simple example of a working a play.</li>\n                    <li>It works fine but what is happening here?</li>\n                    <li>Is yum installing or removing something?</li>\n                    <li>Is service starting or shutting down something?</li>\n                    <li>What was this play supposed to do anyways?  If you just wrote this play you know now or maybe you forgot.</li>\n                    <li>If when running this something fails you'd have no idea from the output, you'd need to open the playbook</li>\n                </ul>\n            </aside>\n        </section>\n\n        <section>\n            <h2>Plays & Tasks</h2>\n            <h3>Exhibit B</h3>\n            <div class=\"columns\">\n                <div class=\"col\">\n                    <pre class=\"language-yaml\"><code>\n  - hosts: web\n    name: installs and starts apache\n    tasks:\n      - name: install apache packages\n        yum:\n          name: httpd\n          state: latest\n\n      - name: starts apache service\n        service:\n          name: httpd\n          state: started\n          enabled: yes\n                    </code></pre>\n                </div>\n\n                <div class=\"col\">\n                    <pre class=\"language-yaml\"><code>\n  PLAY [install and starts apache]\n  ********************************\n\n  TASK [setup]\n  ********************************\n  ok: [web1]\n\n  TASK [install apache packages]\n  ********************************\n  ok: [web1]\n\n  TASK [starts apache service]\n  ********************************\n  ok: [web1]\n                    </code></pre>\n                </div>\n            </div>\n\n            <aside class=\"notes\">\n                <ul>\n                    <li>Here is the same play with names.</li>\n                    <li>By adding descriptive human-meaningful names to the play declaration and tasks we get output that give you  and users of this automation better feedback and clarity to what is being performed.</li>\n                    <li>From the name of the Playbook, line 2, we know what the rest of the playbook will do without having to read it.</li>\n                    <li>Can help keep focus</li>\n              </ul>\n            </aside>\n        </section>\n\n        <section>\n          <h2>Plays & Tasks</h2>\n          <h3>Focus avoids complexity</h3>\n          <ul>\n            <li>Keep plays and playbooks focused. Multiple simple ones are better than having a huge single playbook full of conditionals.</li>\n            <li>Follow the Linux principle of do one thing, and one thing well</li>\n          </ul>\n\n          <aside class=\"notes\">\n            <p>Monoliths!</p>\n            <p>Multiple small roles</p>\n            <p>EXAMPLE: One customer had one playbook for the entire stack,thousands of lines long, crazy, depending on the variables passed in. Impossible to manage, maintain, or know what is going on</p>\n            <p>As your automation grows in sophistication and scope refactor your plays -- includes &amp; roles to compose automation workflows.</p>\n            <p>When refactoring run your roles in checkmode to see</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>Plays & Tasks</h2>\n          <h3>Clean up your debugging tasks</h3>\n          <ul>\n            <li>Make them optional with the verbosity parameter so they’re only displayed when they are wanted.</li>\n          </ul>\n\n          <pre class=\"language-yaml\"><code>\n  - debug:\n     msg: \"This always displays\"\n\n  - debug:\n     msg: \"This only displays with ansible-playbook -vv+\"\n     verbosity: 2\n          </code></pre>\n\n          <aside class=\"notes\">\n            <p>Debugging messages and variable dumps in your plays can be helpful when you are developing or debugging automation automation, but not all the time. Displaying debugging pollutes the standard usage with a lot of noise. It can also scare anxious end users that don’t trust this automation stuff when some JSON dump of information explodes onto their screens.</p>\n\n            <p>The verbosity parameter was added to the debug module in v2.1.</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>Plays & Tasks</h2>\n          <h3>Don’t just start services -- use smoke tests </h3>\n          <pre class=\"language-yaml\"><code>\n  - name: check for proper response\n    uri:\n      url: http://localhost/myapp\n      return_content: yes\n    register: result\n    until: '\"Hello World\" in result.content'\n    retries: 10\n    delay: 1\n          </code></pre>\n\n          <aside class=\"notes\">\n            <p>EXAMPLE: lock file, permissions, something else running on the port</p>\n            <p>Just because a service started or an instances has initialized -- maybe it hit an error initializing and crashed. Use modules like uri and wait_for to smoke test that they are ready to go.</p>\n            <p>reties +delays = polling, so if the server is ready soon we don't have to wait the full time, better than <span class=\"monospace\">sleep 10</span></p>\n</li>\n          </aside>\n        </section>\n\n        <section>\n          <h2>Plays & Tasks</h2>\n          <h3>Use run commands sparingly</h3>\n          <ul>\n              <li>Use the run command modules like <span class=\"monospace\">shell</span> and <span class=\"monospace\">command</span> as a last resort</li>\n              <li>The <span class=\"monospace\">command</span> module is generally safer</li>\n              <li>The <span class=\"monospace\">shell</span> module should only be used for I/O redirect</li>\n\n          </ul>\n\n          <aside class=\"notes\">\n            <p>Run commands are...</p>\n\n            <p>Explain how run command modules have no concept of state.  (See slide from essentials deck here.) </p>\n\n            <p>Command will escape redirection characters such a pipe so they cannot be embedded in a variable and used for something malicious. You really shouldn’t be using a variable provided by a user anyway.</p>\n            <p>Even more important when using privileges - security</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>Plays & Tasks</h2>\n          <h3>Always seek out a module first</h3>\n          <div class=\"columns\">\n            <div class=\"col\">\n              <pre class=\"language-yaml\"><code>\n  - name: add user\n    command: useradd appuser\n\n  - name: install apache\n    command: yum install httpd\n\n  - name: start apache\n    shell: |\n      service httpd start && chkconfig httpd on\n              </code></pre>\n            </div>\n\n            <div class=\"col\">\n              <pre class=\"language-yaml\"><code>\n  - name: add user\n    user:\n      name: appuser\n      state: present\n\n  - name: install apache\n    yum:\n      name: httpd\n      state: latest\n\n  - name: start apache\n    service:\n      name: httpd\n      state: started\n      enabled: yes\n              </code></pre>\n            </div>\n          </div>\n          <aside class=\"notes\">\n              <p>Just because you know how to do something in bash doesn’t mean you don’t need a module. Again, run commands have no sense of desired state. There is a lot going in the modules. A lot goes into making things easy. (FIXME Quote from under the hood deck here?)  The yum module doesn’t just issue a yum command. The service module sniffs out the host OS and even version to use the proper tools and switches.</p>\n              <p>Modules are split into categories and topics. Have a look at these categories: file, system, utils</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>Plays & Tasks</h2>\n          <h3>Still using run commands a lot?</h3>\n          <pre class=\"language-yaml\"><code>\n  - hosts: all\n    vars:\n      cert_store: /etc/mycerts\n      cert_name: my cert\n    tasks:\n    - name: check cert\n      shell: certify --list --name={{ cert_name }} --cert_store={{ cert_store }} | grep \"{{ cert_name }}\"\n      register: output\n\n    - name: create cert\n      command: certify --create --user=chris --name={{ cert_name }} --cert_store={{ cert_store }}\n      when: output.stdout.find(cert_name)\" != -1\n      register: output\n\n    - name: sign cert\n      command: certify --sign  --name={{ cert_name }} --cert_store={{ cert_store }}\n      when: output.stdout.find(\"created\")\" != -1\n          </code></pre>\n\n          <aside class=\"notes\">\n              <p>Here \"certify\" is a fictitious command line tool to illustrate an in-house private tool an organization may have developed for its own use.</p>\n              <p>This example is a good example of \"coding\" in your playbooks. That pattern being something like:</p>\n              <ol>\n                  <li>Perform a task and register its return and perhaps ignore any errors to handle them later.</li>\n                  <li>Parse/search the registered return as the parameter or conditional of a later task</li>\n                  <li>Rise and repeat</li>\n              </ol>\n              <p>Sometimes this cannot be avoided because a module doesn’t exist and you need something fast. In that case...</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>Plays & Tasks</h2>\n          <h3>Develop your own module</h3>\n          <pre class=\"language-yaml\"><code>\n- hosts: all\n   vars:\n     cert_store: /etc/mycerts\n     cert_name: my cert\n   tasks:\n   - name: create and sign cert\n     certify:\n       state: present\n       sign: yes\n       user: chris\n       name: \"{{ cert_name }}\"\n       cert_store: \"{{ cert_store }}\"\n          </code></pre>\n          See <a href=\"https://docs.ansible.com/ansible/devel/dev_guide/\">ansible.com/devel/dev_guide</a>\n\n          <aside class=\"notes\">\n              <p>Modules do the heavy lifting in Ansible.</p>\n              <p>Modules and playbooks work together to provide a simple and easy to read abstraction from the underlying complexity and sophistication that give Ansible it’s power</p>\n              <p>Modules provide access to the full power of a proper programming language (typically Python) and its ecosystem. </p>\n              <p>Also note, that you can (and should) do many operations in a module and present that as one task. The previous example’s function was more verbose, harder to read and was spread across multiple tasks.</p>\n              <p>Modules follow this CRUD (Create, read, update and delete) pattern typically.</p>\n              <p>https://docs.ansible.com/ansible/devel/dev_guide/</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>Plays & Tasks</h2>\n          <h3>Separate provisioning from deployment and configuration tasks</h3>\n          <p class=\"monospace\">\n            acme_corp/<br/>\n            ├── configure.yml<br/>\n            ├── provision.yml<br/>\n            └── site.yml<br/>\n            <br/>\n            $ cat site.yml<br/>\n            ---<br/>\n            - include: provision.yml<br/>\n            - include: configure.yml\n          </p>\n          <aside class=\"notes\">\n            <p>Helpful if you are using different cloud providers</p>\n            <p>Change cloud provider or virtualization framework</p>\n            <p>Use different provisioning in development vs production</p>\n          </aside>\n        </section>\n\n        <section>\n            <h2>Templates</h2>\n            <h3>Jinja2 is powerful but you needn't use all of it</h3>\n            <div class=\"columns\">\n                <div class=\"col\">\n                  <h4>Templates should be simple:</h4>\n                  <ul class=\"text-small\">\n                    <li>Variable substitution</li>\n                    <li>Conditionals</li>\n                    <li>Simple control structures/iterations</li>\n                    <li>Design your templates for your use case, not the world's</li>\n                  </ul>\n                </div>\n                <div class=\"col\">\n                  <h4>Things to avoid:</h4>\n                  <ul class=\"text-small\">\n                    <li>Anything that can be done directly in Ansible</li>\n                    <li>Managing variables in a template</li>\n                    <li>Extensive and intricate conditionals</li>\n                    <li>Conditional logic based on embedded hostnames</li>\n                    <li>Complex nested iterations</li>\n                  </ul>\n                </div>\n            </div>\n            <aside class=\"notes\">\n                <p>From Django project, widely used Python library</p>\n                <p>Expand upon Jinja2 being powerful. There is a bit of grey and a zen to deciding how to how to design your templates. Strive for simplicity. complexity kills.</p>\n                <p>avoid: anything that can be done directly in Ansible - such as groups</p>\n            </aside>\n        </section>\n\n        <section>\n            <h2>Templates</h2>\n            <h3>Identify generated files</h3>\n            <ul>\n              <li>Label template output files as being generated by Ansible</li>\n              <li>Consider using the ansible_managed variable with the comment filter</li>\n              <li>Especially important when you are starting out and not everything is Ansible managed</li>\n            </ul>\n            <p class=\"monospace\" style=\"margin-left: 2em;\">{{ ansible_managed | comment }}</p>\n\n            <aside class=\"notes\">\n              <p>This is useful to tell users that a file has been placed by Ansible and manual changes are likely to be overwritten.</p>\n              <p>Avoids someone manually changing a file and Ansible clobbering that change when it reruns</p>\n            </aside>\n        </section>\n\n        <section>\n            <h2>Roles</h2>\n            <ul>\n              <li>Like playbooks -- keep roles purpose and function focused</li>\n              <li>Use a <span class=\"monospace\">roles/</span> subdirectory for roles developed for organizational clarity in a single project</li>\n              <li>Follow the Ansible Galaxy pattern for roles that are to be shared beyond a single project</li>\n              <li>Limit role dependencies</li>\n            </ul>\n\n            <aside class=\"notes\">\n              <p>One \"thing\" per role — being able to understand the structure</p>\n              <p>Linux principle of do one thing, and one thing well</p>\n              <p>Loosely coupled</p>\n            </aside>\n        </section>\n\n        <section>\n            <h2>Roles</h2>\n            <ul>\n                <li>Use <span class=\"monospace\">ansible-galaxy init</span> to start your roles...</li>\n                <li>...then remove unneeded directories and stub files</li>\n                <li>Use <span class=\"monospace\">ansible-galaxy</span> to install your roles -- even private ones</li>\n                <li>Use a roles files (i.e. <span class=\"monospace\">requirements.yml</span>) to manifest any external roles your project is using</li>\n                <li>Always peg a role to a specific version such as a tag or commit</li>\n            </ul>\n            <aside class=\"notes\">\n                <p>EXAMPLE: Don't leave empty files</p>\n                <p>EXAMPLE: Peg versions</p>\n            </aside>\n        </section>\n\n        <section>\n            <h2>Scaling Your Ansible Workflow</h2>\n            <h3>Command line tools have their limitations</h3>\n            <ul>\n              <li>Coordination across distributed teams & organizations...</li>\n              <li>Controlling access to credentials...</li>\n              <li>Track, audit and report automation and management activity...</li>\n              <li>Provide self-service or delegation...</li>\n              <li>Integrate automation with enterprise systems...</li>\n            </ul>\n\n            <aside class=\"notes\">\n              <p>There are limits to a command line tool...</p>\n            </aside>\n        </section>\n\n        <section>\n            <h2>Recap</h2>\n            <ul>\n                <li>Complexity Kills Productivity</li>\n                <li>Optimize For Readability</li>\n                <li>Think Declaratively</li>\n            <aside class=\"notes\">\n                <p>Check remaining time</p>\n                <p>So I know after these presentations you sometimes think, \"I see what he's saying, but my setup is fairly horrible\" it \"I've got so much to do and not enough time\"</p>\n                <p>Story time</p>\n                <p>Backstory: every developer had a high spec blade for running VMs on</p>\n                <p>Whenever someone joined we just did a disk copy of the previous system, then hacked around in /etc</p>\n                <p>Oh, and a hundred blades in we upgraded from 10.04 to 12.04</p>\n                <p>Noticed a few issues: Fix playbooks for- ldap, ntp,</p>\n                <p>Common packages</p>\n                <p>Everytime we spotted an issue we wrote a small isolated playbook to fix it across the whole server estate</p>\n                <p>Did a fresh, preseed install, ran the fix roles, iterated till I had a working system</p>\n                <p>Desktops - Blade + gnome</p>\n                <p>India & Madrid - group_vars/india.yml</p>\n\n            </aside>\n        </section>\n        <section>\n            <h2>Thank You!</h2>\n            <ul>\n                <li>Complexity Kills Productivity</li>\n                <li>Optimize For Readability</li>\n                <li>Think Declaratively</li>\n            <aside class=\"notes\">\n            </aside>\n        </section>\n\n      </div>\n    </div>\n\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/lib/js/head.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/js/reveal.js\"></script>\n\n    <script>\n      // More info https://github.com/hakimel/reveal.js#configuration\n      Reveal.initialize({\n        history: true,\n        width: \"85%\",\n        height: \"90%\",\n        transition: \"fade\",\n\n        // More info https://github.com/hakimel/reveal.js#dependencies\n        // Notes plugin must remain local for now.\n        // See https://github.com/ansible/lightbulb/issues/125\n        dependencies: [\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/plugin/markdown/marked.js' },\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/plugin/markdown/markdown.js' },\n          { src: 'plugin/notes/notes.js', async: true },\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/prism.min.js'},\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/components/prism-yaml.min.js'}\n          //{ src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }\n        ]\n      });\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "decks/ansible-essentials.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">\n\n    <title>Ansible Essentials Workshop</title>\n\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/css/reveal.css\">\n\n    <!-- Printing and PDF exports -->\n    <script>\n      var link = document.createElement( 'link' );\n      link.rel = 'stylesheet';\n      link.type = 'text/css';\n      link.href = window.location.search.match( /print-pdf/gi ) ? 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/css/print/pdf.css' : 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/css/print/paper.css';\n      document.getElementsByTagName( 'head' )[0].appendChild( link );\n    </script>\n\n\n    <link rel=\"stylesheet\" href=\"css/theme/ansible.css\">\n\n    <!-- Theme used for syntax highlighting of code -->\n    <!--link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/lib/css/zenburn.css\"-->\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/themes/prism.min.css\">\n\n\n  </head>\n  <body>\n  <div class=\"ans-mark\">\n    <svg version=\"1.1\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" viewBox=\"-449 450 125 125\" style=\"enable-background:new -449 450 125 125;\" xml:space=\"preserve\">\n      <g id=\"XMLID_3_\">\n        <circle id=\"XMLID_7_\" class=\"circle\" cx=\"-386.5\" cy=\"512.5\" r=\"62\"/>\n        <path id=\"XMLID_4_\" class=\"a-mark\" d=\"M-356.9,537.1l-24.7-59.4c-0.7-1.7-2.1-2.6-3.9-2.6c-1.7,0-3.2,0.9-4,2.6l-27.1,65.2h9.2 l10.7-26.9l32,25.9c1.3,1,2.2,1.5,3.4,1.5c2.4,0,4.6-1.8,4.6-4.5C-356.5,538.5-356.6,537.8-356.9,537.1z M-385.4,488.4l16.1,39.6 l-24.2-19L-385.4,488.4z\"/>\n      </g>\n    </svg>\n  </div>\n    <div class=\"reveal\">\n      <div class=\"slides\">\n        <section data-state=\"cover\">\n          <p class=\"ans-logo\"><img src=\"images/ansible-wordmark-white.svg\" width=\"260\" alt=\"\" /></p>\n          <h1>Ansible Essentials Workshop</h1>\n          <!--p>NAME HERE, TITLE HERE</p>\n          <p>COMPANY HERE</p-->\n        </section>\n        <section>\n          <h2>What You Will Learn</h2>\n          <p>Ansible is capable of handling many powerful automation tasks with the flexibility to adapt to many environments and workflows. With Ansible, users can very quickly get up and running to do real work.<br><br></p>\n          <ul>\n            <li>What is Ansible and The Ansible Way</li>\n            <li>Installing Ansible</li>\n            <li>How Ansible Works and its Key Components</li>\n            <li>Ad-Hoc Commands</li>\n            <li>Playbook Basics</li>\n            <li>Reuse and Redistribution of Ansible Content with Roles</li>\n          </ul>\n          <aside class=\"notes\">\n            <p>This deck is designed to provide students with direct introductory instruction and guidance to beginning to automate with Ansible. It is the starting point for students intent on becoming more proficient with Ansible through other Lightbulb modules and their own usage.</p>\n            <p>This deck supports lecture and hands-on forms of presenting this material. </p>\n            <p>Allow 2 hours to deliver the lecture-based form and 4 hours for stopping to do the workshop assignments. To access the additional slides for delivering the workshops, navigate down when available. </p>\n            <p>See the <a href=\"../facilitator/README.md\">Ansible Lightbulb facilitator&rsquo;s guide</a> for more details on using this deck and it&rsquo;s associated material.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>What is Ansible?</h2>\n          <p>It&apos;s a <b>simple automation language</b> that can perfectly describe an IT application infrastructure in Ansible Playbooks.</p>\n          <p>It&apos;s an <b>automation engine</b> that runs Ansible Playbooks.</p>\n          <p>Ansible Tower is an <b>enterprise framework</b> for controlling, securing  and managing your Ansible automation with a <b>UI and RESTful API</b>.</p>\n          <aside class=\"notes speaker\">\n            <p>Ansible is an automation platform:\n              <ul>\n                <li>Playbooks make up the automation language</li>\n                <li>The code base is the automation engine.</li>\n                <li>Ansible Tower manages existing automation</li>\n              </ul>\n            </p>\n          </aside>\n        </section>\n        <section>\n            <h2>Ansible Is...</h2>\n            <img src=\"images/simple-powerful-agentless-diagram.svg\" />\n            <aside class=\"notes\">\n              <p>Ansible has a number of qualities that make it the most rapidly growing automation platform in the world. </p>\n              <p><strong>Ansible is simple.</strong> Playbooks are human and machine readable, no special coding skills required &ndash; and even people in your IT organization that don’t know Ansible can read an Ansible playbook and understand what’s happening.</p>\n              <p>This simplicity also means that it&rsquo;s easy to install and get started to do real work with it quickly &ndash; usually in just minutes. </p>\n              <p>Ansible also works like you think &ndash; tasks are always executed in order. All together, the simplicity ensures that you can get started quickly.</p>\n              <p><strong>Ansible is powerful.</strong> Simplicity is great, but to be really useful, you also need the powerful features that ensure you can model even the most complex of IT workflows.</p>\n              <p>Ansible is complete automation, able to deploy apps, manage orchestration, and configure the infrastructure, networks, operating systems, and services that you’re already using today. </p>\n              <p>Together, Ansible’s capabilities allow you to orchestrate the entire application and environment lifecycle, regardless of where It&apos;s deployed.</p>\n              <p><strong>Ansible is Agentless.</strong>  Ansible relies on industry-standard and trusted SSH and WinRM protocols to automate. There are no agents or other software to install, and no additional firewall ports to open. With no need to separately stand up a management infrastructure, Ansible further reduces the activation energy required from your team to start automating today.</p>\n              <p>In a world where IT complexity stymies even the most basic of IT tasks, Ansible provides a much needed respite &ndash; and path forward enabling teams to crush productivity-stealing complexity and overhead.</p>\n            </aisde>\n        </section>\n        <section>\n            <h2>The Ansible Way</h2>\n            <div style=\"font-size: 0.75em;\"><br>\n            <p><strong>CROSS PLATFORM</strong> – Linux, Windows, UNIX</br>\n            Agentless support for all major OS variants, physical, virtual, cloud and network</p>\n            <p><strong>HUMAN READABLE</strong> – YAML</br>\n            Perfectly describe and document every aspect of your application environment</p>\n            <p><strong>PERFECT DESCRIPTION OF APPLICATION</strong></br>\n            Every change can be made by playbooks, ensuring everyone is on the same page</p>\n            <p><strong>VERSION CONTROLLED</strong></br>\n            Playbooks are plain-text. Treat them like code in your existing version control.</p>\n            <p><strong>DYNAMIC INVENTORIES</strong></br>\n            Capture all the servers 100% of the time, regardless of infrastructure, location, etc.</p>\n            <p><strong>ORCHESTRATION THAT PLAYS WELL WITH OTHERS</strong> – HP SA, Puppet, Jenkins, RHNSS, etc. Homogenize existing environments by leveraging current toolsets and update mechanisms.</p>\n            </div>\n            <aside class=\"notes\">\n            </aside>\n        </section>\n        <section>\n          <h2>Ansible: The Language of DevOps</h2>\n          <div style=\"font-size: 0.75em; text-align:center;\">\n          <img src=\"images/devops-language-diagram.svg\" width=\"60%\" height=\"60%\" style=\"padding-top: 20px;\"/>\n          <p class=\"fullwidth\"><strong>COMMUNICATION IS THE KEY TO DEVOPS.</strong></p>\n          <p class=\"fullwidth\">Ansible is the first <strong>automation language</strong><br/>that can be read and written across IT.</p>\n          <p class=\"fullwidth\">Ansible is the only <strong>automation engine</strong><br/>that can automate the entire <strong>application lifecycle</strong><br/>and <strong>continuous delivery pipeline</strong>.</p>\n          </div>\n          <aside class=\"notes\">\n          </aside>\n        </section>\n        <section>\n          <h2>Batteries Included</h2>\n          <p>Ansible comes bundled with hundreds of modules for a wide variety of automation tasks</p>\n          <div class=\"columns\">\n              <div class=\"col\">\n                <ul>\n                 <li>cloud</li>\n                 <li>containers</li>\n                 <li>database</li>\n                 <li>files</li>\n                 <li>messaging</li>\n                 <li>monitoring</li>\n                 <li>network</li>\n               </ul>\n              </div>\n              <div class=\"col\">\n                <ul>\n                  <li>notifications</li>\n                  <li>packaging</li>\n                  <li>source control</li>\n                  <li>system</li>\n                  <li>testing</li>\n                  <li>utilities</li>\n                  <li>web infrastructure</li>\n                </ul>\n              </div>\n              <div class=\"col\">\n              </div>\n          </div>\n          <aside notes=\"notes\">\n            <p>Ansible Modules control the things that you’re automating. They can do everything from acting on system files, installing packages, or making API calls to a service framework. Ansible ships with over 1300 today -- and this number is always expanding with every release.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Community</h2>\n          <div style=\"font-size: 0.75em;\">\n            <p><strong>THE MOST POPULAR OPEN-SOURCE AUTOMATION COMMUNITY ON GITHUB<br><br></strong></p>\n            <div class=\"columns\">\n              <div class=\"col\">\n                <ul>\n                  <li>34,000+ stars &amp; 10,000+ forks on GitHub</li>\n                  <li>4000+ GitHub Contributors</li>\n                  <li>Over 2000 modules shipped with Ansible</li>\n                  <li>New contributors added every day</li>\n                  <li>1200+ users on IRC channel</li>\n                  <li>Top 10 open source projects in 2017</li>\n                  <li>World-wide meetups taking place every week</li>\n                  <li>Ansible Galaxy: over 18,000 subscribers</li>\n                  <li>250,000+ downloads a month</li>\n                  <li>AnsibleFest and Ansible Automates events across the globe</li>\n                </ul>\n                <p>http://ansible.com/community</p>\n              </div>\n              <div class=\"col\">\n                  <img src=\"images/ansible-up-and-running-ebook.png\"/>\n              </div>\n          </div>\n        </div>\n        <aside class=\"notes\">\n          <p>Ansible is open source. Created with contributions from an active open source community and built for the people who use it every day. At its heart, Ansible was made to help more people experience the power of automation so they could work better and faster together.</p>\n        </aside>\n        </section>\n        <section>\n          <h2>Complete Automation</h2>\n          <img src=\"images/ansible-automation-diagram.svg\"/>\n          <aside class=\"notes\">\n            <p>Ansible is complete automation. It&apos;s not just configuration management, It&apos;s provisioning infrastructure, deploying applications, and orchestrating actions against all layers of IT infrastructure in a single tool.</p>\n            <p>Ansible's capabilities allow you to orchestrate the entire application and environment lifecycle, regardless of where It&apos;s deployed.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Use Cases</h2>\n          <img src=\"images/ansible-use-case-diagram.svg\" />\n          <aside class=\"notes\">\n            <p>What can you do with Ansible? Nearly anything. Ansible is the Swiss Army knife of DevOps, capable of handling many powerful automation tasks with the flexibility to adapt to many environments and workflows.</p>\n            <p>Many folks like to categorize Ansible as a configuration manager, and although yes, Ansible can do that, it&quot;s just the tip of the iceberg. When you couple configuration management with orchestration, you can start to model complicated multi-tier deployments with ease.</p>\n            <p>With Ansible, once someone on your team automates something, everyone on the team now knows how to do it.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Installing Ansible</h2>\n          <pre class=\"language-yaml\"><code>\n# the best way to install Ansible on\n# CentOS, RHEL, or Scientific Linux\n# is to configure the EPEL repository\n# and install Ansible directly\n$ sudo yum install ansible\n\n# on Debian or Ubuntu you will need the PPA\n# repo configured\n$ sudo apt-get install ansible\n\n# on all other platforms it can be\n# installed via pip\n$ sudo pip install ansible\n          </code></pre>\n          <aside class=\"notes\">\n            <p>As open source, Ansible is freely-available thru numerous means and can be installed in minutes with only a few requirements that most system already have. The installation methods listed here are the most commonly used.</p>\n            <p>Currently Ansible can be run from any machine with Python 2.6 or 2.7 installed. Python 3 support is in tech preview as of version 2.2. Windows isn't supported for the control machine.</p>\n            <p>The requirements of nodes being managed by Ansible vary based on the type and access used to work with them. Ansible needs a way to communicate, which is normally ssh or winrm though other means such as RESTful APIs and specialized connection types may be necessary. Linux servers will need Python 2.6 or later. Windows serves need PowerShell 3.</p>\n            <p>For more details see <a href=\"http://docs.ansible.com/ansible/intro_installation.html\">the Installation page</a> in the Ansible documentation.\n          </aside>\n        </section>\n        <section>\n          <section data-state=\"title alt\">\n          <h1>Demo Time: <br/>Installing Ansible</h1>\n          <aside class=\"notes\">\n            <p>To demonstrate how easy it is to install Ansible, open an SSH session to your control host and install Ansible using one of the methods in the previous slide. Once complete do a <code>$ ansible --version</code>.</p>\n          </aside>\n          </section>\n          <section data-state=\"title alt\">\n            <h1>Workshop: <br/>Installing Ansible</h1>\n            <aside class=\"notes\">\n              <p>This brief exercise demonstrates how easy it can be to install and configure Ansible and begin automating.</p>\n              <p>See <code>workshops/ansible_install</code> in the Ansible Lightbulb repo for this workshop&apos;s assignment. It's solution and addition details can be found in <code>facilitator/solutions/ansible_install.md</code>.</p>\n            </aside>\n          </section>\n        </section>\n        <section>\n          <h2>How Ansible Works</h2>\n          <img src=\"images/how-ansible-works-diagram-01.svg\" />\n          <aside class=\"notes\">\n            <p>The diagram on this slide shows the relationship between all the key components of Ansible starting with the user who writes an Ansible playbook.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Plays &amp; Playbooks</h2>\n          <img src=\"images/how-ansible-works-diagram-02.svg\" />\n          <aside class=\"notes\">\n            <p>Playbooks are written in YAML and are used to invoke Ansible modules to perform tasks that are executed sequentially i.e top to bottom. They can describe a policy you want your remote systems to enforce, or a set of steps in a general IT workflow. Playbooks are like an instruction manual and describe the state of environment.</p>\n            <p>For more details see <a href=\"http://docs.ansible.com/ansible/playbooks.html\">the Playbook page</a> in the Ansible documentation.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Modules &amp; Tasks</h2>\n          <img src=\"images/how-ansible-works-diagram-03.svg\" />\n          <aside class=\"notes\">\n            <p>If playbooks are the instruction manual for setting up and managing your infrastructure, Ansible modules are the tools in your toolkit.</p>\n            <p>Modules are executable bits of code that operate on hosts; however, we don’t need to understand the underlying implementation to get them to work. Modules do the heavy-lifting in Ansible and abstract users from the complexity of the underlying details.</p>\n            <p>For more details see the <a href=\"http://docs.ansible.com/ansible/modules_intro.html\">Introduction to Modules</a> and <a href=\"http://docs.ansible.com/ansible/modules_by_category.html\">Module Index</a> page in the Ansible documentation.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Plugins</h2>\n          <img src=\"images/how-ansible-works-diagram-04.svg\" />\n          <aside class=\"notes\">\n            <p>Continuing our metaphor, plugins are the gears in the engine.</p>\n            <p>Plugins are pieces of code that extend Ansible’s core functionality. Ansible ships with a number of handy plugins, and you can easily write your own.</p>\n            <p>These are some of the more common plugin types:</p>\n            <ul>\n            <li>Action plugins manage the execution on the controller and deployment of modules to hosts.</li>\n            <li>Callback plugins enable you to hook into Ansible events for display or logging purposes.</li>\n            <li>Connection plugins define how to communicate with inventory hosts.</li>\n            <li>Filters plugins allow you to manipulate data inside Ansible plays and/or templates. This is a Jinja2 feature; Ansible ships extra filter plugins.</li>\n            </ul>\n            <p>For more details see the <a href=\"http://docs.ansible.com/ansible/dev_guide/developing_plugins.html\">Developing Plugins</a> page in the Ansible documentation.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Inventory</h2>\n          <img src=\"images/how-ansible-works-diagram-05.svg\" />\n          <aside class=\"notes\">\n            <p>Your inventory of hosts are your raw material. They are a list of nodes and associated meta data that Ansible can automate.</p>\n            <p>Inventory lists can be built and stored several different ways, including static files, or can be dynamically-generated from an external source.</p>\n            <p>You can also specify variables as part of an inventory list. For instance, set a particular host key that’s needed to log into that system remotely.  Inventories are ultimately lists of things you want to automate across.</p>\n            <p>Here in this slide was see an example of a simple static inventory list of three hosts (webserver1, webserver2 and dbserver1) in two groups (web and db).</p>\n            <p>For more details see the <a href=\"http://docs.ansible.com/ansible/intro_inventory.html\">Inventory</a> page in the Ansible documentation.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Inventory</h2>\n          <img src=\"images/how-ansible-works-diagram-06.svg\" />\n          <aside class=\"notes\">\n            <p>In large-scale environment subject to constant change, synchronizing and maintaining inventory statically is tedious and error prone endeavor. That is why Ansible includes support of external sources such as public and private cloud providers and configuration management database (CMDB) systems.</p>\n            <p>For more details see the <a href=\"http://docs.ansible.com/ansible/intro_dynamic_inventory.html\">Dynamic Inventory</a> page in the Ansible documentation.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Modules</h2>\n          <p>Modules are bits of code transferred to the target system and executed to satisfy the task declaration.</p>\n          <div class=\"columns\">\n            <div class=\"col\">\n              <ul>\n                <li>apt/yum</li>\n                <li>copy</li>\n                <li>file</li>\n                <li>get_url</li>\n                <li>git</li>\n                <li>ping</li>\n                <li>debug</li>\n              </ul>\n            </div>\n            <div class=\"col\">\n              <ul>\n                <li>service</li>\n                <li>synchronize</li>\n                <li>template</li>\n                <li>uri</li>\n                <li>user</li>\n                <li>wait_for</li>\n                <li>assert</li>\n              </ul>\n            </div>\n              <div class=\"col\">\n              </div>\n          </div>\n          <aside class=\"notes\">\n            <p>If playbooks are the instruction manual for setting up and managing your infrastructure, Ansible modules are the tools in your toolkit.</p>\n            <p>We've already discussed, Ansible modules. They are the &ldquo;batteries&rdquo; and the &ldquo;tools in a users toolkit.&rdquo;</p>\n            <p>While there are hundreds of modules at your disposal out-of-the-box these are the most common ones.</p>\n            <p>Playbook tasks and how they relate to modules will be covered ahead. Tasks are the application of a module to perform a specific unit of work.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Modules Documentation</h2>\n            <div class=\"columns\">\n              <div class=\"col\">\n          <p><strong>https://docs.ansible.com/ansible/latest/modules/modules_by_category.html</strong></p>\n                </div>\n                <div class=\"col\">\n          <img src=\"images/modules-doc-screenshots.png\" />\n                </div>\n            </div>\n          <aside class=\"notes\">\n            <p><a href=\"http://docs.ansible.com/ansible/modules_by_category.html\">A categorized index of included Ansible modules</a> along with detailed documentation for the requirements, parameters and return values of each Ansible module can be found on the <a href=\"http://docs.ansible.com/ansible/\">Ansible documentation site</a>. </p>\n          </aside>\n        </section>\n        <section>\n          <h2>Modules Documentation</h2>\n          <pre class=\"language-yaml\"><code>\n# List out all modules installed\n$ ansible-doc -l\n...\ncopy\ncron\n...\n\n# Read documentation for installed module\n$ ansible-doc copy\n> COPY\n\n  The [copy] module copies a file on the local box to remote locations. Use the [fetch] module to copy files from remote locations to the local\n  box. If you need variable interpolation in copied files, use the [template] module.\n\n  * note: This module has a corresponding action plugin.\n\nOptions (= is mandatory):\n...\n          </code></pre>\n        </section>\n        <aside class=\"notes\">\n          <p>Module documentation is also available from the commandline using the <code>ansible-doc</code>.</p>\n          <p>One noteworthy advantage, of ansible-doc is that it can display any custom module with it's own embedded documentation that you may have added to your Ansible environment.</p>\n        </aside>\n        <section>\n          <h2>Modules: Run Commands</h2>\n          <p>If Ansible doesn't have a module that suits your needs there are the “run command” modules:</p><br>\n          <ul>\n            <li><b>command</b>: Takes the command and executes it on the host. The most secure and predictable.</li>\n            <li><b>shell</b>: Executes through a shell like <code>/bin/sh</code> so you can use pipes etc. Be careful.</li>\n            <li><b>script</b>: Runs a local script on a remote node after transferring it.</li>\n            <li><b>raw</b>: Executes a command  without going through the Ansible module subsystem.</li>\n          </ul>\n          <p><br><b>NOTE:</b> Unlike standard modules, run commands have no concept of desired state and should only be used as a last resort.</p>\n          <aside class=\"notes\">\n            <p>&quot;Run commands&quot; are what we collectively call these modules that enable users to do command-line operations in different ways. They’re a great catch all mechanism for getting things done, but they should be used sparingly and as a last resort. The reasons are many and varied.</p>\n            <p>The overuse of run commands is common amongst those just becoming familiar with Ansible for automating their work. They use <code>shell</code> to fire off a bash command they already know without stopping to look at the Ansible docs. That works well enough initially, but it undermines the value of automating with Ansible and sets things up for problems down the road. <b>As a best practice, always check the hundreds of Ansible shipping modules for what you need and use those first and run commands as a last resort.</b></p>\n            <p><b>NOTE:</b> <code>shell</code> allows for IO redirection such as pipes. This is why It&apos;s best practice to use <code>command</code> unless they need to pipe something. It also best practice to <strong>never</strong> pass user input or variables thru a run command.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Inventory</h2>\n          <p>Inventory is a collection of hosts (nodes) with associated data and groupings that Ansible can connect and manage.</p>\n          <ul>\n            <li>Hosts (nodes)</li>\n            <li>Groups</li>\n            <li>Inventory-specific data (variables)</li>\n            <li>Static or dynamic sources</li>\n          </ul>\n          <aside class=\"notes\">\n            <p>We've already discussed inventory in our review of Ansible's key components.</p>\n            <p>Inventory consists of hosts, groups, inventory specific data. Inventory can either be static or dynamic.</p>\n            <p>Inventory is a collection of the hosts (nodes) with associated metadata and groupings that Ansible can connect and manage. An inventory source can be static files or dynamically retrieved from an external system.</p>\n            <p>You can specify a different inventory file using the <code>-i &lt;path&gt;</code> option on the command-line or your Ansible configuration file.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Static Inventory Example</h2>\n          <pre class=\"language-yaml\"><code>\n10.42.0.2\n10.42.0.6\n10.42.0.7\n10.42.0.8\n10.42.0.100\nhost.example.com\n          </code></pre>\n          <aside class=\"notes\">\n            <p>Static inventory is the easiest source to get started with. This example shows a static inventory source in It&rsquo;s simplest form &ndash; a single file with a list of IP addresses or hostnames.</p>\n            <p><strong>NOTE:</strong> Ansible infers a localhost is present even if it is not explicitly listed. This is why you can actually run Ansible without a inventory source. Ansible will only be able to operate on the localhost where it is being run.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Static Inventory Example</h2>\n          <pre class=\"language-yaml\"><code>\n[control]\ncontrol ansible_host=10.42.0.2\n\n[web]\nnode-[1:3] ansible_host=10.42.0.[6:8]\n\n[haproxy]\nhaproxy ansible_host=10.42.0.100\n\n[all:vars]\nansible_user=vagrant\nansible_ssh_private_key_file=~/.vagrant.d/insecure_private_key\n          </code></pre>\n          <aside class=\"notes\">\n            <p>The example shown here is a more practical and common example of a static inventory file.</p>\n            <p>Static inventory files are expressed in INI format. The headings in brackets are group names, which are used in classifying systems and deciding what systems you are controlling at what times and for what purpose. Hosts can belong to multiple groups and groups can be members of other groups. (The latter is not shown here.)</p>\n            <p>NOTE: This example contains variables, a topic we haven't touched on just yet. We'll go into them on the next slide.</p>\n            <p>Other noteworthy attributes in our example:</p>\n            <ul>\n              <li><code>ansible_host</code> is an example of a host variable.</li>\n              <li>Hosts can be assigned arbitrary human-meaningful names or aliases such as \"control\" and \"haproxy\". Ansible will instead use the value of <code>ansible_host</code> (one of several <a href=\"http://docs.ansible.com/ansible/playbooks_variables.html#magic-variables-and-how-to-access-information-about-other-hosts\">&quot;magic&quot; inventory variables</a>) as the network address to connect.</li>\n              <li>Static inventory files can support character ranges such as &quot;node-[1:3]&quot;</li>\n              <li>Like localhost, Ansible infers an &quot;all&quot; group is present. As its name implies, all inventory hosts are members of this group.</li>\n              <li>The heading &quot;all:vars&quot; is an example of group variable assignment using two more &quot;magic&quot; variables, <code>ansible_ssh_private_key_file</code> and <code>ansible_user</code>.</li>\n            </ul>\n            <p>This example just covers the basics. There's a lot more to inventory that is not covered here. See the <a href=\"http://docs.ansible.com/ansible/intro_inventory.html\">Ansible inventory documentation</a> for more details.</p>\n          </aside>\n        </section>\n        <section>\n          <section>\n            <h2>Ad-Hoc Commands</h2>\n            <p>An ad-hoc command is a single Ansible task to perform quickly, but don’t want to save for later.</p>\n            <aside class=\"notes\">\n              <p>Ansible ad-hoc commands is a good place to start to understand the basics of what Ansible can do before learning how to use playbooks &ndash; ad-hoc commands can also be used to do quick things that you might not necessarily want to write a full playbook for.</p>\n              <p>Generally speaking, the true power of Ansible lies in playbooks. So why would you use ad-hoc tasks versus playbooks?</p>\n              <p>For instance, if you wanted to restart a service on all of your lab nodes, you could execute a quick one-liner in Ansible without writing a playbook.</p>\n            </aside>\n          </section>\n          <section>\n            <h2>Ad-Hoc Commands: Common Options</h2>\n            <ul style=\"font-size: 0.75em;\">\n              <li><b>-m MODULE_NAME, --module-name=MODULE_NAME</b><br/>Module name to execute the ad-hoc command</li>\n              <li><b>-a MODULE_ARGS, --args=MODULE_ARGS</b><br/>Module arguments for the ad-hoc command</li>\n              <li><b>-b, --become</b><br/>Run ad-hoc command with elevated rights such as sudo, the default method</li>\n              <li><b>-e EXTRA_VARS, --extra-vars=EXTRA_VARS</b><br/>Set additional variables as key=value or YAML/JSON</li>\n              <li><b>--version</b><br/>Display the version of Ansible</li>\n              <li><b>--help</b><br/>Display the MAN page for the Ansible tool</li>\n            </ul>\n            <aside class=\"notes\">\n              <p>This slide shows essential command-line options for running ad-hoc commands that will be useful in our upcoming workshop assignment.</p>\n            </aside>\n          </section>\n        </section>\n        <section>\n          <h2>Ad-Hoc Commands</h2>\n          <pre class=\"language-yaml\"><code>\n# check all my inventory hosts are ready to be\n# managed by Ansible\n$ ansible all -m ping\n\n# collect and display the discovered facts\n# for the localhost\n$ ansible localhost -m setup\n\n# run the uptime command on all hosts in the\n# web group\n$ ansible web -m command -a &quot;uptime&quot;\n          </code></pre>\n          <aside class=\"notes\">\n            <p>Ad-hoc commands are quick checks on your servers that you don’t want to preserve in an Ansible playbook.</p>\n            <p>An ad-hoc command can be used to do some tasks that you might not necessarily want to write a full playbook and save for later.</p>\n            <p>This is a good place to start to understand the basics of what Ansible can do prior to learning about playbooks where the true power of Ansible automation lies.</p>\n            <p>For more information see <a href=\"http://docs.ansible.com/ansible/intro_adhoc.html\">Introduction To Ad-Hoc Commands</a>.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Sidebar: Discovered Facts</h2>\n          <p>Facts are bits of information derived from examining a host systems that are stored as variables for later use in a play.</p>\n          <pre class=\"language-yaml\"><code>\n$ ansible localhost -m setup\nlocalhost | success >> {\n  \"ansible_facts\": {\n      \"ansible_default_ipv4\": {\n          \"address\": \"192.168.1.37\",\n          \"alias\": \"wlan0\",\n          \"gateway\": \"192.168.1.1\",\n          \"interface\": \"wlan0\",\n          \"macaddress\": \"c4:85:08:3b:a9:16\",\n          \"mtu\": 1500,\n          \"netmask\": \"255.255.255.0\",\n          \"network\": \"192.168.1.0\",\n          \"type\": \"ether\"\n      },\n          </code></pre>\n          <aside class=\"notes\">\n            <p>The second ad-hoc command example from the prior slide provides the following JSON output of a localhost facts run.</p>\n            <p>Facts are bits of information derived from examining a host systems that are stored as variables for later use. An example of this might be the IP address of the host, or what operating system it is running. The facts Ansible will discover about a host is extensive. What's shown here is just a small sample. Run <code>ansible localhost -m setup</code> for a more complete representation.</p>\n            <p>Ansible collects facts using the <code>setup</code> module. By default, Ansible will run the <code>setup</code> module before any other tasks are executed in a playbook. These facts can be referenced by subsequent automation tasks on a per host-basis during the playbook run.</p>\n          </aside>\n        </section>\n        <section>\n          <section data-state=\"title alt\">\n            <h1>Demo Time: <br/>Ad-Hoc Commands</h1>\n            <aside class=\"notes\">\n              <p>To demonstrate ad-hoc commands in action, open an SSH session to your control host and run the ad-hoc commands in the previous slides.</p>\n              <pre class=\"language-yaml\"><code>\n$ ansible all -m ping\n$ ansible localhost -m setup\n$ ansible web -m command -a &quot;uptime&quot;\n              </code></pre>\n            </aside>\n          </section>\n          <section data-state=\"title alt\">\n            <h1>Workshop: <br/>Ad-Hoc Commands</h1>\n            <aside class=\"notes\">\n              <p>This brief exercise demonstrates Ansible in-action at it&apos;s most basic and simple level. Thru ad-hoc commands, students are exposed to Ansible modules and usage and will apply to their understanding of tasks and playbooks. This exercise also begins to expose students to the concepts of Ansible facts and inventory.</p>\n              <p>This workshop is also a good way to verify their lab environments are properly configured before going forward.</p>\n              <p>See <code>workshops/adhoc_commands</code> in the Ansible Lightbulb repo.</p>\n            </aside>\n          </section>\n        </section>\n        <section>\n          <h2>Variables</h2>\n          <p>Ansible can work with metadata from various sources and manage their context in the form of variables.</p>\n          <ul>\n            <li>Command line parameters</li>\n            <li>Plays and tasks</li>\n            <li>Files</li>\n            <li>Inventory</li>\n            <li>Discovered facts</li>\n            <li>Roles</li>\n          </ul>\n          <aside class=\"notes\">\n            <p>While automation exists to make it easier to make things repeatable, all of your systems are not exactly alike.</p>\n            <p>Variables are used to store metadata for each host drawing from numerous sources. For example variable may be for things like facts or file paths or package versions.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Variable Precedence</h2>\n          <p>The order in which the same variable from different sources will override each other.</p>\n          <div class=\"columns\">\n            <div class=\"col\">\n              <ol>\n                <li>extra vars</li>\n                <li>task vars (only for the task)</li>\n                <li>block vars (only for tasks in block)</li>\n                <li>role and include vars</li>\n                <li>play vars_files</li>\n                <li>play vars_prompt</li>\n                <li>play vars</li>\n                <li>set_facts</li>\n              </ol>\n            </div>\n            <div class=\"col\">\n              <ol start=\"9\" class=\"col\">\n                <li>registered vars</li>\n                <li>host facts</li>\n                <li>playbook host_vars</li>\n                <li>playbook group_vars</li>\n                <li><strong>inventory host_vars</strong></li>\n                <li><strong>inventory group_vars</strong></li>\n                <li>inventory vars</li>\n                <li>role defaults</li>\n              </ol>\n            </div>\n          </div>\n          <aside class=\"notes\">\n            <p>If variables of the same name are defined in multiple sources, they get overwritten in a certain and specific order. This is why this variable precedence is important to understand.</p>\n            <p>There are 16 levels of variable precedence as of Ansible 2.x. The <code>extra_vars</code> (passed thru the command-line) always take precedence vs. role defaults which will always get overridden by any other source. The previous inventory example defines vars at 13 and 14 (highlighted in the list) in the variable precedence chain.</p>\n            <p>It&apos;s a good idea to limit the different sources where a specific variable is being set. While Ansible's variable precedence handling is comprehensive and well-defined, it can laborious to keep the resolution of multiple sources straight.</p>\n            <p>NOTE: The inventory variables sources showed in the static inventory example a couple of slides back are in bold type.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Tasks</h2>\n          <p>Tasks are the application of a module to perform a specific unit of work.</p>\n          <ul>\n            <li><b>file</b>: A directory should exist</li>\n            <li><b>yum</b>: A package should be installed</li>\n            <li><b>service</b>: A service should be running</li>\n            <li><b>template</b>: Render a configuration file from a template</li>\n            <li><b>get_url</b>: Fetch an archive file from a URL</li>\n            <li><b>git</b>: Clone a source code repository</li>\n          </ul>\n          <aside class=\"notes\">\n            <p>We've already reviewed modules, the batteries and tools Ansible provides. Tasks are the specific application of a module to perform a unit of automation.</p>\n            <p>Here we see a list of examples of common modules being applied to do something.</p>\n          <aside>\n        </section>\n        <section>\n          <h2>Example Tasks in a Play</h2>\n          <pre class=\"language-yaml\"><code>\ntasks:\n- name: Ensure httpd package is latest\n  yum:\n    name: httpd\n    state: latest\n\n- name: Ensure latest index.html file is present\n  copy:\n    src: files/index.html\n    dest: /var/www/html/\n\n- name: Restart httpd\n  service:\n    name: httpd\n    state: restarted\n          </code></pre>\n          <aside class=\"notes\">\n            <p>This example shows the task list of an Ansible playbook.</p>\n            <p>\n              <ol>\n                <li>The first task assures the latest nginx package is installed using yum.</li>\n                <li>The next uses the copy module to assure the latest version of a static home page file has been published.</li>\n                <li>The last task restarts the nginx service with the service module</li>\n              </ol>\n            </p>\n          </aside>\n        </section>\n        <section>\n          <h2>Handler Tasks</h2>\n          <p>Handlers are special tasks that run at the end of a play if notified by another task when a change occurs.</p>\n          <blockquote>If a package gets installed or updated, notify a service restart task that it needs to run.</blockquote>\n          <aside class=\"notes\">\n            <p>Normal tasks run sequentially; handler tasks run on notification of a change and will only run once at the end of a play.</p>\n            <p>For more information see <a href=\"http://docs.ansible.com/ansible/playbooks_intro.html#handlers-running-operations-on-change\">Handler Tasks</a>.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Example Handler Task in a Play</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\ntasks:\n- name: Ensure httpd package is latest\n  yum:\n    name: httpd\n    state: latest\n  <mark>notify: restart httpd</mark>\n\n- name: Ensure latest index.html file is present\n  copy:\n    src: files/index.html\n    dest: /var/www/html/\n\n<mark>handlers:</mark>\n- name: restart httpd\n  service:\n    name: httpd\n    state: restarted\n          </code></pre>\n          <aside class=\"notes\">\n            <p>Here is our previous example with a few slight modifications that have been highlighted.</p>\n            <p>In this version of our example, we add a <code>notify</code> keyword to the first task to trigger a handler task if it returns &quot;changed&quot; as being true. The third tasks now is under a &quot;handlers&quot; section that signifies that the handler task will run on notification at the end of the play of change occurs during the first task.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Plays &amp; Playbooks</h2>\n          <p>Plays are ordered sets of tasks to execute against host selections from your inventory. A playbook is a file containing one or more plays.</p>\n          <aside class=\"notes\">\n            <p>Playbooks are text files that contain one or more plays that are expressed in YAML. A play defines target hosts and a task list that are executed sequentially (i.e top to bottom) to achieve a certain state on those hosts.</p>\n            <p>For more details see <a href=\"http://docs.ansible.com/ansible/playbooks.html\">the Playbook page</a> in the Ansible documentation.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Playbook Example</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\n---\n- name: Ensure apache is installed and started\n  hosts: web\n  become: yes\n  vars:\n    http_port: 80\n\n  tasks:\n  - name: Ensure httpd package is present\n    yum:\n      name: httpd\n      state: latest\n\n  - name: Ensure latest index.html file is present\n    copy:\n      src: files/index.html\n      dest: /var/www/html/\n\n  - name: Ensure httpd is started\n    service:\n      name: httpd\n      state: started\n          </code></pre>\n          <aside class=\"notes\">\n            <p>Here we can see an example of a simple but complete Ansible play. The slides that follow will explore each of these parts and what they do.</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>Human-Meaningful Naming</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\n ---\n - <mark>name: install and start apache</mark>\n   hosts: web\n   become: yes\n   vars:\n     http_port: 80\n\n   tasks:\n   - <mark>name: httpd package is present</mark>\n     yum:\n       name: httpd\n       state: latest\n\n   - <mark>name: latest index.html file is present</mark>\n     copy:\n       src: files/index.html\n       dest: /var/www/html/\n\n   - <mark>name: httpd is started</mark>\n     service:\n        name: httpd\n        state: started\n          </code></pre>\n          <aside class=\"notes\">\n            <p>Every play and each task in it can be assigned a name that describes its objective in the automation workflow and is output during execution.</p>\n            <p>It&apos;s best practice to always name your plays and tasks. Adding name with a human-meaningful description better communicates the intent to users when running a play.</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>Host Selector</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\n---\n- name: Ensure apache is installed and started\n  <mark>hosts: web</mark>\n  become: yes\n  vars:\n    http_port: 80\n\n  tasks:\n  - name: Ensure httpd package is present\n    yum:\n      name: httpd\n      state: latest\n\n  - name: Ensure latest index.html file is present\n    copy:\n      src: files/index.html\n      dest: /var/www/html/\n\n  - name: Ensure httpd is started\n    service:\n      name: httpd\n      state: started\n          </code></pre>\n          <aside class=\"notes\">\n            <p>The Ansible play host selector defines which nodes in the inventory the automation is targeting.</p>\n            <p>In this example, a single group of &quot;web&quot; is being targeted. Ansible supports targeting intersections, unions and filters of multiple groups or hosts though.</p>\n            <p>For more details see <a href=\"http://docs.ansible.com/ansible/intro_patterns.html\">the host selector Patterns page</a> in the Ansible documentation.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Privilege Escalation</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\n---\n- name: Ensure apache is installed and started\n  hosts: web\n  <mark>become: yes</mark>\n  vars:\n    http_port: 80\n\n  tasks:\n  - name: Ensure httpd package is present\n    yum:\n      name: httpd\n      state: latest\n\n  - name: Ensure latest index.html file is present\n    copy:\n      src: files/index.html\n      dest: /var/www/html/\n\n  - name: Ensure httpd is started\n    service:\n      name: httpd\n      state: started\n          </code></pre>\n          <aside class=\"notes\">\n            <p>Ansible allows you to &quot;become&quot; another user or with elevated rights to execute a task or an entire play (shown above). This is done using existing privilege escalation tools, which you probably already use or have configured, like sudo (the default), su, pfexec, doas, pbrun, dzdo, ksu and others.</p>\n            <p>For more details see <a href=\"http://docs.ansible.com/ansible/become.html\">the Privilege Escalation page</a> in the Ansible documentation.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Play Variables</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\n---\n- name: Ensure apache is installed and started\n  hosts: web\n  become: yes\n  <mark>vars:</mark>\n    <mark>http_port: 80</mark>\n\n  tasks:\n  - name: Ensure httpd package is present\n    yum:\n      name: httpd\n      state: latest\n\n  - name: Ensure latest index.html file is present\n    copy:\n      src: files/index.html\n      dest: /var/www/html/\n\n  - name: Ensure httpd is started\n    service:\n      name: httpd\n      state: started\n          </code></pre>\n          <aside class=\"notes\">\n            <p>Variables are used to store metadata for each host drawing from numerous sources. Here we see another one of those sources, a variable &quot;http_port&quot; that has been embedded in a play.</p>\n            <p>Recalling the previous variable precedence slide, play variables are number 7 in Ansible&apos;s the precedence chain.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Tasks</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\n---\n- name: Ensure apache is installed and started\n  hosts: web\n  become: yes\n  vars:\n    http_port: 80\n\n  tasks:\n  - name: Ensure latest httpd package is present\n    <mark>yum:</mark>\n      <mark>name: httpd</mark>\n      <mark>state: latest</mark>\n\n  - name: Ensure latest index.html file is present\n    <mark>copy:</mark>\n      <mark>src: files/index.html</mark>\n      <mark>dest: /var/www/html/</mark>\n\n  - name: Ensure httpd is started\n    <mark>service:</mark>\n      <mark>name: httpd</mark>\n      <mark>state: started</mark>\n          </code></pre>\n          <aside class=\"notes\">\n            <p>Here we see modules, such as yum, being applied to a to perform a unit of automation such as to assure the latest httpd package is present on a host.</p>\n          </aside>\n        </section>\n        <section>\n          <section data-state=\"title alt\">\n            <h1>Demo Time: <br/>A Simple Playbook Run</h1>\n            <aside class=\"notes\">\n              <p>To demonstrate how an Ansible, open an SSH session to your control host and run the playbook in <code>examples/apache-simple-playbook</code>. That example playbook is essentially the same one we just examined.</p>\n              <p>Don't forget to reverse what this playbook does before continuing to not interfere with later demos and workshops.</p>\n            </aside>\n          </section>\n          <section data-state=\"title alt\">\n            <h1>Workshop: <br/>Your First Playbook</h1>\n            <aside class=\"notes\">\n              <p>This assignment provides a quick introduction to playbook structure to give them a feel for how Ansible works, but in practice is too simplistic to be useful.</p>\n              <p>See <code>workshops/simple_playbook</code> in the Ansible Lightbulb repo.</p>\n              <p>Note: If your time is limited, this is a workshop you can skip. We'll cover this and more topics in the next workshop.</p>\n            </aside>\n          </section>\n        </section>\n        <section>\n          <section>\n            <h2>Doing More with Playbooks</h2>\n            <p>Here are some more essential playbook features that you can apply:</p>\n            <ul>\n              <li>Templates</li>\n              <li>Loops</li>\n              <li>Conditionals</li>\n              <li>Tags</li>\n              <li>Blocks</li>\n            </ul>\n            <aside class=\"notes\">\n              <p>We only have covered the most essential capabilities of what can be done with a playbook so far.</p>\n              <p>Here we list a few more though this is still far from all there is. There&apos;s many other powerful playbook features for handling less common though vital automation workflows. No need to learn everything at once. You can start small and pick up more features over time as you need them.</p>\n            </aside>\n          </section>\n          <section>\n            <h2>Templates</h2>\n            <p>Ansible embeds the <a href=\"http://jinja.pocoo.org/docs/\">Jinja2 templating engine</a> that can be used to dynamically:</p>\n            <ul>\n              <li>Set and modify play variables</li>\n              <li>Conditional logic</li>\n              <li>Generate files such as configurations from variables</li>\n            </ul>\n            <aside class=\"notes\">\n              <p>Templates are a vital feature provided by the Jinja2 template engine, a powerful piece of software independent of Ansible. Ansible makes this usage as seamless and transparent as possible. Most will not realize they are doing templating when they develop plays with Ansible.</p>\n              <p>We don't show any specific template examples at the moment because we'll have plenty of opportunity to see templates in action as we cover other topics.</p>\n              <p>In all actuality, what is covered here only touches upon a few of its most basic features. To go deeper see these docs:</p>\n              <ul>\n              <li><a href=\"http://jinja.pocoo.org/docs/dev/templates/\">Jinja2 Template Designer Documentation</a></li>\n              <li><a href=\"http://docs.ansible.com/ansible/playbooks_filters.html\">Ansible Jinja2 Filters</a></li>\n              <li><a href=\"http://docs.ansible.com/ansible/playbooks_tests.html\">Ansible Jinja2 Tests</a></li>\n              </ul>\n            </aside>\n          </section>\n          <section>\n            <h2>Loops</h2>\n            <p>Loops can do one task on multiple things, such as create a lot of users, install a lot of packages, or repeat a polling step until a certain result is reached.</p>\n            <pre class=\"language-yaml\"><code data-noescape>\n- yum:\n    name: <mark>&quot;{{ item }}&quot;</mark>\n    state: latest\n  <mark>loop:</mark>\n  <mark>- httpd</mark>\n  <mark>- mod_wsgi</mark>\n            </code></pre>\n            <aside class=\"notes\">\n              <p>This example demonstrates a basic loop with <code>loop</code>. The loop sets a variable called &quot;item&quot;, a template variable courtesy of the embedded Jinja2 template engine, with the list value that is in context.</p>\n              <p>There are many different and specialized types of task loops to work with. See the <a href=\"http://docs.ansible.com/ansible/playbooks_loops.html\">Loops documentation</a> to go deeper.</p>\n            </aside>\n          </section>\n          <section>\n            <h2>Conditionals</h2>\n            <p>Ansible supports the conditional execution of a task based on the run-time evaluation of variable, fact, or previous task result.</p>\n            <pre class=\"language-yaml\"><code data-noescape>\n- yum:\n    name: httpd\n    state: latest\n  <mark>when: ansible_os_family == &quot;RedHat&quot;</mark>\n            </code></pre>\n            <aside class=\"notes\">\n              <p>Conditionals are another instance of Jinja2 in action within Ansible plays themselves. In the provided example &quot;ansible_os_family&quot; is a fact variable Ansible will set.</p>\n              <p>There are other forms of conditional clauses, but <code>when</code> is usually all that is needed.</p>\n              <p>NOTE: Conditional clauses are considered to be raw Jinja2 expression without double curly braces.</p>\n            </aside>\n          </section>\n          <section>\n            <h2>Tags</h2>\n            <p>Tags are useful to be able to run a subset of a playbook on-demand.</p>\n            <pre class=\"language-yaml\"><code data-noescape>\n- yum:\n    name: &quot;{{ item }}&quot;\n    state: latest\n  loop:\n  - httpd\n  - mod_wsgi\n  <mark>tags:</mark>\n     <mark>- packages</mark>\n\n - template:\n     src: templates/httpd.conf.j2\n     dest: /etc/httpd/conf/httpd.conf\n  <mark>tags:</mark>\n     <mark>- configuration</mark>\n            </code></pre>\n            <aside class=\"notes\">\n              <p>In the provided example, if the playbook were run with <code>--tags &quot;packages&quot;</code> the yum task would execute and the template task would be skipped. If the playbook were run with <code>--tags &quot;configuration&quot;</code> the opposite would happen. Without a <code>--tags</code> both tasks would execute like normal.</p>\n              <p>Tags can be seen as a simple though specialized form of conditional statement designed to enable the execution of a subset tasks. Tags are a bit more than simple boolean flags though. To dig deeper see the <a href=\"http://docs.ansible.com/ansible/playbooks_tags.html\">playbook Tags documentation</a>.</p>\n            </aside>\n          </section>\n          <section>\n            <h2>Blocks</h2>\n            <p>Blocks cut down on repetitive task directives, allow for logical grouping of tasks and even in play error handling.</p>\n            <pre class=\"language-yaml\"><code data-noescape>\n- <mark>block:</mark>\n  - yum:\n      name: &quot;{{ item }}&quot;\n      state: latest\n    loop:\n    - httpd\n    - mod_wsgi\n\n  - template:\n      src: templates/httpd.conf.j2\n      dest: /etc/httpd/conf/httpd.conf\n  when: ansible_os_family == &quot;RedHat&quot;\n            </code></pre>\n            <aside class=\"notes\">\n              <p>In the provided example we use a block to group two tasks that are to be conditional executed if a host is running a Red Hat family linux using one conditional clause. In practice, we could have copied the <code>when</code> clause onto each task and gotten same result. Using a block, there is less clutter thereby less to maintain and nothing to keep in sync. The utility of using a block increases the more tasks and clauses are in use.</p>\n              <p>Blocks have a play in error handling and automating roll backs that we won't get into here given the scope of that topic.</p>\n              <p>See the <a href=\"http://docs.ansible.com/ansible/playbooks_blocks.html\">documentation on blocks</a> to dig deeper.</p>\n          </section>\n        </section>\n        <section>\n          <section data-state=\"title alt\">\n            <h1>Demo Time: <br/>A More Practical Playbook</h1>\n            <aside class=\"notes\">\n              <p>The simple playbook we examined gave us a sense of the structure of a playbook and how they work, but in reality was too simplistic to be practical. Here we look at a basic playbook that it more complete to what users will commonly do with Ansible.</p>\n              <p>Walk thru the site.yml playbook in <code>examples/apache-basic-playbook</code> and note the added logic and its function. Then, run the playbook on the control host.</p>\n              <p>Don't forget to reverse what this playbook does before continuing to not interfere with later demos and workshops.</p>\n            </aside>\n          </section>\n          <section data-state=\"title alt\">\n            <h1>Workshop: <br/>Practical Playbook Development</h1>\n            <aside class=\"notes\">\n              <p>In this workshop assignment students are tasked with developing their first complete playbook. The assignment approximates the tasks they will typical need to take in order to deploy and configure a single application service using Nginx.</p>\n              <p>See <code>workshops/basic_playbook/</code> in the Ansible Lightbulb repo.</p>\n              <p>Tip: Don&apos;t rush this workshop. Give students ample time to do this workshop themselves to completion. This workshop covers the essential concepts and features of effectively automation with Ansible and builds up their core skills for further exploration.</p>\n            </aside>\n          </section>\n        </section>\n        <section>\n          <h2>Roles</h2>\n          <p>Roles are a packages of closely related Ansible content that can be shared more easily than plays alone.</p>\n          <ul>\n            <li>Improves readability and maintainability of complex plays</li>\n            <li>Eases sharing, reuse and standardization of automation processes</li>\n            <li>Enables Ansible content to exist independently of playbooks, projects -- even organizations</li>\n            <li>Provides functional conveniences such as file path resolution and default values</li>\n          </ul>\n          <aside class=\"notes\">\n            <p>Roles are closely related Ansible content that are organized into a pre-defined directory structure, making them easier to reuse and share among groups.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Project with Embedded Roles Example</h2>\n          <pre class=\"language-yaml\"><code>\nsite.yml\nroles/\n   common/\n     files/\n     templates/\n     tasks/\n     handlers/\n     vars/\n     defaults/\n     meta/\n   apache/\n     files/\n     templates/\n     tasks/\n     handlers/\n     vars/\n     defaults/\n     meta/\n          </code></pre>\n          <aside class=\"notes\">\n            <p>This slide shows an example file system with a top-level playbook (site.yml) and two sample roles (common and apache) embedded in the project. Notice how the file structure under each role are similar to what we find in a play. Role deconstruct the content we'd put in a play and package it up in such a way that it is portable and easily shared and reused.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Project with Embedded Roles Example</h2>\n          <pre class=\"language-yaml\"><code>\n# site.yml\n---\n- name: Execute common and apache role\n  hosts: web\n  roles:\n     - common\n     - apache\n          </code></pre>\n          <aside class=\"notes\">\n            <p>This slide shows the site.yml playbook using the roles in our example project. Notice that it is much more concise than what we've seen.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Ansible Galaxy</h2>\n          <p><strong>http://galaxy.ansible.com</strong></p>\n          <p>Ansible Galaxy is a hub for finding, reusing and sharing Ansible content.</p>\n          <p>Jump-start your automation project with content contributed and reviewed by the Ansible community.</p>\n          <aside class=\"notes speaker\">\n            <p>Ansible Galaxy refers to the Galaxy website, a hub for finding, downloading, and sharing community developed roles. Downloading roles from Galaxy is a great way to jump-start your automation projects.\n            <p>Galaxy also refers to a command line tool, <code>ansible-galaxy</code>, for installing, creating and managing roles from the Galaxy website or directly from a git based SCM. You can also use it to create a new role, remove roles, or perform tasks on the Galaxy website.</p>\n          </aside>\n        </section>\n        <section>\n            <section data-state=\"title alt\">\n              <h1>Demo Time: <br/>A Playbook Using Roles</h1>\n              <aside class=\"notes\">\n                <p>Walk thru the site.yml playbook and the contents of <code>roles/apache</code> in <code>examples/apache-role</code>. Note how the tasks, variables and handlers from the basic Ansible playbook we've been examined and have been refactored into a role.</p>\n                <p>Run the playbook on the control host. Notice anything different? Other than a few subtle display changes, you shouldn't. What's important is how roles let you better organize, share and reuse Ansible content that will be vital as the scope and sophistication of your automation accelerates.</p>\n              </aside>\n            </section>\n            <section data-state=\"title alt\">\n              <h1>Workshop: <br/>Your First Roles</h1>\n              <aside class=\"notes\">\n                <p>Here students are tasked with refactoring their previous work from the Practical Playbook workshop into a role and modifying their playbook accordingly. We intentionally avoid introducing any new tasks or functionality otherwise. The objective here is to focus students specifically on how roles are structured and develop.</p>\n                <p>You should emphasize the value of roles in better organizing playbooks as they grow in sophistication and making Ansible automation more portable and reusable than a basic playbook.</p>\n                <p>See <code>workshops/roles</code> in the Ansible Lightbulb repo.</p>\n              </aside>\n            </section>\n        </section>\n        <section>\n          <h2>Next Steps</h2>\n          <ul>\n            <li><strong>It&apos;s easy to get started</strong><br/>ansible.com/get-started</li>\n            <li><strong>Join the Ansible community</strong><br/>ansible.com/community</li>\n            <li><strong>Would you like to learn a lot more?</strong><br/>redhat.com/en/services/training/do407-automation-ansible</li>\n          </ul>\n        </section>\n      </div>\n    </div>\n\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/lib/js/head.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/js/reveal.js\"></script>\n\n    <script>\n      // More info https://github.com/hakimel/reveal.js#configuration\n      Reveal.initialize({\n        history: true,\n        width: \"85%\",\n        height: \"90%\",\n        transition: \"fade\",\n\n        // More info https://github.com/hakimel/reveal.js#dependencies\n        // Notes plugin must remain local for now.\n        // See https://github.com/ansible/lightbulb/issues/125\n        dependencies: [\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/plugin/markdown/marked.js' },\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/plugin/markdown/markdown.js' },\n          { src: 'plugin/notes/notes.js', async: true },\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/prism.min.js'},\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/components/prism-yaml.min.js'}\n          //{ src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }\n        ]\n      });\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "decks/ansible_basics.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">\n\n    <title>Ansible Basics</title>\n\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/css/reveal.css\">\n\n    <!-- Printing and PDF exports -->\n    <script>\n      var link = document.createElement( 'link' );\n      link.rel = 'stylesheet';\n      link.type = 'text/css';\n      link.href = window.location.search.match( /print-pdf/gi ) ? 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/css/print/pdf.css' : 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/css/print/paper.css';\n      document.getElementsByTagName( 'head' )[0].appendChild( link );\n    </script>\n\n\n    <link rel=\"stylesheet\" href=\"css/theme/ansible.css\">\n\n    <!-- Theme used for syntax highlighting of code -->\n    <!--link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/lib/css/zenburn.css\"-->\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/themes/prism.min.css\">\n\n\n  </head>\n  <body>\n  <div class=\"ans-mark\">\n    <svg version=\"1.1\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" viewBox=\"-449 450 125 125\" style=\"enable-background:new -449 450 125 125;\" xml:space=\"preserve\">\n      <g id=\"XMLID_3_\">\n        <circle id=\"XMLID_7_\" class=\"circle\" cx=\"-386.5\" cy=\"512.5\" r=\"62\"/>\n        <path id=\"XMLID_4_\" class=\"a-mark\" d=\"M-356.9,537.1l-24.7-59.4c-0.7-1.7-2.1-2.6-3.9-2.6c-1.7,0-3.2,0.9-4,2.6l-27.1,65.2h9.2 l10.7-26.9l32,25.9c1.3,1,2.2,1.5,3.4,1.5c2.4,0,4.6-1.8,4.6-4.5C-356.5,538.5-356.6,537.8-356.9,537.1z M-385.4,488.4l16.1,39.6 l-24.2-19L-385.4,488.4z\"/>\n      </g>\n    </svg>\n  </div>\n    <div class=\"reveal\">\n      <div class=\"slides\">\n        <section data-state=\"cover\">\n          <p class=\"ans-logo\"><img src=\"images/ansible-wordmark-white.svg\" width=\"260\" alt=\"\" /></p>\n          <h1>Ansible Basics</h1>\n          <p>NAME HERE, TITLE HERE</p>\n          <p>COMPANY HERE</p>\n        </section>\n\n        <section>\n          <h2>WHAT YOU WILL LEARN</h2>\n          <p>Ansible is capable of handling many powerful automation tasks with the flexibility to adapt to many environments and workflows. With Ansible, users can very quickly get up and running to do real work.</p>\n          <br><br>In this presentation, we will cover:</p>\n          <ul>\n            <li>What is Ansible</li>\n            <li>How Ansible Works</li>\n            <li>Ad-Hoc Commands</li>\n            <li>Playbook Basics</li>\n            <li>Reuse and Redistribution of Ansible Content with Roles</li>\n            <li>Ansible Tower</li>\n          </ul>\n          <aside class=\"notes\">\n            <p>Today we are going to cover the following topics: (read the list)</p>\n          </aside>\n        </section>\n\n        <section>\n            <h2>WHAT IS ANSIBLE AUTOMATION?</h2>\n            <p>The Ansible project is an open source community sponsored by Red Hat. It’s also a <strong>simple automation language</strong> that perfectly describes IT application environments in <strong>Ansible Playbooks</strong>.\n            <br><br>\n            <strong>Ansible Engine</strong> is a <strong>supported product</strong> built from the Ansible community project.\n            <br><br>\n            Ansible Tower is an <strong>enterprise framework</strong> for controlling, securing, managing and extending your Ansible automation (community or engine) with a <strong>UI and RESTful API</strong>.</p>\n            <br><br>\n            <aside class=\"notes\">\n              <p>The Ansible Project is open source and sponsored by Red Hat.\n                Ansible Engine is a supported product built from the Ansible community project, and Ansible Tower is the framework for the automation that Ansible Engine does.\n                There’s also an open-source version of Tower which is the AWX project, and it’s associated with the Ansible GitHub.</p>\n            </aside>\n        </section>\n\n        <section>\n            <h2>WHY ANSIBLE?</h2>\n            <p><img src=\"images/why_ansible.jpg\"></p>\n            <aside class=\"notes\">\n              <p>So why should you think about using Ansible and what sets it apart?\n              First off, Ansible is simple: we expect that your user base is going to be mixed with people of all skill levels and because of that we wanted to make it incredibly easy to get started.  The language playbooks are written in is YAML, which is very easy-to-read for humans, and those playbooks work consistently across many environments\n              It’s also extremely powerful in how many nodes we can spin up and manage at any given time, which is largely due to the fact that Ansible is agentless.  There are no agents to keep up to date and the requirements are minimal.\n              </p>\n            </aside>\n        </section>\n\n        <section>\n          <h2>THE ANSIBLE WAY</h2>\n          <p><font color=\"52AAAC\"><strong>CROSS PLATFORM – Linux, Windows, UNIX</strong></font><br>\n          Agentless support for all major OS variants, physical, virtual, cloud and network.</p>\n          <p><font color=\"52AAAC\"><strong>HUMAN READABLE – YAML</strong></font><br>\n          Perfectly describe and document every aspect of your application environment</p>\n          <p><font color=\"52AAAC\"><strong>PERFECT DESCRIPTION OF APPLICATION</strong></font><br>\n          Every change can be made by playbooks, ensuring everyone is on the same page.</p>\n          <p><font color=\"52AAAC\"><strong>VERSION CONTROLLED</strong></font><br>\n          Playbooks are plain-text. Treat them like code in your existing version control.</p>\n          <p><font color=\"52AAAC\"><strong>DYNAMIC INVENTORIES</strong></font><br>\n          Capture all the servers 100% of the time, regardless of infrastructure, location, etc.</p>\n          <p><font color=\"52AAAC\"><strong>ORCHESTRATION THAT PLAYS WELL WITH OTHERS – HP SA, Puppet, Jenkins, RHNSS, etc.</strong></font><br>\n          Homogenize existing environments by leveraging current toolsets and update mechanisms.</p>\n          <aside class=\"notes\">\n            <p>In addition, that agentless support isn’t just Linux exclusive.\n              This also applies to Windows machines, networking devices, and of course both physical and cloud servers, as well as static or dynamic inventory.\n              Also, one of the main selling points of Ansible is that we work well with existing toolkits.\n              Sometimes people don’t know where to start; I’d advise you to pick a pain point in your environment to focus on, something small that you can leverage Ansible for and see how that feels, because you can always homogenize over time.\n            </p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>BATTERIES INCLUDED</h2>\n          <p>Ansible comes bundled with hundreds of modules for a wide variety of automation tasks:</p>\n          <div style=\"float: left; width: 50%;\">\n          <ul>\n          <li>cloud</li>\n          <li>containers</li>\n          <li>database</li>\n          <li>files</li>\n          <li>messaging</li>\n          <li>monitoring</li>\n          <li>network</li>\n          </ul>\n          </div>\n          <div style=\"float: right; width: 50%;\">\n          <ul>\n          <li>notifications</li>\n          <li>packaging</li>\n          <li>source control</li>\n          <li>system</li>\n          <li>testing</li>\n          <li>utilities</li>\n          <li>web infrastructure</li>\n          </ul>\n          </div>\n          <aside class=\"notes\">\n            <p>Installing Ansible nets you a TON of modules that range in type from the popular cloud modules to packaging and database modules.\n              The list you see here are some of the “high profile” modules, but there are lots more that aren’t listed.</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>COMMUNITY</h2>\n          <p><font color=\"52AAAC\"><strong>THE MOST POPULAR OPEN-SOURCE AUTOMATION COMMUNITY ON GITHUB</strong></font><br></p>\n<!-- Remember to check the numbers listed below!!  -->\n          <ul>\n            <li>34,000+ stars & 13,500+ forks on GitHub</li>\n            <li>4000+ GitHub Contributors</li>\n            <li>Over 2000 modules shipped with Ansible</li>\n            <li>New contributors added every day</li>\n            <li>1400+ users on IRC channel</li>\n            <li>World-wide meetups taking place every week</li>\n            <li>Ansible Galaxy: over 9,000 contributors and 18,000 roles</li>\n            <li>500,000+ downloads a month</li>\n            <li>AnsibleFests, Ansible Automates, and other global events</li>\n          </ul>\n          </aside>\n        </section>\n\n        <section>\n          <h2>ANSIBLE: COMPLETE AUTOMATION</h2>\n          <p><img src=\"images/complete_automation.jpg\"></p>\n          <aside class=\"notes\">\n            <p>One of the other bullet points that we highlight around Ansible is that it’s the complete package.\n              So it’s not just configuration management; it’s also orchestration, app-deployment and provisioning as well.\n              Looking at this graphic here , you can see that there are tools that definitely perform one or more of these use cases, but can’t do them all and that’s really one of the big strengths of Ansible.\n              Some users like to refer to it as “Automation Glue”.</p>\n          </aside>\n        </section>\n\n        <section>\n            <h2>WHAT CAN I DO WITH ANSIBLE?</h2>\n            <p>Automate the deployment and management of your entire IT footprint.</p>\n            <p><img src=\"images/ansible_possibilities.jpg\"></p>\n            <aside class=\"notes\">\n              <p>On this slide, you can see the different use cases for how to manage and automate your machines.\n                (some examples are: security and compliance, orchestration, creating new VMs, creating a CI/CD pipeline) - all of these things can be done with Ansible.\n              </p>\n            </aside>\n        </section>\n\n        <section>\n          <h2>INSTALLING ANSIBLE</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\n# the most common and preferred way of installation\n$ pip install ansible\n\n# install the epel-release RPM if needed on CentOS, RHEL, or Scientific Linux\n$ sudo yum install ansible\n\n# you will need the PPA repo configured\n$ sudo apt-get install ansible\n          </code></pre>\n          <aside class=\"notes\">\n            <p>There are many ways to install Ansible, including making your own rpm package, running from source, or you can just use a package manager.   As you can see, the examples below are using pip, yum, and apt.\n              The yum and apt installations require additional steps so if you prefer using a single command, then you can install with pip.\n                1 - Most common and preferred way (unless your Python is set up weird)\n                2 - If you’re running a RHEL or CentOS type of machine, install epel-release and grab the package from there\n                3- Ubuntu/Debian flavor OS\n              If anything stumps you, it’s always helpful to take a look at the Ansible Installation documentation for more information.\n            </p>\n          </aside>\n        </section>\n\n        <section>\n            <h2>HOW ANSIBLE WORKS</h2>\n            <p><img src=\"images/how_ansible_works.jpg\"></p>\n            <aside class=\"notes\">\n              <p>Once you have Ansible installed, you’re going to need to become acquainted with the key components of how Ansible works.\n                The graphic on this slide shows the relationship between all the main pieces of Ansible, starting with the user who writes an Ansible playbook.\n              </p>\n            </aside>\n        </section>\n\n        <section>\n          <h2>MODULES</h2>\n          <p><font color=\"52AAAC\"><strong>Modules are bits of code transferred to the target system and executed to satisfy the task declaration.</strong></font><br></p>\n          <div style=\"float: left; width: 50%;\">\n          <ul>\n          <li>apt/yum</li>\n          <li>copy</li>\n          <li>file</li>\n          <li>get_url</li>\n          <li>git</li>\n          <li>ping</li>\n          <li>debug</li>\n          </ul>\n          </div>\n          <div style=\"float: right; width: 50%;\">\n          <ul>\n          <li>service</li>\n          <li>synchronize</li>\n          <li>template</li>\n          <li>uri</li>\n          <li>user</li>\n          <li>wait_for</li>\n          <li>assert</li>\n          </ul>\n          </div>\n          <aside class=\"notes\">\n            <p>We talked a bit about modules in the previous couple slides, but I did want to point out a list of some commonly used modules that you may see out in example repos, blog posts, etc. a fair amount.\n              Typically, you're going to see a lot of package management modules being used, lots of file modules like copy and file, but as mentioned previously, there are tons more as well.</p>\n          </aside>\n        </section>\n\n        <section>\n            <h2>MODULES</h2>\n            <p><img src=\"images/module_index.jpg\"></p>\n            <p><font color=\"52AAAC\"><strong>https://docs.ansible.com/ansible/latest/modules/modules_by_category.html</strong></font><br></p>\n            <aside class=\"notes\">\n              <p>You can check out all of them by looking at the exhaustive module index on docs.ansible.com.\n                They’re broken down by category, so you can more easily find what you need.\n                I would highly recommend bookmarking this page!\n              </p>\n            </aside>\n        </section>\n\n        <section>\n          <h2>MODULES: RUN COMMANDS</h2>\n          <p><font color=\"52AAAC\"><strong>If Ansible doesn’t have a module that suits your needs there are the “run command” modules:</strong></font><br><br></p>\n          <ul>\n            <li><strong>command:</strong> Takes the command and executes it. The most secure and predictable.</li>\n            <li><strong>shell:</strong> Executes through a shell like /bin/sh so you can use pipes etc. Be careful.</li>\n            <li><strong>script:</strong> Runs a local script on a remote node after transferring it.</li>\n            <li><strong>raw:</strong> Executes a command  without going through the Ansible module subsystem.</li>\n          </ul>\n          <br>\n            <p><strong>NOTE:</strong> Unlike standard modules, run commands have no concept of desired state and should only be used as a last resort</p>\n          <aside class=\"notes\">\n            <p>On this slide we’re gonna discuss the command and shell modules which are super useful because you can execute commands with them on your remote nodes.\n              The only big difference is that the command is not processed through the shell so redirect options and certain variables won’t work.\n              If you need those, just use the shell module instead.\n              The command modules let you run networking commands with Ansible, the same way a network engineer would type them on the command line.\n\n              If you have something that’s not a core module or you haven't written a custom one, you can utilize the script module.\n              For example, if you want to push out a homegrown script, you can do that here.\n\n              We also have the raw module, which issues a pure SSH command and bypasses the python module mechanism.\n              This is handy if you’re talking to a machine that doesn’t have python (for example, a server that’s running Windows!).\n            </p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>AD-HOC COMMANDS</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\n            # check all my inventory hosts are ready to be managed by Ansible\n            $ ansible all -m ping\n\n            # run the uptime command on all hosts in the web group\n            $ ansible web -m command -a “uptime”\n\n            # collect and display the discovered for the localhost\n            $ ansible localhost -m setup\n          </code></pre>\n          <aside class=\"notes\">\n            <p>We’re going to transition right into ad-hoc commands from the commands modules because often times, you utilize command/shell to issue one-off commands to your hosts.\n              This is essentially what ad-hoc commands are; just quick checks on your servers that you don’t want to preserve in an Ansible playbook.\n              The examples here are pretty typical.\n                Our first example is just checking whether we have connectivity with the host by issuing the ping command (I do this all the time to make sure everything is responding).\n                In the second example here, you can see the command to check the uptime on the two servers in my web group using the command module.\n                And lastly, the third example is gathering facts about our local machine which would yield the information that you can use for conditionals and variables and whatnot.\n                [a bit of the output is shown on next slide]\n            </p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>SIDEBAR: DISCOVERED FACTS</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\n$ ansible localhost -m setup\nlocalhost | success >> {\n\t\"ansible_facts\": {\n    \t\"ansible_default_ipv4\": {\n        \t\"address\": \"192.168.1.37\",\n        \t\"alias\": \"wlan0\",\n        \t\"gateway\": \"192.168.1.1\",\n        \t\"interface\": \"wlan0\",\n        \t\"macaddress\": \"c4:85:08:3b:a9:16\",\n        \t\"mtu\": 1500,\n        \t\"netmask\": \"255.255.255.0\",\n        \t\"network\": \"192.168.1.0\",\n        \t\"type\": \"ether\"\n    \t},\n          </code></pre>\n          <aside class=\"notes\">\n            <p>This is just a small sampling of what’s gathered from that setup ad hoc command.</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>INVENTORY</h2>\n          <p>Inventory is a collection of hosts (nodes) against which Ansible can work with.</p>\n          <ul>\n            <li>Hosts</li>\n            <li>Groups</li>\n            <li>Inventory-specific data (variables)</li>\n            <li>Static or dynamic sources</li>\n          </ul>\n          <aside class=\"notes\">\n            <p>Ad-hoc commands and modules always come back to the inventory we’re managing.  Inventory consists of hosts, groups, inventory specific data, and whether it’s static or dynamic sources. Note that “hosts” has been expanded overtime to include network devices, containers and virtually anything else that is network-addressable.</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>STATIC INVENTORY EXAMPLE</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\n            10.42.0.2\n            10.42.0.6\n            10.42.0.7\n            10.42.0.8\n            10.42.0.100\n          </code></pre>\n          <aside class=\"notes\">\n            <p>This is a free-form static inventory listed by IP address. If I clean that up a bit, I get...</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>STATIC INVENTORY EXAMPLE</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\n            [control]\n            control ansible_host=10.42.0.2\n\n            [web]\n            node-1 ansible_host=10.42.0.6\n            node-2 ansible_host=10.42.0.7\n            node-3 ansible_host=10.42.0.8\n\n            [haproxy]\n            haproxy ansible_host=10.42.0.100\n\n            [all:vars]\n            ansible_user=vagrant\n            ansible_ssh_private_key_file=~/.vagrant.d/insecure_private_key\n          </code></pre>\n          <aside class=\"notes\">\n            <p>...a host file that’s separated into groups (which in this example is control, web, and haproxy).\n              We also have some inventory-specific data where we’re defining ansible_host for each of these different nodes, and we’ve got a vars section defined for all the hosts where we’re setting the connecting user.\n              There’s also a path to the private key file we’re using for authentication.</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>STATIC INVENTORY EXAMPLE IN YAML</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\n            all:\n              vars:\n                ansible_user: vagrant\n                ansible_ssh_private_key_file: ~/.vagrant.d/insecure_private_key\n\n              children:\n                web:\n                  hosts:\n                    node-1:\n                      ansible_host: 10.42.0.6\n                    node-2:\n                      ansible_host: 10.42.0.7\n                    node-3:\n                      ansible_host: 10.42.0.8\n          </code></pre>\n          <aside class=\"notes\">\n            <p>This is a similar example of the same hosts file in YAML format which is a new option as of version 2.4.\n              This provides some flexibility in regards to the format you might find easier to work with.</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>VARIABLES</h2>\n          <p>Ansible can work with metadata from various sources and manage their context in the form of variables.</p>\n          <ul>\n            <li>Command line parameters</li>\n            <li>Plays and tasks</li>\n            <li>Files</li>\n            <li>Inventory</li>\n            <li>Discovered facts</li>\n            <li>Roles</li>\n          </ul>\n          <aside class=\"notes\">\n            <p>To go into a little more detail, variables in Ansible are used to account for differences between servers.\n              By using variables, I have the ability to keep a nice layer of abstraction in my playbook and define my variables as needed in various host or group vars files.\n              These could be for things like facts or file paths or package versions.</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>VARIABLE PRECEDENCE</h2>\n          <p><font color=\"52AAAC\"><strong>The order in which the same variable from different sources will override each other:</strong></font><br></p>\n          <div style=\"float: left; width: 50%;\">\n          <ol>\n          <li>Extra vars</li>\n          <li>Set_facts/Registered vars</li>\n          <li>Include_vars</li>\n          <li>Include_params</li>\n          <li>Role params</li>\n          <li>Task vars (only for the task)</li>\n          <li>Block vars (only for tasks in the block)</li>\n          <li>Role vars</li>\n          <li>Play vars_files</li>\n          <li>Play vars_prompt</li>\n          <li>Play vars</li>\n          </div>\n          <div style=\"float: right; width: 50%;\">\n          <ol start=\"12\">\n          <li>Host facts</li>\n          <li>Playbook host_vars</li>\n          <li>Inventory host_vars</li>\n          <li>Inventory file/script host vars</li>\n          <li>Playbook group_vars</li>\n          <li>Inventory group_vars</li>\n          <li>Playbook group_vars/all</li>\n          <li>Inventory group_vars/all</li>\n          <li>Inventory file or script group vars</li>\n          <li>Role defaults</li>\n          </ol>\n          </div>\n          <aside class=\"notes\">\n            <p>This brings up a good time to talk about variable precedence, which is basically the order in which the same variables will override each other.\n              The latest version has 21 levels of variable precedence wherein extra_vars will always take precedence and role_defaults will get overwritten by all of these other var types.</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>TASKS</h2>\n          <p><font color=\"52AAAC\">Tasks are the application of a module to perform a specific unit of work.</font><br><br></p>\n          <ul>\n            <li><strong>file:</strong>A directory should exist</li>\n            <li><strong>yum:</strong>A package should be installed</li>\n            <li><strong>service:</strong>A service should be running</li>\n            <li><strong>template:</strong>Render a configuration file from a template</li>\n            <li><strong>get_url:</strong>Fetch an archive file from a URL</li>\n            <li><strong>git:</strong>Clone a source code repository</li>\n          </ul>\n          <aside class=\"notes\">\n            <p>Just to recap, so far we’ve talked about acting against inventory with modules, but those modules are going to be contained in things called tasks.\n              These are simple and declarative, because we’re telling the module to do something specific like (give examples from slide of things the modules will do within the tasks).\n            </p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>EXAMPLE TASKS IN A PLAY</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\ntasks:\n- name: add cache in directory\n  file:\n    path: /opt/cache\n    state: directory\n\n- name: install nginx\n  yum:\n    name: nginx\n    state: latest\n\n- name: restart nginx\n  service:\n    name: nginx\n    state: restarted\n          </code></pre>\n          <aside class=\"notes\">\n            <p>In this example, the first task is called “add cache in directory”.\n              Note that you can name it anything you'd like.\n              Here we're going to call the file module and give it a couple of options.\n              Our path is going to be opt/cache and the state is directory, meaning we're going to create the directory opt/cache.</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>HANDLER TASKS</h2>\n          <p><font color=\"52AAAC\"><strong>Handlers are special tasks that run at the end of a play if notified by another task.</strong></font><br><br></p>\n          <aside class=\"notes\">\n            <p>Normal tasks run sequentially, handler tasks run on notification.\n              Handlers may be notified multiple times during a single play, and are triggered when a tasks causes a change in state.\n              If nothing notifies a handler, it will not run. Regardless of how many tasks notify a handler, it will run only once, after all of the tasks complete in a particular play.\n            </p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>EXAMPLE HANDLER IN A PLAY</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\ntasks:\n  - name: add cache dir\n\t file:\n      path: /opt/cache\n      state: directory\n\n  - name: install nginx\n    yum:\n      name: nginx\n      state: latest\n    notify: restart nginx\n\nhandlers:\n  - name: restart nginx\n    service:\n      name: nginx\n      state: restarted\n          </code></pre>\n          <aside class=\"notes\">\n            <p>In this slide, we have an example of a handler in which after we install nginx, we add a notify to restart the services.\n              Then, there’s a handlers section in which we define that handler.\n              If the conditions to trigger the handler aren’t met, then the handler won’t run.\n              Handlers may be notified multiple times during a single play, and are triggered when a task causes a change in state.\n            </p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>PLAYS & PLAYBOOKS</h2>\n          <p><font color=\"52AAAC\"><strong>Plays are ordered sets of tasks to execute against host selections from your inventory.\n            A playbook is a file containing one or more plays.</strong></font><br><br></p>\n          <aside class=\"notes\">\n            <p>All these things we’ve been talking about including tasks, handlers, modules, etc. are all part of what make up plays and playbooks.\n            </p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>PLAYBOOK EXAMPLE</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\n---\n- name: install and start apache\n  hosts: web\n  vars:\n    http_port: 80\n    max_clients: 200\n  remote_user: root\n\n  tasks:\n  - name: install httpd\n    yum: pkg=httpd state=latest\n  - name: write the apache config file\n    template: src=/srv/httpd.j2 dest=/etc/httpd.conf\n  - name: start httpd\n    service: name=httpd state=started\n          </code></pre>\n          <aside class=\"notes\">\n            <p>We’re gonna break down what makes up a playbook and review what we’ve been talking about so far.\n            </p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>ROLES</h2>\n          <p><font color=\"52AAAC\"><strong>Roles are a packages of closely-related Ansible content that can be shared more easily than plays alone.</strong></font><br><br></p>\n          <aside class=\"notes\">\n            <ul>\n              <li>Improves readability and maintainability of complex plays</li>\n              <li>Eases sharing, reuse and standardization of automation processes</li>\n              <li>Enables Ansible content to exist independently of playbooks, projects -- even organizations</li>\n              <li>Provides functional conveniences such as file path resolution and default values</li>\n            </ul>\n          <aside class=\"notes\">\n            <p>We’re gonna break down what makes up a playbook and review what we’ve been talking about so far.\n            </p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>PLAYBOOK EXAMPLE</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\nsite.yml\nroles/\n   common/\n     files/\n     templates/\n     tasks/\n     handlers/\n     vars/\n     defaults/\n     meta/\n   webservers/\n     files/\n     templates/\n     tasks/\n     handlers/\n     vars/\n     defaults/\n     meta/\n          </code></pre>\n          <aside class=\"notes\">\n            <p>This is how it looks from a project perspective.\n              Not all of these directories need to be used, these are just standards that are available and ready to fill in with your tasks, handlers, variables, whatever you need for the particular role.\n            </p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>PLAYBOOK WITH ROLES EXAMPLE</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\n# site.yml\n---\n- hosts: web\n  roles:\n     - common\n     - webservers\n          </code></pre>\n          <aside class=\"notes\">\n            <p>This is how you would call a role from the main playbook.\n              It keeps everything simple and you can switch things out or delete easily without parsing through a huge list of tasks that you’ve made separately for every host type.\n            </p>\n          </aside>\n        </section>\n\n        <section>\n            <h2>ANSIBLE GALAXY</h2>\n            <p><img src=\"images/galaxy.jpg\"></p>\n            <aside class=\"notes\">\n              <p>If you’d like to check out our huge community site for Ansible roles, visit Galaxy.Ansible.com.\n                You can sort through things by author name, stars, number of downloads, as well as other filters, and you can even share your own roles here as well.\n              </p>\n            </aside>\n        </section>\n\n        <section>\n          <h2>WHERE TO FIND PLAYBOOK EXAMPLES</h2>\n          <p><strong>General Examples:</strong></p>\n          <p>https://github.com/ansible/ansible-examples</p>\n          <p><strong>Lightbulb:</strong></p>\n          <p>https://github.com/ansible/lightbulb/tree/master/examples</p>\n          <p><strong>Ansible + Vagrant:</strong></p>\n          <p>https://github.com/geerlingguy/ansible-vagrant-examples</p>\n          <aside class=\"notes\">\n            <p>Here are some places where you can find some good playbook examples.</p>\n          </aside>\n        </section>\n\n        <section>\n          <h2>WHERE TO FIND PLAYBOOK EXAMPLES</h2>\n          <p><strong>Would you like to learn Ansible?  It’s easy to get started:</strong></p>\n          <p>ansible.com/get-started</p>\n          <p><strong>Have you used Ansible already?  Try Tower for free:</strong></p>\n          <p>ansible.com/tower-trial</p>\n          <p><strong>Want to learn more?</strong></p>\n          <p>ansible.com/whitepapers</p>\n          <aside class=\"notes\">\n            <p>If you’d like to learn more about Ansible our Getting Started page is a great resource.\n              Our documentation is also very thorough and well-written, so check that out as well.\n              If you’re ready to try Tower you can visit our Tower Trial page, and you can also check out the whitepapers on our website!</p>\n          </aside>\n        </section>\n\n        <section>\n            <section data-state=\"title alt\">\n              <h1>Demo Time!</h1>\n              <aside class=\"notes\">\n                <p></p>\n              </aside>\n            </section>\n            <section data-state=\"title alt\">\n              <h1>Show a playbook written out?  Show roles?  Tower?</h1>\n              <aside class=\"notes\">\n                <p>more speaker notes here for those deep dive slides</p>\n              </aside>\n            </section>\n        </section>\n\n        <section>\n          <h1>Thank You!</h1>\n        </section>\n      </div>\n    </div>\n\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/lib/js/head.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/js/reveal.js\"></script>\n\n    <script>\n      // More info https://github.com/hakimel/reveal.js#configuration\n      Reveal.initialize({\n        history: true,\n        width: \"85%\",\n        height: \"90%\",\n        transition: \"fade\",\n\n        // More info https://github.com/hakimel/reveal.js#dependencies\n        // Notes plugin must remain local for now.\n        // See https://github.com/ansible/lightbulb/issues/125\n        dependencies: [\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/plugin/markdown/marked.js' },\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/plugin/markdown/markdown.js' },\n          { src: 'plugin/notes/notes.js', async: true },\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/prism.min.js'},\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/components/prism-yaml.min.js'}\n          //{ src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }\n        ]\n      });\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "decks/contributing-to-ansible.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">\n\n    <title>Contributing to Ansible</title>\n\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/css/reveal.css\">\n\n    <!-- Printing and PDF exports -->\n    <script>\n      var link = document.createElement( 'link' );\n      link.rel = 'stylesheet';\n      link.type = 'text/css';\n      link.href = window.location.search.match( /print-pdf/gi ) ? 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/css/print/pdf.css' : 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/css/print/paper.css';\n      document.getElementsByTagName( 'head' )[0].appendChild( link );\n    </script>\n\n\n    <link rel=\"stylesheet\" href=\"css/theme/ansible.css\">\n\n    <!-- Theme used for syntax highlighting of code -->\n    <!--link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/lib/css/zenburn.css\"-->\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/themes/prism.min.css\">\n\n\n  </head>\n  <body>\n  <div class=\"ans-mark\">\n    <svg version=\"1.1\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" viewBox=\"-449 450 125 125\" style=\"enable-background:new -449 450 125 125;\" xml:space=\"preserve\">\n      <g id=\"XMLID_3_\">\n        <circle id=\"XMLID_7_\" class=\"circle\" cx=\"-386.5\" cy=\"512.5\" r=\"62\"/>\n        <path id=\"XMLID_4_\" class=\"a-mark\" d=\"M-356.9,537.1l-24.7-59.4c-0.7-1.7-2.1-2.6-3.9-2.6c-1.7,0-3.2,0.9-4,2.6l-27.1,65.2h9.2 l10.7-26.9l32,25.9c1.3,1,2.2,1.5,3.4,1.5c2.4,0,4.6-1.8,4.6-4.5C-356.5,538.5-356.6,537.8-356.9,537.1z M-385.4,488.4l16.1,39.6 l-24.2-19L-385.4,488.4z\"/>\n      </g>\n    </svg>\n  </div>\n    <div class=\"reveal\">\n      <div class=\"slides\">\n        <section data-state=\"cover\">\n          <p class=\"ans-logo\"><img src=\"images/ansible-wordmark-white.svg\" width=\"260\" alt=\"\" /></p>\n          <h1>Contributing to Ansible</h1>\n          <p><a href=\"https://docs.ansible.com/ansible/devel/community\">docs.ansible.com/ansible/devel/community</a></li>\n          <aside class=\"notes\">\n              <ul>\n                  <li>Thank you for taking the time</li>\n                  <li>Recorded, email video inc slides, linked from the URL on screen</li>\n                  <li>Please keep the questions coming, we hope for this to be interactive</li>\n              </ul>\n          </aside>\n         </section>\n\n        <section data-state=\"cover\">\n            <h2>Who am I?</h2>\n            <code><pre class=\"language-yaml\">\n  - name: John Barker\n    aliases: gundalow\n    title: Principal Software Engineer\n    org: Ansible, by Red Hat\n    start_date: 2016\n    roles:\n      - community\n      - core\n    freenode: gundalow\n    github: gundalow\n    twitter: the_gundalow\n    email: gundalow@redhat.com\n          </pre></code>\n          <aside class=\"notes\">\n              <ul>\n                <li>That's me, what about you</li>\n                <li>Poll results</li>\n                <!--\n                <li>SHOW OF HANDS: Who's just starting out with Ansible</li>\n                <li>SHOW OF HANDS: Who has a complex system that they want to simplify</li>\n                <li>SHOW OF HANDS: Who's got more they want to automate with Ansible</li>\n                <li>SHOW OF HANDS: Who's a contributor - raised PRs (modules, bugs, docs) - Thank you</li>\n                <li>SHOW OF HANDS: Who considers themselves an expert? - Excellent, you can answer the difficult Q&A at the end</li>\n                -->\n              </ul>\n\n          </aside>\n        </section>\n\n        <section class=\"text-center\" data-state=\"cover\">\n          <h2>THE ANSIBLE COMMUNITY</h2>\n          <p style=\"text-align: left;\">The humans who get involved, even once, make us stronger</p>\n          <p style=\"text-align: left;\">We will cover:</p>\n          <ol>\n              <li>Scale and skill set - what <i>you</i> can bring</li>\n              <li>Opportunities to help</li>\n              <li>Issue &amp; PR workflow</li>\n              <li>How to review PRs</li>\n              <li>How to join the conversation and shape the future</li>\n          </ol>\n          <aside class=\"notes\">\n              <ul>\n                  <li>Everybody watching joining in today knows things that can help us improve, share that knowledge.</li>\n                  <li>Aiming to show you the next steps you can take</li>\n                  <li>Briefly cover this topics, and show where to get further info</li>\n                  <li>Again, please use the chat and Q&amp;A features</li>\n              </ul>\n          </aside>\n        </section>\n\n        <section class=\"text-center\">\n            <p><a href=\"https://docs.ansible.com/ansible/devel/community/\">docs.ansible.com/ansible/devel/community</a></p>\n          <aside class=\"notes\">\n              <ul>\n                  <li>A lot of what we will go through today is explained in more detail on here</li>\n                  <li>Notice the URL /devel/</li>\n                  <li>Again, we will send out video and slides</li>\n                  <li>Talk through the sections, grouped by persona/activity</li>\n                  <li>Show maillist/IRC page /communication.html</li>\n                  <li>So now we know where the docs are, let's first clear some of the myths</li>\n              </ul>\n          </aside>\n        </section>\n\n        <section class=\"text-center\">\n          <h2>Myth #1: I can’t contribute because I’m not a programmer.</h2>\n          <ul>\n              <li>Can’t code (in Python)? No problem!</li>\n              <li>Module docs are YAML - You know YAML</li>\n              <li>Testing changes doesn't (necessarily) require you to understand <i>how</i> it works</li>\n              <li>Bug reports &amp; confirm fixes</li>\n          </ul>\n          <aside class=\"notes\">\n              <ul>\n                  <li>One of the biggest myths</li>\n                  <li></li>\n              </ul>\n          </aside>\n        </section>\n\n        <section class=\"text-center\">\n          <h2>Myth #2: I'm brand new to Ansible, I can't contribute</h2>\n          <ul>\n              <li>Think you have no knowledge to share? Think again!</li>\n              <li>New users can tell us how well the documentation is working</li>\n              <li>Provide feedback test, review, report bugs</li>\n              <li>If you’re new to Ansible, you have fresh eyes - keep us honest</li>\n          </ul>\n          <aside class=\"notes\">\n              <ul>\n                  <li>Once you’ve learned something, it’s hard to remember how you thought before you learned it. So new users are a vital link in the documentation chain. If you’re learning Ansible and you find something confusing, that’s important information. I guarantee you’re not the only one. You can help make the documentation better, for yourself and for the next new person. </li>\n                  <li>Soon enough, you’ll be an experienced Ansible user, and you’ll need someone else to remind you what it was like to learn.</li>\n                  <li>See something confusing, Fix it there and then, Edit on GitHub</li>\n              </ul>\n          </aside>\n        </section>\n\n        <section class=\"text-center\">\n          <h2>Myth #3: I don't know Git or GitHub</h2>\n          <ul>\n              <li>Scared of source control? Fear not!</li>\n              <li>docs.ansible.com - \"Edit on GitHub\"</li>\n              <li>You can directly from GitHub.com - demo later</li>\n              <!-- FIXME: Image of bot/guidance -->\n              <li>Worst case you make a mess of your PR - Ansibullbot will guide you</li>\n          </ul>\n          <aside class=\"notes\">\n              <ul>\n                  <li>If you don’t work with git on a daily basis, it can be intimidating to fork a repo, create a branch, commit a change, and open a pull request. Fortunately, for docs changes, GitHub can do most of the work for you. </li>\n                  <li>Contributing is a good way to learn Git &amp; GitHub</li>\n                  <li>Demo - We can see just how easy this is</li>\n                  <li>Lots of people have issues rebasing, Bot will help you </li>\n              </ul>\n          </aside>\n        </section>\n\n        <section class=\"text-center\">\n          <h2>Myth #4: Writing tests is difficult</h2>\n          <ul>\n              <li>Integration tests are just Playbooks</li>\n              <li>Extending a test case is generally fairly straight forward</li>\n              <li>Gives us confidence to merge PRs</li>\n              <li>Help us stop regressions</li>\n          </ul>\n          <aside class=\"notes\">\n              <ul>\n                  <li></li>\n              </ul>\n          </aside>\n        </section>\n\n        <section class=\"text-center\">\n          <h2>Myth #5: I need to make a commitment to stay around and support what I've done</h2>\n          <ul>\n              <li>Drive-by contributions are welcome</li>\n              <ul>\n                  <li>Clarifies documentation</li>\n                  <li>Fixes a bug</li>\n                  <li>Add a test case.</li>\n              </ul>\n              <li>When adding new modules we look for some ongoing support</li>\n          </ul>\n          <aside class=\"notes\">\n              <ul>\n                  <li>No throwing over the wall</li>\n                  <li>Also applies to WG, talk more later</li>\n              </ul>\n          </aside>\n        </section>\n\n        <section class=\"text-center\">\n          <h2>Myths: Proof</h2>\n          <ul>\n          <aside class=\"notes\">\n              <ul>\n                  <li><a href=\"https://github.com/ansible/ansible/pull/4947/files\">Gundalow's first PR</a> <a href=\"https://github.com/ansible/ansible/pull/6826/files\">2nd</a></li>\n                  <li>If you look at some of my first PRs, you can see me failing to get a clean PR after not rebasing correctly</a>\n              </ul>\n          </aside>\n        </section>\n\n        <section class=\"text-center\">\n          <h2>Scaling: The power of Community</h2>\n          <ul>\n              <li>The philosophical perspective, collectively we know everything</li>\n              <li>Some of us know lots of things</li>\n              <li>Some of us know very little</li>\n              <li>Collectively we know everything</li>\n              <li>Allows <b>you</b> to set direction</li>\n          </ul>\n          <aside class=\"notes\">\n              <ul>\n                  <li>The Ansible community is awesome... generous, engaged, knowledgeable, supportive. </li>\n                  <li>Individually, we may know a little or a lot. But collectively, we know everything about Ansible.</li>\n              </ul>\n          </aside>\n        </section>\n\n        <section class=\"text-center\">\n          <h2>Scaling: The power of Community</h2>\n          <p style=\"text-align: left;\"><b>Community</b> is the <b>force multiplier</b>.</p>\n          <p style=\"text-align: left;\"><b>You</b> are the <b>community</b>.</p>\n\n          <aside class=\"notes\">\n              <ul>\n                  <li>We are a friendly and welcoming community.</li>\n              </ul>\n          </aside>\n        </section>\n\n        <section class=\"text-center\">\n          <h2>Scaling: Core Teams remit</h2>\n          <ul>\n              <li>The Ansible Core Team focus on the Engine and main modules</li>\n              <li>Core: 300 modules</li>\n              <li>Community: 2000 modules</li>\n              <li>Reviewing Community issues &amp; PRs are the communities responsibility</li>\n          </ul>\n          <aside class=\"notes\">\n              <ul>\n                  <li>Core Team is focusing on the Core Engine and language features</li>\n                  <li>Ansible is a plugin engine, that's what helps us scale</li>\n                  <li>Don't expect the guy in the sky (someone with authority) to do reviews</li>\n                  <li>We want to empower the community</li>\n              </ul>\n          </aside>\n        </section>\n<!--\n        <section class=\"text-center\">\n          <h2>Scaling: Proof that it works</h2>\n          <ul>\n              <li>FIXME: Insert Docker graph here</li>\n              <li>Add some details</li>\n              <li>Not just about PRs merged, it's about the whole community benefiting - Improved Docs, integration tests to give us confidence in future work</li>\n              <li>Confidence that Ansible does what it needs to do</li>\n          </ul>\n          <aside class=\"notes\">\n              <ul>\n                  <li>Notice backlog ($COLOUR) drops, so the backlog is reducing</li>\n                  <li>Number of Issues &amp; PRs ($COLOUR), increases</li>\n              </ul>\n          </aside>\n        </section>\n-->\n<!-- PAUSE -->\n        <section class=\"text-center\">\n          <p>Pause for questions</p>\n          <aside class=\"notes\">\n              <ul>\n                  <li></li>\n              </ul>\n          </aside>\n        </section>\n<!-- Ansible's Development Process -->\n\n<!-- FIXME Show actual examples of issues and PRs\n* Really good description of problem, shows error message\n\n* PR: Has link to issue (for context and shows GH project management)\n* Issues & PR can be document as well as units of our working life\n\n* Talk about GH Labels, move slide here\n-->\n        <section class=\"text-center\" data-state=\"cover\">\n          <h2>GitHub Issues and PRs</h2>\n        </section>\n\n        <section class=\"text-center\">\n          <h2>GitHub Issues and PRs</h2>\n          <ul>\n              <li>GitHub Issues/PRs or it never happened</li>\n              <li>Issues</li>\n              <ul>\n                  <li>Bug reports and feature ideas - Details</li>\n                  <li>Discussion before implementation starts</li>\n\n              </ul>\n              <li>Pull Requests (PRs)</li>\n              <ul>\n                  <li>Improve a one thing: Fix <b>a</b> bug/feature</li>\n                  <li>Always use feature branches</li>\n              </ul>\n              <li>If you know the fix, just create a PR</li>\n              <li>HELP: <a href=\"https://lab.github.com\">lab.github.com</a>, <a href=\"https://guides.github.com\">guides.github.com</a></li>\n          </ul>\n          <aside class=\"notes\">\n              <ul>\n                  <li>How everything is tracked</li>\n                  <li>Mail lists, Twitter, etc are good for discussion. Though unless it ends up in GitHub it will not be done</li>\n                  <li>Not for discussion, use the email lists, IRC, stack overflow, for that. Remember /communication.html we showed earlier</li>\n                  <li>Bugs cannot be triaged without steps to reproduce + details on expected / actual results</li>\n              </ul>\n          </aside>\n        </section>\n\n        <section class=\"text-center\">\n          <h2>Ansible's Development Process</h2>\n          <ol>\n              <li>Triage</li>\n              <li>Cycle: Fix tests, get reviews, address review comments</li>\n              <li>Merged</li>\n              <li>Backport bug fixes [optional]</li>\n          </ol>\n          <aside class=\"notes\">\n              <ul>\n                  <li>Showing PR process. Issues is similar, though simpler</li>\n                  <li>PRs often get stuck due to lack of reviews</li>\n              </ul>\n          </aside>\n        </section>\n\n        <section class=\"text-center\">\n          <h2>Development Process: PR Life Cycle Demo</h2>\n          <aside class=\"notes\">\n              <ul>\n                  <li><a href=\"https://github.com/ansible/ansible/pull/46151\">#46151</a></li>\n                  <li>Description</li>\n                  <li>Labels: Added by Bot, scale &amp; search, explain each of them</li>\n                  <li>Maintainers pinged</li>\n                  <li>Details of manual testing: (1st Nov 2018)</li>\n                  <li>Review Cycle: Back and forth to fix issues</li>\n                  <li>Every one should be willing to do review, so lets talk about how to find PRs and review them</li>\n              </ul>\n          </aside>\n        </section>\n\n<!-- PAUSE -->\n        <section class=\"text-center\">\n          <p>Pause for questions</p>\n          <aside class=\"notes\">\n              <ul>\n                  <li></li>\n              </ul>\n          </aside>\n        </section>\n\n        <section data-state=\"cover\">\n            <h2>Who am I?</h2>\n            <code><pre class=\"language-yaml\">\n  - name: Alicia Cozine\n    title: Lead Technical Writer\n    org: Ansible, by Red Hat\n    start_date: 2018\n    roles:\n      - docs\n      - core\n      - community\n    freenode: acozine\n    github: acozine\n    email: acozine@redhat.com\n          </pre></code>\n        </section>\n\n        <section class=\"text-center\">\n          <h2>Documentation Demo</h2>\n          <p><a href=\"https://docs.ansible.com/ansible/devel/community/documentation_contributions.html\">docs.ansible.com/ansible/devel/community/documentation_contributions.html</a></p>\n          <p><a href=\"https://github.com/ansible/community/wiki/Docs\">github.com/ansible/community/wiki/Docs</a> Docs Working Group</p>\n          <aside class=\"notes\">\n              <ul>\n                  <li>How to fix an Docs issue</li>\n                  <li>Example of docs Working Group</li>\n              </ul>\n          </aside>\n        </section>\n\n<!-- PAUSE -->\n        <section class=\"text-center\">\n          <p>Pause for questions</p>\n          <aside class=\"notes\">\n              <ul>\n                  <li></li>\n              </ul>\n          </aside>\n        </section>\n\n\n\n        <section class=\"text-center\">\n          <h2>Development Process: Finding Issues &amp; PRs</h2>\n          <ul>\n              <li>GitHub Labels</li>\n              <ul>\n                  <li><a href=\"https://github.com/ansible/ansible/labels\">github.com/ansible/ansible/labels</a></li>\n                  <li>bug, feature, module, test, plugin</li>\n                  <li>Labels for each Working Group</li>\n                  <li>lib/modules/cloud/docker/docker_image = [module, cloud, docker]</li>\n              </ul>\n              <li><a href=\"https://ansible.sivel.net/pr/byfile.html\">ansible.sivel.net/pr/byfile.html</a></li>\n              <ul>\n                  <li>Search by filename (or directory) to find PRs</li>\n                  <li>Great for finding related PRs</li>\n              </ul>\n              <li><a href=\"https://github.com/ansible/community/wiki\">github.com/ansible/community/wiki</a></li>\n          </ul>\n          <aside class=\"notes\">\n              <ul>\n                  <li>As we talked about earlier, review Issues and PRs, with whatever skills you bring <b>is</b> really useful, so where do <b>you</b> start</li>\n                  <li>FIXME: Give some useful links that people can use small_patch, bug+is:pr</li>\n                  <li><a href=\"https://github.com/ansible/ansible/labels\">labels -> AIX</a> </li>\n                  <li><a href=\"https://ansible.sivel.net/pr/byfile.html\">ansible.sivel.net/pr/byfile.html</a></li>\n              </ul>\n          </aside>\n        </section>\n<!-- START: PR Review -->\n\n\n<!-- Need to flesh this out -->\n\n        <section class=\"text-center\">\n          <h2>Development Process: Aims of reviews</h2>\n          <ul>\n              <li>We need confidence before code can be merged</li>\n              <li>We need user feedback to tell us if this is right</li>\n              <li>PR Backlog size due to lack of review - WG &amp; <a href=\"https://github.com/ansible/community/issues/407\">Big PR Review Day 2019-02-21</a></li>\n              <li>Anyone can help with this</li>\n          </ul>\n          <aside class=\"notes\">\n              <ul>\n                  <li>Previously mentioned, any skill set can help</li>\n                  <li>Community <a href=\"https://github.com/ansible/community/issues/407\">PR review days</a>, 2019-02-21</li>\n                  <li>First point sums up why PRs don't get merged</li>\n                  <li>Testing (manual/automated) gives us confidence</li>\n              </ul>\n          </aside>\n        </section>\n\n        <section class=\"text-center\">\n          <h2>Development Process: Types of review</h2>\n          <p style=\"text-align: left;\">Whatever your skill set, you can help with at least one of these:</p>\n          <ol>\n              <li>Functional review</li>\n              <li>Documentation reviews</li>\n              <li>Code reviews</li>\n          </ol>\n          <aside class=\"notes\">\n              <ul>\n                  <li>One thing I think is often overlooked here is that you don't need to be a programmers, or have any understanding of the internals to do a functional or documentation review.</li>\n                  <li>We know the \"how to review\" parts of Ansible's Docs need some work, what would you like to see there? - give comments in Chat or join in #ansible-community</li>\n                  <li>I accept some of this is subjective, so we are looking for a net improvement</li>\n              </ul>\n          </aside>\n        </section>\n\n\n        <section class=\"text-center\">\n          <h2>Reviews #1: Functional Reviews</h2>\n          <ul>\n             <li>Does it work?</li>\n             <li>Address a use case you have?</li>\n             <li>Does it break any edge/use case you have?</li>\n             <li>Is the interface clear</li>\n          </ul>\n          <aside class=\"notes\">\n              <ul>\n              </ul>\n          </aside>\n        </section>\n\n        <section class=\"text-center\">\n          <h2>Reviews #2: Documentation Reviews</h2>\n          <ul>\n              <li>Are the docs clear, is there any ambiguity?</li>\n              <li>Do the examples work?</li>\n              <li>Is there any missing knowledge?</li>\n              <li>Feedback from newer user is really important</li>\n          </ul>\n          <aside class=\"notes\">\n              <ul>\n              </ul>\n          </aside>\n        </section>\n\n        <section class=\"text-center\">\n          <h2>Reviews #3: Code Reviews</h2>\n          <ul>\n              <li>Best use of existing APIs</li>\n              <li>Following the \"the Ansible way\" (variable names, etc.)</li>\n              <li>Honor Ansible's coding guidelines?</li>\n              <li>Follow the CRUD principles (Create, Read, Update, Delete)?</li>\n          </ul>\n          <aside class=\"notes\">\n              <ul>\n              </ul>\n          </aside>\n        </section>\n\n\n\n\n\n\n\n        <section class=\"text-center\">\n          <h2>Development Process: How to review</h2>\n          <ul>\n              <li>Three part review:</li>\n              <ol>\n                  <li>Is the idea behind the contribution sound?</li>\n                  <li>Is the contribution architected correctly?</li>\n                  <li>Is the contribution polished?</li>\n              </ol>\n              <li>Be mindful:</li>\n              <ul>\n                  <li>Go above and beyond to promote a collaborative and respectful community</li>\n                  <li>\"Would this be clearer if...\" - invites discussion</li>\n                  <li>Avoid bikeshedding. Ansible's coding guidelines is enforced by CI. </li>\n              </ul>\n          </ul>\n          <aside class=\"notes\">\n              <ul>\n                  <li>Reviewing PRs is one of the most beneficial things to do</li>\n                  <li>Blog: <a href=\"https://sage.thesharps.us/2014/09/01/the-gentle-art-of-patch-review/\">The Gentle Art of Patch Review</a></li>\n                  <li>contribution sound? - Clear, consistent user interface</li>\n                  <li>polished? - Docs, Tests, etc</li>\n                  <li>Mindful</li>\n                  <li>We know the \"how to review\" parts of Ansible's Docs need some work, what would you like to see there? - give comments in Chat or join the discussion in #ansible-community</li>\n              </ul>\n          </aside>\n        </section>\n\n<!--\n    We saw an example as part of https://github.com/ansible/ansible/pull/46151\n        <section class=\"text-center\">\n          <h2>Development Process: How to review - example</h2>\n          <ul>\n              <li>FIXME: Screenshot/code-snippit of a good feedback</li>\n              <li>FIXME: Details of what and how it was tested, mindful comments</li>\n          </ul>\n          <aside class=\"notes\">\n              <ul>\n                  <li>So lets see this in action</li>\n              </ul>\n          </aside>\n        </section>\n-->\n<!-- END: PR Review -->\n\n<!-- STAR: WORKING GROUPS -->\n\n        <section class=\"text-center\">\n          <h2>Working Groups: List</h2>\n          <ul>\n              <li>General: Community, Core, Documentation, Testing</li>\n              <li>Cloud Providers and VMs: AWS, Azure, Docker, Linode, VMware</li>\n              <li>Network</li>\n              <li>Operating Systems: AIX, BSD, HP-UX, Linux, Solaris, Windows</li>\n              <li>Related projects: ansible-lint, AWX (Tower), Galaxy, Molecule</li>\n              <li>Plus many more...</li>\n              <li>Full list: <a href=\"https://github.com/ansible/community/wiki\">github.com/ansible/community/wiki</a></li>\n          </ul>\n          <aside class=\"notes\">\n              <ul>\n                  <li>BROWSER: <a href=\"https://github.com/ansible/community/wiki\">github.com/ansible/community/wiki</a></li>\n                  <li>We've mentioned this a few times, but what are they?</li>\n                  <li>Some are cross-functional: Core, Community, Docs, Testing,</li>\n                  <li>Mention <b>each</b> WG one by one, saying what they are working on/achieved</li>\n              </ul>\n          </aside>\n        </section>\n\n        <section class=\"text-center\">\n          <h2>Working Groups: Purpose</h2>\n          <p style=\"text-align: left;\">Are you willing to help advance the thing that is obviously stuck and needs people united in order to get unstuck</p>\n          <ul>\n              <li>Focused effort on a specific topic</li>\n              <li>Help build communities</li>\n              <li>Allows people closer to the technology to set direction</li>\n              <li>Make Ansible do what <b>you</b> want, solve <b>your</b> challenges</li>\n              <li>Empowers\n          </ul>\n          <aside class=\"notes\">\n              <ul>\n                  <li>We've mentioned this a few times, but what are they?</li>\n                  <li>Distributed across the globe, like Ansible's Core Team</li>\n                  <li>We are looking for people interested in making/helping build communities around technologies, as Ansible is huge, it's impossible to leave this up to one person, or a core Ansible team</li>\n              </ul>\n          </aside>\n        </section>\n\n        <section class=\"text-center\">\n          <h2>Working Groups: How?</h2>\n          <ul>\n              <li>Mix of skill sets required</li>\n              <li>Maybe spread across the globe</li>\n              <li>Communicate via GitHub Issues/PRs/Wiki</li>\n              <li>May have a dedicated IRC channel</li>\n              <li>May have IRC Meetings (recorded)</li>\n              <li>No regular commitment required</li>\n              <li>I can help you set this up</li>\n          </ul>\n          <aside class=\"notes\">\n              <ul>\n                  <li>Not asking for n-hours a week </li>\n                  <li>As specially early on even a small amount of time makes a huge benefit, low hanging fruit</li>\n                  <li>Feel free to message me directly, we have ideas of other groups</li>\n              </ul>\n          </aside>\n        </section>\n\n\n\n\n\n<!-- END: WORKING GROUPS -->\n\n\n\n\n\n        <section>\n            <h2>Recap</h2>\n            <ul>\n                <li><a href=\"https://docs.ansible.com/ansible/devel/community\">docs.ansible.com/ansible/devel/community</a> Community guides</li>\n                <li><a href=\"https://github.com/ansible/community/wiki\">github.com/ansible/community/wiki</a> Join a working Group</li>\n                <li><a href=\"https://github.com/ansible/community/issues/407\">github.com/ansible/community#407</a> Big PR Review Day 2019-02-21</li>\n                <li><a href=\"https://ansible.com/events\">ansible.com/events</a>, <a href=\"https://ansible.meetup.com\">ansible.meetup.com</a></li>\n                <li><a href=\"https://ansible.com/ansiblefest\">ansible.com/ansiblefest</a> Ansible Fest Atlanta 24-26 Sept 2019</li>\n                <li><a href=\"mailto:gundalow@redhat.com\">gundalow@redhat.com</a> &amp; <a href=\"mailto:acozine@redhat.com\">acozine@redhat.com</a> for feedback, help or mentoring</li>\n            </ul>\n          <aside class=\"notes\">\n          </aside>\n        </section>\n        <section>\n            <h2>Thank You!</h2>\n            <ul>\n                <li><a href=\"https://docs.ansible.com/ansible/devel/community\">docs.ansible.com/ansible/devel/community</a> Community guides</li>\n                <li><a href=\"https://github.com/ansible/community/wiki\">github.com/ansible/community/wiki</a> Join a working Group</li>\n                <li><a href=\"https://github.com/ansible/community/issues/407\">github.com/ansible/community#407</a> Big PR Review Day 2019-02-21</li>\n                <li><a href=\"https://ansible.com/events\">ansible.com/events</a>, <a href=\"https://ansible.meetup.com\">ansible.meetup.com</a></li>\n                <li><a href=\"https://ansible.com/ansiblefest\">ansible.com/ansiblefest</a> Ansible Fest Atlanta 24-26 Sept 2019</li>\n                <li><a href=\"mailto:gundalow@redhat.com\">gundalow@redhat.com</a> &amp; <a href=\"mailto:acozine@redhat.com\">acozine@redhat.com</a> for feedback, help or mentoring</li>\n            </ul>\n          <aside class=\"notes\">\n          </aside>\n        </section>\n\n      </div>\n    </div>\n\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/lib/js/head.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/js/reveal.js\"></script>\n\n    <script>\n      // More info https://github.com/hakimel/reveal.js#configuration\n      Reveal.initialize({\n        history: true,\n        width: \"85%\",\n        height: \"90%\",\n        transition: \"fade\",\n        slideNumber: true,\n\n        // More info https://github.com/hakimel/reveal.js#dependencies\n        // Notes plugin must remain local for now.\n        // See https://github.com/ansible/lightbulb/issues/125\n        dependencies: [\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/plugin/markdown/marked.js' },\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/plugin/markdown/markdown.js' },\n          { src: 'plugin/notes/notes.js', async: true },\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/prism.min.js'},\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/components/prism-yaml.min.js'}\n          //{ src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }\n        ]\n      });\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "decks/css/theme/ansible.css",
    "content": "/**\n * An ANSIBLE theme for reveal.js presentations, similar\n * to the simple theme. \n */\n@import url(https://fonts.googleapis.com/css?family=News+Cycle:400,700);\n@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);\n\n@import url('https://fonts.googleapis.com/css?family=Open+Sans:400,700');\n\n/*********************************************\n * GLOBAL STYLES\n *********************************************/\n\n*{\n  outline: none;\n}\n\nbody {\n  background: #fff;\n  background-color: #fff;\n}\n\nmark{\n  background: #5bbdbf;\n}\n\nmark span.hljs-number{\n  color: #286669;\n}\n\nmark span.hljs-symbol,\nmark span.hljs-string{\n  color: #af2e2e;\n}\n\n.reveal {\n  font-family: \"Open Sans\", sans-serif;\n  font-size: 28px;\n  font-weight: normal;\n  color: #555;\n  text-align: left;\n}\n\n.reveal .slides{\n  text-align: left;\n}\n\n::selection {\n  color: #fff;\n  background: rgba(0, 0, 0, 0.99);\n  text-shadow: none;\n}\n\n.reveal .slides > section,\n.reveal .slides > section > section {\n  line-height: 1.3;\n  font-weight: inherit;\n}\n  \n  \n.text-center{\n  text-align: center;\n}\n\n.text-small{\n  font-size: 70% !important;\n}\n\n/*********************************************\n * HEADERS\n *********************************************/\n.reveal h1,\n.reveal h2,\n.reveal h3,\n.reveal h4,\n.reveal h5,\n.reveal h6 {\n  margin: 0 0 20px 0;\n  color: #cc0000;\n  font-family: \"Open Sans\", Impact, sans-serif;\n  font-weight: bold;\n  letter-spacing: normal;\n  text-transform: none;\n  text-shadow: none;\n  margin-bottom: 0.6em;\n  word-wrap: break-word; }\n\n.reveal h1 {\n  font-size: 2em;\n}\n\n.cover .reveal h1{\n  margin: 1em 0;\n}\n\n.reveal h2 {\n  font-size: 1.35em; }\n\n.reveal h3 {\n  font-size: 1.2em;\n  color: #555;\n}\n\n.reveal h4 {\n  font-size: 1em;\n  color: #555;\n}\n\n.reveal h1 {\n  text-shadow: none; }\n\n/*********************************************\n * OTHER\n *********************************************/\n.reveal p {\n  margin: 20px 0 0.5em;\n  line-height: 1.3; }\n\n/* Ensure certain elements are never larger than the slide itself */\n.reveal img,\n.reveal video,\n.reveal iframe {\n  max-width: 95%;\n  max-height: 95%; }\n\n.reveal strong,\n.reveal b {\n  font-weight: bold; }\n\n.reveal em {\n  font-style: italic; }\n\n.reveal ol,\n.reveal dl,\n.reveal ul {\n  /*display: inline-block;*/\n  text-align: left;\n  margin: 0 0 0 1em; }\n  \n.reveal li{\n  margin: 0 0 0.3em;\n}\n\n.reveal ol {\n  list-style-type: decimal; }\n\n.reveal ul {\n  list-style-type: disc; }\n\n.reveal ul ul {\n  list-style-type: square; }\n\n.reveal ul ul ul {\n  list-style-type: circle; }\n\n.reveal ul ul,\n.reveal ul ol,\n.reveal ol ol,\n.reveal ol ul {\n  display: block;\n  margin-left: 40px; }\n\n.reveal dt {\n  font-weight: bold; }\n\n.reveal dd {\n  margin-left: 40px; }\n\n.reveal q,\n.reveal blockquote {\n  quotes: none; }\n\n.reveal blockquote {\n  display: block;\n  position: relative;\n  width: 70%;\n  margin: 20px auto;\n  padding: 5px;\n  font-style: italic;\n  background: rgba(255, 255, 255, 0.05);\n  /*box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2);*/\n}\n\n.reveal blockquote p:first-child,\n.reveal blockquote p:last-child {\n  display: inline-block; }\n\n.reveal q {\n  font-style: italic; }\n\n.reveal pre,\n.reveal pre[class*=language-] {\n  display: block;\n  position: relative;\n  margin: 10px auto;\n  text-align: left;\n  font-size: 0.55em;\n  font-family: monospace;\n  line-height: 1em;\n  word-wrap: break-word;\n  box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3);\n  padding: 0px;\n}\n\n.reveal code {\n  font-family: monospace; }\n\n.reveal pre code {\n  display: block;\n  padding: 5px;\n  overflow: auto;\n  max-height: 55vh;\n  word-wrap: normal;\n}\n\n.reveal table {\n  margin: auto;\n  border-collapse: collapse;\n  border-spacing: 0; }\n\n.reveal table th {\n  font-weight: bold; }\n\n.reveal table th,\n.reveal table td {\n  text-align: left;\n  padding: 0.2em 0.5em 0.2em 0.5em;\n  border-bottom: 1px solid; }\n\n.reveal table th[align=\"center\"],\n.reveal table td[align=\"center\"] {\n  text-align: center; }\n\n.reveal table th[align=\"right\"],\n.reveal table td[align=\"right\"] {\n  text-align: right; }\n\n.reveal table tbody tr:last-child th,\n.reveal table tbody tr:last-child td {\n  border-bottom: none; }\n\n.reveal sup {\n  vertical-align: super; }\n\n.reveal sub {\n  vertical-align: sub; }\n\n.reveal small {\n  display: inline-block;\n  font-size: 0.6em;\n  line-height: 1.2em;\n  vertical-align: top; }\n\n.reveal small * {\n  vertical-align: top; }\n\n/*********************************************\n * LINKS\n *********************************************/\n.reveal a {\n  color: #00008B;\n  text-decoration: none;\n  -webkit-transition: color .15s ease;\n  -moz-transition: color .15s ease;\n  transition: color .15s ease; }\n\n.reveal a:hover {\n  color: #0000f1;\n  text-shadow: none;\n  border: none; }\n\n.reveal .roll span:after {\n  color: #fff;\n  background: #00003f; }\n\n/*********************************************\n * IMAGES\n *********************************************/\n.reveal section img {\n  margin: 15px 0px;\n  background: rgba(255, 255, 255, 0.12);\n  max-height: 500px;\n}\n\n.reveal section img.plain {\n  border: 0;\n  box-shadow: none; }\n\n.reveal a img {\n  -webkit-transition: all .15s linear;\n  -moz-transition: all .15s linear;\n  transition: all .15s linear; }\n\n.reveal a:hover img {\n  background: rgba(255, 255, 255, 0.2);\n  border-color: #00008B;\n  box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); }\n\n/*********************************************\n * NAVIGATION CONTROLS\n *********************************************/\n\n.reveal .controls {\n    bottom: auto;\n    top: 10px;\n}\n\n\n.reveal .controls .navigate-left,\n.reveal .controls .navigate-left.enabled {\n  border-right-color: #00008B; }\n\n.reveal .controls .navigate-right,\n.reveal .controls .navigate-right.enabled {\n  border-left-color: #00008B; }\n\n.reveal .controls .navigate-up,\n.reveal .controls .navigate-up.enabled {\n  border-bottom-color: #00008B; }\n\n.reveal .controls .navigate-down,\n.reveal .controls .navigate-down.enabled {\n  border-top-color: #00008B; }\n\n.reveal .controls .navigate-left.enabled:hover {\n  border-right-color: #0000f1; }\n\n.reveal .controls .navigate-right.enabled:hover {\n  border-left-color: #0000f1; }\n\n.reveal .controls .navigate-up.enabled:hover {\n  border-bottom-color: #0000f1; }\n\n.reveal .controls .navigate-down.enabled:hover {\n  border-top-color: #0000f1; }\n\n/*********************************************\n * PROGRESS BAR\n *********************************************/\n.reveal .progress {\n  background: rgba(0, 0, 0, 0.2);\n}\n\n.reveal .progress span {\n  background: #00008B;\n  -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);\n  -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);\n  transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);\n}\n\n\n/*********************************************\n * CUSTOM STYLES\n *\n *\n * colors:\n *\n * Pool -   #5bbdbf;\n * Mango -  #ff5850;\n *********************************************/\n.reveal section .ans-logo img{\n    box-shadow: none;\n    border: none;\n    margin: 0px;\n    background: none;\n}\n\n.cover div.ans-logo{\n  display: block;\n}\n\ndiv.ans-mark{\n  position: absolute;\n  width: 6vw; /*vertical-width*/\n  bottom: 0.5em;\n  right: 1em;\n  z-index: 9999;\n}\n\ndiv.ans-mark .circle{\n  fill:#CC0000;\n}\n\n.cover div.ans-mark .circle,\n.title.alt div.ans-mark .circle{\n  fill:#000;\n}\n\nsvg .a-mark{\n  fill:#FFFFFF;\n}\n\n.reveal {\n    box-sizing: border-box;\n    transition: all 300ms ease-in-out;\n}\n\n.reveal p,\n.reveal li{\n  width: 80%;\n}\n\n.reveal p.fullwidth,\n.reveal .col p,\n.reveal .col li,\n.reveal aside p{\n  width: 100%;\n}\n\n.reveal ul,\n.reveal ol{\n  margin-left: 2em;\n}\n\n.slide-background.present{\n  background-color: #fff;\n}\n\n.cover .slide-background.present,\n.title.alt .slide-background.present{\n  background-color: #cc0000;\n  display: block !important;\n}\n\n.cover .present,\n.cover .present h1,\n.cover .present h2,\n.cover .present h3,\n.cover .present h4,\n.cover .present h5,\n.cover .present h6,\n.cover .present p,\n.cover .present li{\n  color: #fff;\n}\n\n.title .reveal h1,\n.title .reveal h2,\n.title .reveal h3,\n.title .reveal h4{\n  color: #cc0000;\n}\n\n\n.title.alt .reveal h1,\n.title.alt .reveal h2,\n.title.alt .reveal h3,\n.title.alt .reveal h4,\n.title.alt .reveal p{\n  color: #fff;\n}\n\n.columns{\n  display: flex;\n  justify-content: flex-start;\n}\n\n.reveal .columns > *{\n  flex-basis: 31%;\n  margin-right: 2.333%;\n  flex-grow: 1;\n}\n\n.reveal .progress span {\n  background: #ff5850;\n}\n\n.monospace{\n  font-family: courier, monospace !important;\n}\n\n\n/*********************************************\n * CONTROLS\n *********************************************/\n\n.reveal .controls .navigate-left, .reveal .controls .navigate-left.enabled{\n  border-right-color: #ccc;\n}\n.reveal .controls .navigate-left.enabled:hover {\n    border-right-color: #999;\n    opacity: 0.5;\n}\n\n.reveal .controls .navigate-right, .reveal .controls .navigate-right.enabled{\n  border-left-color: #ccc;\n}\n.reveal .controls .navigate-right.enabled:hover {\n    border-left-color: #999;\n    opacity: 0.5;\n}\n\n.reveal .controls .navigate-down, .reveal .controls .navigate-down.enabled {\n  border-top-color: #ccc;\n}\n.reveal .controls .navigate-down.enabled:hover {\n    border-top-color: #999;\n    opacity: 0.5;\n}\n\n.reveal .controls .navigate-up, .reveal .controls .navigate-up.enabled {\n  border-bottom-color: #ccc;\n}\n\n.reveal .controls .navigate-up.enabled:hover {\n    border-bottom-color: #999;\n    opacity: 0.5;\n}\n\n.reveal .controls button {\n  opacity: 0.1;\n}\n\n\n\n/*********************************************\n * PRINT\n *********************************************/\n\n@media print{\n  /*@page {size: landscape}*/\n  \n  .ans-logo{\n    padding: 20px !important;\n    background: #c00;\n    display: inline-block;\n  }\n  \n  .reveal,\n  .reveal h1,\n  .reveal h2,\n  .reveal h3,\n  .reveal h4,\n  .reveal h5,\n  .reveal h6,\n  .reveal p,\n  .reveal ul,\n  .reveal ol,\n  .reveal li,\n  .reveal blockquote{\n    font-family: \"Open Sans\", sans-serif !important;\n    color: #000 !important;\n  }\n  \n  .reveal blockquote{\n    font-size: 20px !important;\n    border: none !important;\n  }\n  \n  .reveal p,\n  .reveal li{\n    font-size: 20px !important;\n  }\n  \n  .reveal img{\n    border: none !important;\n  }\n  \n  .reveal .columns .col {\n    max-width: 50%;\n  }\n  \n  .reveal pre code{\n    max-height: none !important;\n  }\n  \n  .reveal section aside.notes {\n    display: block;\n    border-top: 1px solid black;\n    margin-top: 60px;\n    padding-top: 20px;\n  }\n  \n  .reveal section aside.notes *{\n    font-size: 14px !important;\n    font-family: sans-serif;\n  }\n\n  div.ans-mark{\n    position: fixed;\n    width: 6vw; /*vertical-width*/\n    z-index: 9999;\n  }\n\n}\n\n/*\n@media print and (orientation:landscape) {\n  .reveal section aside.notes {\n    display: none !important;\n  }\n}\n*/"
  },
  {
    "path": "decks/intro-to-ansible-tower.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">\n\n    <title>Introduction to Ansible Tower</title>\n\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/css/reveal.css\">\n\n    <!-- Printing and PDF exports -->\n    <script>\n      var link = document.createElement( 'link' );\n      link.rel = 'stylesheet';\n      link.type = 'text/css';\n      link.href = window.location.search.match( /print-pdf/gi ) ? 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/css/print/pdf.css' : 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/css/print/paper.css';\n      document.getElementsByTagName( 'head' )[0].appendChild( link );\n    </script>\n\n\n    <link rel=\"stylesheet\" href=\"css/theme/ansible.css\">\n\n    <!-- Theme used for syntax highlighting of code -->\n    <!--link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/lib/css/zenburn.css\"-->\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/themes/prism.min.css\">\n\n\n  </head>\n  <body>\n  <div class=\"ans-mark\">\n    <svg version=\"1.1\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" viewBox=\"-449 450 125 125\" style=\"enable-background:new -449 450 125 125;\" xml:space=\"preserve\">\n      <g id=\"XMLID_3_\">\n        <circle id=\"XMLID_7_\" class=\"circle\" cx=\"-386.5\" cy=\"512.5\" r=\"62\"/>\n        <path id=\"XMLID_4_\" class=\"a-mark\" d=\"M-356.9,537.1l-24.7-59.4c-0.7-1.7-2.1-2.6-3.9-2.6c-1.7,0-3.2,0.9-4,2.6l-27.1,65.2h9.2 l10.7-26.9l32,25.9c1.3,1,2.2,1.5,3.4,1.5c2.4,0,4.6-1.8,4.6-4.5C-356.5,538.5-356.6,537.8-356.9,537.1z M-385.4,488.4l16.1,39.6 l-24.2-19L-385.4,488.4z\"/>\n      </g>\n    </svg>\n  </div>\n    <div class=\"reveal\">\n      <div class=\"slides\">\n      <section data-state=\"cover\">\n          <p class=\"ans-logo\"><img src=\"images/ansible-wordmark-white.svg\" width=\"260\" alt=\"\" /></p>\n          <h1>Introduction to Ansible Tower</h1>\n          <!--p>NAME HERE, TITLE HERE</p>\n          <p>COMPANY HERE</p-->\n      </section>\n        <section>\n          <h2>What You Will Learn</h2>\n          <p>Red Hat<sup>&reg;</sup> Ansible<sup>&reg;</sup> Tower helps you scale IT automation, manage complex deployments and speed productivity. Centralize and control your IT infrastructure with a visual dashboard, role-based access control, job scheduling and graphical inventory management.</p><br>\n          <ul>\n            <li>What is Ansible Tower</li>\n            <li>How Ansible Tower Works</li>\n            <li>Installing Ansible Tower</li>\n            <li>Key Features</li>\n          </ul>\n          <aside class=\"notes\">\n            <p>This deck was designed to provide students with a general overview of how Ansible core knowledge can be transitioned into Ansible Tower.</p>\n            <p>This deck supports lecture and hands-on forms of presenting this material. </p>\n            <p>Allow 2 hours to deliver the lecture-based form and 4 hours for stopping to do the workshop assignments. To access the additional slides for delivering the workshops, navigate down when available. </p>\n            <p>See the <a href=\"../facilitator/README.md\">Ansible Lightbulb facilitator&rsquo;s guide</a> for more details on using this deck and it&rsquo;s associated material.</p>\n          </aside>\n        </section>\n        <section>\n            <h2>What is Ansible Tower?</h2>\n            <p>Ansible Tower is an <strong>enterprise framework</strong> for controlling, securing and managing your Ansible automation – with a <strong>UI and RESTful API</strong></p><br>\n            <ul>\n              <li><strong>Role-based access control</strong> keeps environments secure, and teams efficient</li>\n              <li>Non-privileged users can <strong>safely deploy</strong> entire applications with <strong>push-button deployment</strong> access</li>\n              <li>All Ansible automations are <strong>centrally logged</strong>, ensuring <strong>complete auditability and compliance</strong></li>\n            </ul>\n            <aside class=\"notes\">\n              <p>Red Hat Ansible Tower offers the control around Ansible automations in the form of RBAC, Job Scheduling, security abstraction from the user and API integrations with other systems.</p>\n              <p>In Ansible Tower, all Ansible job launches are tracked and logged centrally. What was changed and when it was changed can be viewed, exported to other logging capabilities and controlled within allowances of a user's access based on role.</p>\n            </aisde>\n        </section>\n        <section>\n          <img src=\"images/ansible-tower-platform-diagram.svg\">\n          <aside class=\"notes\">\n            <p>Ansible Core brought you simple and powerful automation, Ansible Tower provides a visual interface to scale out that power. Run jobs against multiple inventories at the same time and schedule jobs to run when you want them too.</p>\n            <p>With Core, everyone has their own playbooks stored locally or in their own SCM tool. With Ansible Tower, everyone's playbooks can be centralized to one location. Control what teams can see what with RBAC to resources and projects.</p>\n            <p>Launch playbooks with the click of a button and see the expected results for that same job to compare and confirm the desired outcome.\n          </aside>\n        </section>\n        <section>\n          <h2>Platform Overview</h2>\n          <img src=\"images/ansible-platform-overview.svg\"/>\n          <aside class=\"notes\">\n            <p>\n          </aside>\n        </section>\n        <section>\n          <h2>Installing Ansible Tower</h2>\n          <pre class=\"language-yaml\"><code>\n# the most common and preferred way of\n# installation for RHEL (Preferred) or Ubuntu\n$ wget https://bit.ly/ansibletower\n\n# bundled installer can be downloaded for\n# RHEL (and select derivatives) at\n$ wget https://bit.ly/ansibletowerbundle\n\n# looking for a specific version? navigate to http://releases.ansible.com/ansible-tower\n# to see all the versions available for download\n          </code></pre>\n          <aside class=\"notes\">\n            <p>Installation methods listed here are the most common used. Please note: Do not install Ansible Core via pip before installing Ansible Tower. Ansible Tower expects Ansible Core to be installed via package manager - and it will do this for you.</p>\n            <p>The standard installer can be used with normal internet access but if you are in a restricted network environment, the bundled installer will be your best choice. Please note that there the bundled installer currently only supports RHEL Based systems.</p>\n            <p>There are also Vagrant Images and AWS AMIs available. To find out more, visit ansible.com/tower-trial</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Server Requirements</h2>\n          <ul>\n            <li>Red Hat Enterprise Linux (RHEL) 7 (and select derivatives), Ubuntu 14.04 64-bit, and Ubuntu 16.04 LTS 64-bit support required (kernel and runtime).</li>\n            <li>A currently supported version of Mozilla Firefox or Google Chrome.</li>\n            <li>2 GB RAM minimum (4+ GB RAM highly recommended)</li>\n            <li>20 GB of dedicated hard disk space</li>\n          </ul>\n          <aside class=\"notes\">\n            <p>Unlike Ansible Core, Ansible Tower cannot be installed locally on your laptop, it must be installed in a server environment.</p>\n            <p>Other HTML5 compliant web browsers may work but are not fully tested or supported.</p>\n            <p>10 GB of the 20 GB requirement must be dedicated to /var/, where Ansible Tower stores its files and working directories (dedicating less space will cause the installation to fail)</p>\n          </aside>\n        </section>\n        <section>\n          <section data-state=\"title alt\">\n          <h1>Demo Time: <br/>Installing Ansible Tower</h1>\n          <aside class=\"notes\">\n            <p>Installing Ansible Tower is simple and quick. Go ahead and open up a terminal and connect to VM with an OS of your choice.</p>\n            <p>Start by downloading the latest bundled tower installer from https://bit.ly/ansibletowerbundle and unpack the tarball once it is done downloading.</p>\n            <p>Now that the package is unzipped, move into the directory and look for the inventory file. Here you will set all the necessary passwords needed for the install to take place. </p>\n            <p>You will need to set:</p>\n            <ul>\n              <li>admin_password</li>\n              <li>pg_password</li>\n              <li>rabbitmq_password</li>\n            </ul>\n            <p>You are setting the admin password because the admin user account will be the only account at the beginning that can access your Ansible Tower instance.</p>\n            <p>You are setting the postgres password so that tower will have access to the database that is created</p>\n            <p>And you are setting rabbitmq password so that rabbitmq will be able to <p>\n          </aside>\n          </section>\n          <section data-state=\"title alt\">\n          <h1>Workshop: <br/>Installing Ansible Tower</h1>\n          <aside class=\"notes\">\n            <p>See</p>\n            <pre class=\"language-yaml\"><code>workshops/tower_install</code></pre>\n            <p>in the Ansible Lightbulb repo.</p>\n            <p>It's solution and addition details can be found in <code>facilitator/solutions/tower_install.md</code>.</p>\n          </aside>\n          </section>\n        </section>\n        <section>\n          <h2>Key Features of Ansible Tower</h2>\n            <ul>\n              <li>Dashboard and User Interface</li>\n              <li>User Base -- Organizations, Teams &amp; Users</li>\n              <li>Credentials</li>\n              <li>Inventories</li>\n              <li>Projects</li>\n              <li>Job Templates &amp; Jobs</li>\n              <li>Role Based Access Control (RBAC)</li>\n            </ul>\n          <aside class=\"notes\">\n            <p>We now have Ansible Tower installed on a VM, but before we can get started with automating in Ansible Tower, we need to learn how some of the key features of it work.</p>\n            <p>Listed are some of the key features that we are going to be discussing in the coming slides. </p>\n          </aside>\n        </section>\n        <section>\n          <h2 style=\"text-align:center\">Dashboard and User Interface</h2>\n          <div style=\"text-align: center;\"><img src=\"images/ansible-tower3-monitor-1x.png\"/></div>\n          <aside class=\"notes\">\n            <p>The Ansible Tower dashboard provides a heads-up NOC-style display and a friendly graphical framework for your IT orchestration needs. Across the top-left side of the Ansible Tower Dashboard, administrators can quickly navigate to their Projects, Inventories, Job Templates, and Jobs.</p>\n            <p>Across the top-right side of this interface, administrators can access the tools they need to configure organizations, users, groups, and permissions as well as view related documentation, access portal mode, and log out.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>User Base</h2><br>\n            <p>A <strong>user</strong> is an account to access Ansible Tower and its services given the permissions granted to it.</p>\n            <p>An <strong>organization</strong> is a logical collection of users, teams, projects, inventories and more. All entities belong to an organization with the exception of users.</p>\n            <p><strong>Teams</strong> provide a means to implement role-based access control schemes and delegate responsibilities across organizations.</p>\n            <aside class=\"notes\">\n              <p>Users are individuals and can be added to teams and or organizations. There are 3 types of users: Normal User, System Auditor and System Administrator.</p>\n              <p>A normal user has read write access limited to the resources they are able to access.</p>\n              <p>A system auditor implicitly inherit the read-only capability for all objects within the Ansible Tower environment.</p>\n              <p>A system administrator (also known as Superuser) has admin, read, and write privileges over the entire Ansible Tower installation. A System Administrator is typically responsible for managing all aspects of Ansible Tower and delegating responsibilities for day-to-day work to various Users.\n              <p>An organization is is a logical collection of Users, Teams, Projects, and Inventories, and is the highest level in the Ansible Tower object hierarchy.</p>\n              <p>A Team is a subdivision of an organization with associated users, projects, credentials, and permissions. Teams provide a means to implement role-based access control schemes and delegate responsibilities across organizations. </p>\n              <p>The user-base plays an important part in implementing role-based access control schemes across your organization.</p>\n            </aside>\n        </section>\n        <section>\n          <h2>Credentials</h2>\n          <p>Credentials are utilized by Ansible Tower for authentication with various external resources:</p>\n          <ul>\n            <li>Connecting to <strong>remote machines</strong> to run jobs</li>\n            <li>Syncing with <strong>inventory</strong> sources</li>\n            <li>Importing project content from <strong>version control systems</strong></li>\n            <li>Connecting to and managing <strong>networking devices</strong>\n          </ul>\n          <p>Centralized management of various credentials allows end users to leverage a  secret without ever exposing that secret to them.</p>\n          <aside class=\"notes\">\n            <p>Credentials are utilized by Ansible Tower for authentication when launching Jobs against machines, synchronizing with inventory sources, and importing project content from a version control system.</p>\n            <p>Ansible Tower credentials are imported and stored encrypted in Ansible Tower, and are not retrievable in plain text on the command line by any user.</p>\n            <p>You can grant users and teams the ability to use these credentials, without actually exposing the credential to the user.</p>\n            <p>If you have a user move to a different team or leave the organization, you don’t have to re-key all of your systems just because that credential was available in Ansible Tower.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Inventory</h2>\n          <p>Inventory is a collection of hosts (nodes) with associated data and groupings that Ansible Tower can connect to and manage.</p>\n          <ul>\n            <li>Hosts (nodes)</li>\n            <li>Groups</li>\n            <li>Inventory-specific data (variables)</li>\n            <li>Static or dynamic sources</li>\n          </ul>\n          <aside class=\"notes\">\n          <p>Inventory is a collection of the hosts (nodes) with associated metadata and groupings that Ansible can connect and manage. An inventory source can be static files or dynamically retrieved from an external system.</p>\n          <p>In Ansible Tower, there are many features that you can take advantage of in regards to inventory. Groups, all be them not new in the Ansible space, are now more easily manipulated and created. Groups can be nested and this allows for you to copy or move the group to a different group.</p>\n          <p>You also have the ability in Ansible Tower to setup a dynamic inventory within Ansible Tower. We will talk more in depth about dynamic inventories here in a few minutes.</p>\n        </aside>\n        </section>\n        <section>\n          <h2>Projects</h2>\n          <p>A Project is a logical collection of Ansible Playbooks, represented in Ansible Tower.</p>\n          <p>You can manage Playbooks and Playbook directories by placing them in a <strong>source code management system</strong> supported by Ansible Tower, including Git, Subversion, and Mercurial.</li>\n          <aside class=\"notes\">\n            <p>As a refresher from the core presentation, a Project is a logical collection of Ansible playbooks. </p>\n            <p>In Ansible Tower, you can manage playbooks and playbook directories by either placing them manually under the Project Base Path on your Ansible Tower server, or by placing your playbooks into a source code management (SCM) system supported by Ansible Tower, including Git, Subversion, and Mercurial.</p>\n            <p>Managing your projects with a SCM is recommended to ensure that only those with assigned access to the repository can change the playbook before execution, and for the extra layer of accountability and change control it provides.</p>\n            <p>Within Ansible Tower, there are a few new things that you can do with your projects.</p>\n            <ul>\n              <li>Update: If configured for a particular project, you can invoke an immediate update to source control.</li>\n              <li>Schedule: If configured for a particular project , you can schedule an update from source control.</li>\n              <li>Permissions: Any project can have permissions added to it so only a specific set of users or teams can access it.</li>\n            </ul>\n          </aside>\n        </section>\n        <section>\n          <h2>Job Templates</h2>\n          <p>A job template is a definition and set of parameters for running an Ansible Playbook. <br><br>Job templates are useful to <strong>execute</strong> the same job many times and encourage the <strong>reuse</strong> of Ansible Playbook content and collaboration between teams.</p>\n          <aside class=\"notes\">\n            <p>While the REST API allows for the execution of jobs directly, Ansible Tower requires that you first create a job template.</p>\n            <p>To create a job template, there are some things that are required. You will need to have:\n              <ul>\n                <li>Name</li>\n                <li>Job Type</li>\n                <li>Inventory</li>\n                <li>Project</li>\n                <li>Playbook</li>\n                <li>Credentials (a machine credential is always required but depending on what you are running against, other credentials might be needed)</li>\n                <li>Verbosity Level (A level 0 is always selected by default but depending on the kind of output that is needed, you can select a level all the way up to 5)</li>\n              </ul>\n            <p>Those are required for a job template but their other options for you to cater each job template to your exact needs.</p>\n            <p>Due to time, we won't go to far into the weeds on some of the advanced configurations that you can do with Job Templates but there are some options that you can select to make life a little easier when running job templates.</p>\n            <p>Options such as Enable privilege escalation, enable provisioning callbacks and enable concurrent jobs.</p>\n             <ul>\n                <li>Enable Privilege Escalation: If enabled, run this playbook as an administrator. This is the equivalent of passing the --become option to the ansible-playbook command.</li>\n                <li>Allow Provisioning Callbacks: Enable a host to call back to Ansible Tower via the Ansible Tower API and invoke the launch of a job from this job template.</li>\n                <li>Allow Provisioning Callbacks: Enable a host to call back to Ansible Tower via the Ansible Tower API and invoke the launch of a job from this job template.</li>\n          </aside>\n        </section>\n        <section>\n          <h2>Jobs</h2>\n          <p>A job is an instance of Ansible Tower launching an Ansible Playbook against an inventory of hosts.</p>\n            <ul>\n              <li>Job results can be easily viewed</li>\n              <li>View the standard out for a more in-depth look</li>\n            </ul>\n          <aside class=\"notes\">\n            <p>The Jobs link displays a list of jobs and their status, shown as completed successfully or failed, or as an active (running) job.</p>\n            <p>Actions you can take against jobs include viewing the details and standard output of a particular job, relaunch jobs, or remove jobs.</p>\n            <p>Clicking on any type of job takes you to the Job Details View for that job, which consists of two sections:</p>\n              <ul>\n                <li>Details: provides information and status about the job</li>\n                <li>Standard Out: displays the job processes and output</li>\n              </ul>\n          </aside>\n        </section>\n        <section>\n          <h2>Role Based Access Control (RBAC)</h2>\n          <p>Role-Based Access Controls (RBAC) are built into Ansible Tower and allow administrators to <strong>delegate access</strong> to server inventories, organizations, and more. These controls allow Ansible Tower to help you <strong>increase security</strong> and <strong>streamline management</strong> of your Ansible automation.</p>\n          <aside class=\"notes\">\n            <p>RBAC is at the core of how Ansible Tower scales organizations while increasing the security of automating.</p>\n            <p>By delegating access to resources within tower, you are removing an element of human error. Users can only view what they are permitted to see and can only run jobs against inventories that they are permitted to act against.</p>\n            <p>(increase security) </p>\n            <p>Streamline management</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Role Based Access Control (RBAC)</h2><br>\n          <img src=\"images/ansible-tower-rbac.svg\">\n          <aside class=\"notes\">\n            <p>Roles can be granted to individual users, teams and as well as to organizations.</p>\n            <p>Roles control access to projects, inventories and job templates in the form of permissions.</p>\n            <p>Tower security centers around role based access for users and teams.</p>\n            <p>Security can be applied granularly to Credentials, Inventories, Projects, Job Templates and API Access based on the user or team role.</p>\n        </section>\n        <section>\n          <section data-state=\"title alt\">\n            <h1>Demo Time:<br/>Ansible Tower Basic Setup<br>&amp; Job Run</h1>\n            <aside class=\"notes\">\n              <p>Installing Ansible Tower is simple and quick. Go ahead and open up a terminal and connect to VM with an OS of your choice..</p>\n            </aside>\n          </section>\n          <section data-state=\"title alt\">\n            <h1>Workshop:<br/>Ansible Tower Basic Setup<br>&amp; Your First Job Run</h1>\n            <aside class=\"notes\">\n              <p>See</p>\n              <pre class=\"language-yaml\"><code>workshops/tower_basic_setup</code></pre>\n              <p>in the Ansible Lightbulb repo.</p>\n            </aside>\n          </section>\n        </section>\n        <section>\n          <section>\n            <h2>Dynamic Inventory in Ansible Tower</h2>\n            <p>Dynamic inventory is a script that queries a service, like a cloud provider API or a management application. This data is formatted in an Ansible-specific JSON data structure and is used in lieu of static inventory files.</p>\n            <ul>\n              <li>Groups are generated based on host metadata</li>\n              <li>Single source of truth saves time, avoids duplication and reduces human error</li>\n              <li>Dynamic and static inventory sources can be used together</li>\n            </ul>\n            <aside class=\"notes\">\n              <p>Ansible can take inventory from a wide variety of sources on-the-fly. It can draw from many different systems such as VMWare, Openstack, Amazon EC2, Digital Ocean, and other provisioning systems like Cobbler. You can also create your own inventory module to interface with any proprietary or in-house CMDB systems.</p>\n              <p>Ideally, the source of data for our dynamic inventory scripts is 100% correct, and is automatically updated as infrastructure changes. This is especially useful in environments where machines are coming on and offline all the time, like a cloud or virtual environment.</p>\n            </aside>\n          </section>\n          <section data-state=\"title alt\">\n            <h1>Demo:<br/>Ansible Tower Dynamic Inventory</h1>\n            <aside class=\"notes speaker\">\n              <p>Setup a cloud credential in Ansible Tower to (presumably) AWS EC2 and then manually trigger an inventory update. Show the inventory in the Ansible Tower UI. Note how it generated groups and job templates can be setup to run inventory updates before executing.</p>\n              <p>Alternatively you can open a terminal window and use Ansible core to demonstrate dynamic inventory at work. Assuming you have your local credentials setup, run the inventory script and pipe it to less. Then run an ad-hoc ping command using <code>-i</code> switch to call your dynamic inventory.</p>\n            </aside>\n          </section>\n        </section>\n        <section>\n          <section>\n            <h2>More with Ansible Tower</h2>\n            <ul>\n              <li>Job Status Updates</li>\n              <li>Activity Stream</li>\n              <li>Integrated Notifications</li>\n              <li>Schedule Jobs</li>\n              <li>Manage and Track Your Inventory</li>\n              <li>Self Service IT (User Surveys)</li>\n              <li>Remote Command Execution</li>\n              <li>External Logging</li>\n              <li>Multi-Playbook Workflows</li>\n            </ul>\n            <aside class=\"notes\">\n            <p>What we've shown you so far are only the basics of what Ansible Tower can do to scale the automation activity in your organization. Here are some other features that have been added to Tower.</p>\n            <p>You can navigate down into slides with more detail on each or return to these at a later date when you the time.</p>\n            </aside>\n          </section>\n\n          <!-- NEW SLIDES BEGIN HERE -->\n          <section>\n            <h2>Job Status Update</h2>\n            <div class=\"columns\">\n              <div class=\"col\">\n                  <p>Heads-up NOC-style <strong>automation dashboard</strong> displays everything going on in your Ansible environment.</p>\n              </div>\n              <div class=\"col\" style=\"flex-grow: 10;\">\n                  <img src=\"images/tower/job-status-update.png\" alt=\"\" />\n              </div>\n            </div>\n            <aside class=\"notes speaker\">\n\n            </aside>\n          </section>\n\n\n          <section>\n            <h2>Activity Stream</h2>\n            <div class=\"columns\">\n              <div class=\"col\" style=\"flex-grow: 10;\">\n                  <img src=\"images/tower/activity-stream.png\" alt=\"\" />\n              </div>\n              <div class=\"col\">\n                  <p>Securely stores every Job that runs, and enables you to view them later, or export details through Ansible Tower’s API.</p>\n              </div>\n            </div>\n            <aside class=\"notes speaker\">\n\n            </aside>\n          </section>\n\n\n          <section>\n            <h2>Integrated Notifications</h2>\n            <div class=\"columns\">\n              <div class=\"col\">\n                  <p>Stay informed of your automation status via integrated notifications. Connect Slack, Hipchat, SMS, email and more. </p>\n              </div>\n              <div class=\"col\" style=\"flex-grow: 10;\">\n                  <img src=\"images/tower/integrated-notifications.png\" alt=\"\" />\n              </div>\n            </div>\n            <aside class=\"notes speaker\">\n\n            </aside>\n          </section>\n\n\n          <section>\n            <h2>Schedule Jobs</h2>\n            <div class=\"columns\">\n              <div class=\"col\" style=\"flex-grow: 10;\">\n                  <img src=\"images/tower/schedule-jobs.png\" alt=\"\" />\n              </div>\n              <div class=\"col\">\n                  <p>Enables you to schedule any Job now, later, or forever.</p>\n              </div>\n            </div>\n            <aside class=\"notes speaker\">\n\n            </aside>\n          </section>\n\n\n          <section>\n            <h2>Manage and Track Your Inventory</h2>\n            <div class=\"columns\">\n              <div class=\"col\">\n                  <p>Ansible Tower’s <strong>inventory syncing</strong> and <strong>provisioning callbacks</strong> allow nodes to request configuration on demand, enabling auto-scaling.</p>\n              </div>\n              <div class=\"col\" style=\"flex-grow: 10;\">\n                  <img src=\"images/tower/manage-track-inventory.png\" alt=\"\" />\n              </div>\n            </div>\n            <aside class=\"notes speaker\">\n\n            </aside>\n          </section>\n\n\n          <section>\n            <h2>Self Service IT</h2>\n            <div class=\"columns\">\n              <div class=\"col\" style=\"flex-grow: 10;\">\n                  <img src=\"images/tower/self-service-it.png\" alt=\"\" />\n              </div>\n              <div class=\"col\">\n                  <p>Ansible Tower lets you launch Playbooks with just a single click. It can prompt you for variables, let you choose from available secure credentials and monitor the resulting deployments.</p>\n              </div>\n            </div>\n            <aside class=\"notes speaker\">\n\n            </aside>\n          </section>\n\n\n          <section>\n            <h2>Remote Command Execution</h2>\n            <div class=\"columns\">\n              <div class=\"col\">\n                  <p>Run simple tasks on any hosts with Ansible Tower's <strong>remote command execution</strong>. Add users or groups, reset passwords, restart a malfunctioning service or patch a critical security issue, quickly.</p>\n              </div>\n              <div class=\"col\" style=\"flex-grow: 10;\">\n                  <img src=\"images/tower/remote-command-execution.png\" alt=\"\" />\n              </div>\n            </div>\n            <aside class=\"notes speaker\">\n\n            </aside>\n          </section>\n\n\n          <section>\n            <h2>External Logging</h2>\n            <div class=\"columns\">\n              <div class=\"col\" style=\"flex-grow: 10;\">\n                  <img src=\"images/tower/external-logging.png\" alt=\"\" />\n              </div>\n              <div class=\"col\">\n                  <p>Connect Ansible Tower to your external logging and analytics provider to perform analysis of automation and event correlation across your entire environment.</p>\n              </div>\n            </div>\n            <aside class=\"notes speaker\">\n\n            </aside>\n          </section>\n\n\n          <section>\n            <h2>Multi-Playbook Workflows</h2>\n            <div class=\"columns\">\n              <div class=\"col\">\n                  <p>Ansible Tower’s multi-Playbook workflows chains any number of Playbooks together to create a single workflow. Different Jobs can be run depending on success or failure of the prior Playbook.</p>\n              </div>\n              <div class=\"col\" style=\"flex-grow: 10;\">\n                  <img src=\"images/tower/multi-playbook-workflows.png\" alt=\"\" />\n              </div>\n            </div>\n            <aside class=\"notes speaker\">\n\n            </aside>\n          </section>\n\n          <!-- NEW SLIDES END HERE -->\n        </section>\n        <section>\n          <h2>Next Steps</h2>\n          <ul>\n            <li><strong>It’s easy to get started</strong><br/>ansible.com/get-started</li>\n            <li><strong>Try Ansible Tower for free:</strong><br/>ansible.com/tower-trial</li>\n            <li><strong>Would you like to learn a lot more?</strong><br/>redhat.com/en/services/training/do409-automation-ansible-ii-ansible-tower</li>\n          </ul>\n        </section>\n      </div>\n    </div>\n\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/lib/js/head.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/js/reveal.js\"></script>\n\n    <script>\n      // More info https://github.com/hakimel/reveal.js#configuration\n      Reveal.initialize({\n        history: true,\n        width: \"85%\",\n        height: \"90%\",\n        transition: \"fade\",\n\n        // More info https://github.com/hakimel/reveal.js#dependencies\n        // Notes plugin must remain local for now.\n        // See https://github.com/ansible/lightbulb/issues/125\n        dependencies: [\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/plugin/markdown/marked.js' },\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/plugin/markdown/markdown.js' },\n          { src: 'plugin/notes/notes.js', async: true },\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/prism.min.js'},\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/components/prism-yaml.min.js'}\n          //{ src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }\n        ]\n      });\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "decks/plugin/notes/notes.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\">\n\n\t\t<title>reveal.js - Slide Notes</title>\n\n\t\t<style>\n\t\t\tbody {\n\t\t\t\tfont-family: Helvetica;\n\t\t\t\tfont-size: 18px;\n\t\t\t}\n\n\t\t\t#current-slide,\n\t\t\t#upcoming-slide,\n\t\t\t#speaker-controls {\n\t\t\t\tpadding: 6px;\n\t\t\t\tbox-sizing: border-box;\n\t\t\t\t-moz-box-sizing: border-box;\n\t\t\t}\n\n\t\t\t#current-slide iframe,\n\t\t\t#upcoming-slide iframe {\n\t\t\t\twidth: 100%;\n\t\t\t\theight: 100%;\n\t\t\t\tborder: 1px solid #ddd;\n\t\t\t}\n\n\t\t\t#current-slide .label,\n\t\t\t#upcoming-slide .label {\n\t\t\t\tposition: absolute;\n\t\t\t\ttop: 10px;\n\t\t\t\tleft: 10px;\n\t\t\t\tz-index: 2;\n\t\t\t}\n\n\t\t\t.overlay-element {\n\t\t\t\theight: 34px;\n\t\t\t\tline-height: 34px;\n\t\t\t\tpadding: 0 10px;\n\t\t\t\ttext-shadow: none;\n\t\t\t\tbackground: rgba( 220, 220, 220, 0.8 );\n\t\t\t\tcolor: #222;\n\t\t\t\tfont-size: 14px;\n\t\t\t}\n\n\t\t\t.overlay-element.interactive:hover {\n\t\t\t\tbackground: rgba( 220, 220, 220, 1 );\n\t\t\t}\n\n\t\t\t#current-slide {\n\t\t\t\tposition: absolute;\n\t\t\t\twidth: 60%;\n\t\t\t\theight: 100%;\n\t\t\t\ttop: 0;\n\t\t\t\tleft: 0;\n\t\t\t\tpadding-right: 0;\n\t\t\t}\n\n\t\t\t#upcoming-slide {\n\t\t\t\tposition: absolute;\n\t\t\t\twidth: 40%;\n\t\t\t\theight: 40%;\n\t\t\t\tright: 0;\n\t\t\t\ttop: 0;\n\t\t\t}\n\n\t\t\t/* Speaker controls */\n\t\t\t#speaker-controls {\n\t\t\t\tposition: absolute;\n\t\t\t\ttop: 40%;\n\t\t\t\tright: 0;\n\t\t\t\twidth: 40%;\n\t\t\t\theight: 60%;\n\t\t\t\toverflow: auto;\n\t\t\t\tfont-size: 18px;\n\t\t\t}\n\n\t\t\t\t.speaker-controls-time.hidden,\n\t\t\t\t.speaker-controls-notes.hidden {\n\t\t\t\t\tdisplay: none;\n\t\t\t\t}\n\n\t\t\t\t.speaker-controls-time .label,\n\t\t\t\t.speaker-controls-notes .label {\n\t\t\t\t\ttext-transform: uppercase;\n\t\t\t\t\tfont-weight: normal;\n\t\t\t\t\tfont-size: 0.66em;\n\t\t\t\t\tcolor: #666;\n\t\t\t\t\tmargin: 0;\n\t\t\t\t}\n\n\t\t\t\t.speaker-controls-time {\n\t\t\t\t\tborder-bottom: 1px solid rgba( 200, 200, 200, 0.5 );\n\t\t\t\t\tmargin-bottom: 10px;\n\t\t\t\t\tpadding: 10px 16px;\n\t\t\t\t\tpadding-bottom: 20px;\n\t\t\t\t\tcursor: pointer;\n\t\t\t\t}\n\n\t\t\t\t.speaker-controls-time .reset-button {\n\t\t\t\t\topacity: 0;\n\t\t\t\t\tfloat: right;\n\t\t\t\t\tcolor: #666;\n\t\t\t\t\ttext-decoration: none;\n\t\t\t\t}\n\t\t\t\t.speaker-controls-time:hover .reset-button {\n\t\t\t\t\topacity: 1;\n\t\t\t\t}\n\n\t\t\t\t.speaker-controls-time .timer,\n\t\t\t\t.speaker-controls-time .clock {\n\t\t\t\t\twidth: 50%;\n\t\t\t\t\tfont-size: 1.9em;\n\t\t\t\t}\n\n\t\t\t\t.speaker-controls-time .timer {\n\t\t\t\t\tfloat: left;\n\t\t\t\t}\n\n\t\t\t\t.speaker-controls-time .clock {\n\t\t\t\t\tfloat: right;\n\t\t\t\t\ttext-align: right;\n\t\t\t\t}\n\n\t\t\t\t.speaker-controls-time span.mute {\n\t\t\t\t\tcolor: #bbb;\n\t\t\t\t}\n\n\t\t\t\t.speaker-controls-notes {\n\t\t\t\t\tpadding: 10px 16px;\n\t\t\t\t}\n\n\t\t\t\t.speaker-controls-notes .value {\n\t\t\t\t\tmargin-top: 5px;\n\t\t\t\t\tline-height: 1.4;\n\t\t\t\t\tfont-size: 1.2em;\n\t\t\t\t}\n\n\t\t\t/* Layout selector */\n\t\t\t#speaker-layout {\n\t\t\t\tposition: absolute;\n\t\t\t\ttop: 10px;\n\t\t\t\tright: 10px;\n\t\t\t\tcolor: #222;\n\t\t\t\tz-index: 10;\n\t\t\t}\n\t\t\t\t#speaker-layout select {\n\t\t\t\t\tposition: absolute;\n\t\t\t\t\twidth: 100%;\n\t\t\t\t\theight: 100%;\n\t\t\t\t\ttop: 0;\n\t\t\t\t\tleft: 0;\n\t\t\t\t\tborder: 0;\n\t\t\t\t\tbox-shadow: 0;\n\t\t\t\t\tcursor: pointer;\n\t\t\t\t\topacity: 0;\n\n\t\t\t\t\tfont-size: 1em;\n\t\t\t\t\tbackground-color: transparent;\n\n\t\t\t\t\t-moz-appearance: none;\n\t\t\t\t\t-webkit-appearance: none;\n\t\t\t\t\t-webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n\t\t\t\t}\n\n\t\t\t\t#speaker-layout select:focus {\n\t\t\t\t\toutline: none;\n\t\t\t\t\tbox-shadow: none;\n\t\t\t\t}\n\n\t\t\t.clear {\n\t\t\t\tclear: both;\n\t\t\t}\n\n\t\t\t/* Speaker layout: Wide */\n\t\t\tbody[data-speaker-layout=\"wide\"] #current-slide,\n\t\t\tbody[data-speaker-layout=\"wide\"] #upcoming-slide {\n\t\t\t\twidth: 50%;\n\t\t\t\theight: 45%;\n\t\t\t\tpadding: 6px;\n\t\t\t}\n\n\t\t\tbody[data-speaker-layout=\"wide\"] #current-slide {\n\t\t\t\ttop: 0;\n\t\t\t\tleft: 0;\n\t\t\t}\n\n\t\t\tbody[data-speaker-layout=\"wide\"] #upcoming-slide {\n\t\t\t\ttop: 0;\n\t\t\t\tleft: 50%;\n\t\t\t}\n\n\t\t\tbody[data-speaker-layout=\"wide\"] #speaker-controls {\n\t\t\t\ttop: 45%;\n\t\t\t\tleft: 0;\n\t\t\t\twidth: 100%;\n\t\t\t\theight: 50%;\n\t\t\t\tfont-size: 1.25em;\n\t\t\t}\n\n\t\t\t/* Speaker layout: Tall */\n\t\t\tbody[data-speaker-layout=\"tall\"] #current-slide,\n\t\t\tbody[data-speaker-layout=\"tall\"] #upcoming-slide {\n\t\t\t\twidth: 45%;\n\t\t\t\theight: 50%;\n\t\t\t\tpadding: 6px;\n\t\t\t}\n\n\t\t\tbody[data-speaker-layout=\"tall\"] #current-slide {\n\t\t\t\ttop: 0;\n\t\t\t\tleft: 0;\n\t\t\t}\n\n\t\t\tbody[data-speaker-layout=\"tall\"] #upcoming-slide {\n\t\t\t\ttop: 50%;\n\t\t\t\tleft: 0;\n\t\t\t}\n\n\t\t\tbody[data-speaker-layout=\"tall\"] #speaker-controls {\n\t\t\t\tpadding-top: 40px;\n\t\t\t\ttop: 0;\n\t\t\t\tleft: 45%;\n\t\t\t\twidth: 55%;\n\t\t\t\theight: 100%;\n\t\t\t\tfont-size: 1.25em;\n\t\t\t}\n\n\t\t\t/* Speaker layout: Notes only */\n\t\t\tbody[data-speaker-layout=\"notes-only\"] #current-slide,\n\t\t\tbody[data-speaker-layout=\"notes-only\"] #upcoming-slide {\n\t\t\t\tdisplay: none;\n\t\t\t}\n\n\t\t\tbody[data-speaker-layout=\"notes-only\"] #speaker-controls {\n\t\t\t\tpadding-top: 40px;\n\t\t\t\ttop: 0;\n\t\t\t\tleft: 0;\n\t\t\t\twidth: 100%;\n\t\t\t\theight: 100%;\n\t\t\t\tfont-size: 1.25em;\n\t\t\t}\n\n\t\t\t@media screen and (max-width: 1080px) {\n\t\t\t\tbody[data-speaker-layout=\"default\"] #speaker-controls {\n\t\t\t\t\tfont-size: 16px;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@media screen and (max-width: 900px) {\n\t\t\t\tbody[data-speaker-layout=\"default\"] #speaker-controls {\n\t\t\t\t\tfont-size: 14px;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@media screen and (max-width: 800px) {\n\t\t\t\tbody[data-speaker-layout=\"default\"] #speaker-controls {\n\t\t\t\t\tfont-size: 12px;\n\t\t\t\t}\n\t\t\t}\n\n\t\t</style>\n\t</head>\n\n\t<body>\n\n\t\t<div id=\"current-slide\"></div>\n\t\t<div id=\"upcoming-slide\"><span class=\"overlay-element label\">Upcoming</span></div>\n\t\t<div id=\"speaker-controls\">\n\t\t\t<div class=\"speaker-controls-time\">\n\t\t\t\t<h4 class=\"label\">Time <span class=\"reset-button\">Click to Reset</span></h4>\n\t\t\t\t<div class=\"clock\">\n\t\t\t\t\t<span class=\"clock-value\">0:00 AM</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"timer\">\n\t\t\t\t\t<span class=\"hours-value\">00</span><span class=\"minutes-value\">:00</span><span class=\"seconds-value\">:00</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"clear\"></div>\n\t\t\t</div>\n\n\t\t\t<div class=\"speaker-controls-notes hidden\">\n\t\t\t\t<h4 class=\"label\">Notes</h4>\n\t\t\t\t<div class=\"value\"></div>\n\t\t\t</div>\n\t\t</div>\n\t\t<div id=\"speaker-layout\" class=\"overlay-element interactive\">\n\t\t\t<span class=\"speaker-layout-label\"></span>\n\t\t\t<select class=\"speaker-layout-dropdown\"></select>\n\t\t</div>\n\n\t\t<script src=\"../../plugin/markdown/marked.js\"></script>\n\t\t<script>\n\n\t\t\t(function() {\n\n\t\t\t\tvar notes,\n\t\t\t\t\tnotesValue,\n\t\t\t\t\tcurrentState,\n\t\t\t\t\tcurrentSlide,\n\t\t\t\t\tupcomingSlide,\n\t\t\t\t\tlayoutLabel,\n\t\t\t\t\tlayoutDropdown,\n\t\t\t\t\tconnected = false;\n\n\t\t\t\tvar SPEAKER_LAYOUTS = {\n\t\t\t\t\t'default': 'Default',\n\t\t\t\t\t'wide': 'Wide',\n\t\t\t\t\t'tall': 'Tall',\n\t\t\t\t\t'notes-only': 'Notes only'\n\t\t\t\t};\n\n\t\t\t\tsetupLayout();\n\n\t\t\t\twindow.addEventListener( 'message', function( event ) {\n\n\t\t\t\t\tvar data = JSON.parse( event.data );\n\n\t\t\t\t\t// The overview mode is only useful to the reveal.js instance\n\t\t\t\t\t// where navigation occurs so we don't sync it\n\t\t\t\t\tif( data.state ) delete data.state.overview;\n\n\t\t\t\t\t// Messages sent by the notes plugin inside of the main window\n\t\t\t\t\tif( data && data.namespace === 'reveal-notes' ) {\n\t\t\t\t\t\tif( data.type === 'connect' ) {\n\t\t\t\t\t\t\thandleConnectMessage( data );\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if( data.type === 'state' ) {\n\t\t\t\t\t\t\thandleStateMessage( data );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Messages sent by the reveal.js inside of the current slide preview\n\t\t\t\t\telse if( data && data.namespace === 'reveal' ) {\n\t\t\t\t\t\tif( /ready/.test( data.eventName ) ) {\n\t\t\t\t\t\t\t// Send a message back to notify that the handshake is complete\n\t\t\t\t\t\t\twindow.opener.postMessage( JSON.stringify({ namespace: 'reveal-notes', type: 'connected'} ), '*' );\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if( /slidechanged|fragmentshown|fragmenthidden|paused|resumed/.test( data.eventName ) && currentState !== JSON.stringify( data.state ) ) {\n\n\t\t\t\t\t\t\twindow.opener.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ]} ), '*' );\n\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t} );\n\n\t\t\t\t/**\n\t\t\t\t * Called when the main window is trying to establish a\n\t\t\t\t * connection.\n\t\t\t\t */\n\t\t\t\tfunction handleConnectMessage( data ) {\n\n\t\t\t\t\tif( connected === false ) {\n\t\t\t\t\t\tconnected = true;\n\n\t\t\t\t\t\tsetupIframes( data );\n\t\t\t\t\t\tsetupKeyboard();\n\t\t\t\t\t\tsetupNotes();\n\t\t\t\t\t\tsetupTimer();\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t/**\n\t\t\t\t * Called when the main window sends an updated state.\n\t\t\t\t */\n\t\t\t\tfunction handleStateMessage( data ) {\n\n\t\t\t\t\t// Store the most recently set state to avoid circular loops\n\t\t\t\t\t// applying the same state\n\t\t\t\t\tcurrentState = JSON.stringify( data.state );\n\n\t\t\t\t\t// No need for updating the notes in case of fragment changes\n\t\t\t\t\tif ( data.notes ) {\n\t\t\t\t\t\tnotes.classList.remove( 'hidden' );\n\t\t\t\t\t\tnotesValue.style.whiteSpace = data.whitespace;\n\t\t\t\t\t\tif( data.markdown ) {\n\t\t\t\t\t\t\tnotesValue.innerHTML = marked( data.notes );\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tnotesValue.innerHTML = data.notes;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tnotes.classList.add( 'hidden' );\n\t\t\t\t\t}\n\n\t\t\t\t\t// Update the note slides\n\t\t\t\t\tcurrentSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );\n\t\t\t\t\tupcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );\n\t\t\t\t\tupcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'next' }), '*' );\n\n\t\t\t\t}\n\n\t\t\t\t// Limit to max one state update per X ms\n\t\t\t\thandleStateMessage = debounce( handleStateMessage, 200 );\n\n\t\t\t\t/**\n\t\t\t\t * Forward keyboard events to the current slide window.\n\t\t\t\t * This enables keyboard events to work even if focus\n\t\t\t\t * isn't set on the current slide iframe.\n\t\t\t\t */\n\t\t\t\tfunction setupKeyboard() {\n\n\t\t\t\t\tdocument.addEventListener( 'keydown', function( event ) {\n\t\t\t\t\t\tcurrentSlide.contentWindow.postMessage( JSON.stringify({ method: 'triggerKey', args: [ event.keyCode ] }), '*' );\n\t\t\t\t\t} );\n\n\t\t\t\t}\n\n\t\t\t\t/**\n\t\t\t\t * Creates the preview iframes.\n\t\t\t\t */\n\t\t\t\tfunction setupIframes( data ) {\n\n\t\t\t\t\tvar params = [\n\t\t\t\t\t\t'receiver',\n\t\t\t\t\t\t'progress=false',\n\t\t\t\t\t\t'history=false',\n\t\t\t\t\t\t'transition=none',\n\t\t\t\t\t\t'autoSlide=0',\n\t\t\t\t\t\t'backgroundTransition=none'\n\t\t\t\t\t].join( '&' );\n\n\t\t\t\t\tvar urlSeparator = /\\?/.test(data.url) ? '&' : '?';\n\t\t\t\t\tvar hash = '#/' + data.state.indexh + '/' + data.state.indexv;\n\t\t\t\t\tvar currentURL = data.url + urlSeparator + params + '&postMessageEvents=true' + hash;\n\t\t\t\t\tvar upcomingURL = data.url + urlSeparator + params + '&controls=false' + hash;\n\n\t\t\t\t\tcurrentSlide = document.createElement( 'iframe' );\n\t\t\t\t\tcurrentSlide.setAttribute( 'width', 1280 );\n\t\t\t\t\tcurrentSlide.setAttribute( 'height', 1024 );\n\t\t\t\t\tcurrentSlide.setAttribute( 'src', currentURL );\n\t\t\t\t\tdocument.querySelector( '#current-slide' ).appendChild( currentSlide );\n\n\t\t\t\t\tupcomingSlide = document.createElement( 'iframe' );\n\t\t\t\t\tupcomingSlide.setAttribute( 'width', 640 );\n\t\t\t\t\tupcomingSlide.setAttribute( 'height', 512 );\n\t\t\t\t\tupcomingSlide.setAttribute( 'src', upcomingURL );\n\t\t\t\t\tdocument.querySelector( '#upcoming-slide' ).appendChild( upcomingSlide );\n\n\t\t\t\t}\n\n\t\t\t\t/**\n\t\t\t\t * Setup the notes UI.\n\t\t\t\t */\n\t\t\t\tfunction setupNotes() {\n\n\t\t\t\t\tnotes = document.querySelector( '.speaker-controls-notes' );\n\t\t\t\t\tnotesValue = document.querySelector( '.speaker-controls-notes .value' );\n\n\t\t\t\t}\n\n\t\t\t\t/**\n\t\t\t\t * Create the timer and clock and start updating them\n\t\t\t\t * at an interval.\n\t\t\t\t */\n\t\t\t\tfunction setupTimer() {\n\n\t\t\t\t\tvar start = new Date(),\n\t\t\t\t\t\ttimeEl = document.querySelector( '.speaker-controls-time' ),\n\t\t\t\t\t\tclockEl = timeEl.querySelector( '.clock-value' ),\n\t\t\t\t\t\thoursEl = timeEl.querySelector( '.hours-value' ),\n\t\t\t\t\t\tminutesEl = timeEl.querySelector( '.minutes-value' ),\n\t\t\t\t\t\tsecondsEl = timeEl.querySelector( '.seconds-value' );\n\n\t\t\t\t\tfunction _updateTimer() {\n\n\t\t\t\t\t\tvar diff, hours, minutes, seconds,\n\t\t\t\t\t\t\tnow = new Date();\n\n\t\t\t\t\t\tdiff = now.getTime() - start.getTime();\n\t\t\t\t\t\thours = Math.floor( diff / ( 1000 * 60 * 60 ) );\n\t\t\t\t\t\tminutes = Math.floor( ( diff / ( 1000 * 60 ) ) % 60 );\n\t\t\t\t\t\tseconds = Math.floor( ( diff / 1000 ) % 60 );\n\n\t\t\t\t\t\tclockEl.innerHTML = now.toLocaleTimeString( 'en-US', { hour12: true, hour: '2-digit', minute:'2-digit' } );\n\t\t\t\t\t\thoursEl.innerHTML = zeroPadInteger( hours );\n\t\t\t\t\t\thoursEl.className = hours > 0 ? '' : 'mute';\n\t\t\t\t\t\tminutesEl.innerHTML = ':' + zeroPadInteger( minutes );\n\t\t\t\t\t\tminutesEl.className = minutes > 0 ? '' : 'mute';\n\t\t\t\t\t\tsecondsEl.innerHTML = ':' + zeroPadInteger( seconds );\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// Update once directly\n\t\t\t\t\t_updateTimer();\n\n\t\t\t\t\t// Then update every second\n\t\t\t\t\tsetInterval( _updateTimer, 1000 );\n\n\t\t\t\t\ttimeEl.addEventListener( 'click', function() {\n\t\t\t\t\t\tstart = new Date();\n\t\t\t\t\t\t_updateTimer();\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t} );\n\n\t\t\t\t}\n\n\t\t\t\t/**\n\t\t\t\t * Sets up the speaker view layout and layout selector.\n\t\t\t\t */\n\t\t\t\tfunction setupLayout() {\n\n\t\t\t\t\tlayoutDropdown = document.querySelector( '.speaker-layout-dropdown' );\n\t\t\t\t\tlayoutLabel = document.querySelector( '.speaker-layout-label' );\n\n\t\t\t\t\t// Render the list of available layouts\n\t\t\t\t\tfor( var id in SPEAKER_LAYOUTS ) {\n\t\t\t\t\t\tvar option = document.createElement( 'option' );\n\t\t\t\t\t\toption.setAttribute( 'value', id );\n\t\t\t\t\t\toption.textContent = SPEAKER_LAYOUTS[ id ];\n\t\t\t\t\t\tlayoutDropdown.appendChild( option );\n\t\t\t\t\t}\n\n\t\t\t\t\t// Monitor the dropdown for changes\n\t\t\t\t\tlayoutDropdown.addEventListener( 'change', function( event ) {\n\n\t\t\t\t\t\tsetLayout( layoutDropdown.value );\n\n\t\t\t\t\t}, false );\n\n\t\t\t\t\t// Restore any currently persisted layout\n\t\t\t\t\tsetLayout( getLayout() );\n\n\t\t\t\t}\n\n\t\t\t\t/**\n\t\t\t\t * Sets a new speaker view layout. The layout is persisted\n\t\t\t\t * in local storage.\n\t\t\t\t */\n\t\t\t\tfunction setLayout( value ) {\n\n\t\t\t\t\tvar title = SPEAKER_LAYOUTS[ value ];\n\n\t\t\t\t\tlayoutLabel.innerHTML = 'Layout' + ( title ? ( ': ' + title ) : '' );\n\t\t\t\t\tlayoutDropdown.value = value;\n\n\t\t\t\t\tdocument.body.setAttribute( 'data-speaker-layout', value );\n\n\t\t\t\t\t// Persist locally\n\t\t\t\t\tif( window.localStorage ) {\n\t\t\t\t\t\twindow.localStorage.setItem( 'reveal-speaker-layout', value );\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t/**\n\t\t\t\t * Returns the ID of the most recently set speaker layout\n\t\t\t\t * or our default layout if none has been set.\n\t\t\t\t */\n\t\t\t\tfunction getLayout() {\n\n\t\t\t\t\tif( window.localStorage ) {\n\t\t\t\t\t\tvar layout = window.localStorage.getItem( 'reveal-speaker-layout' );\n\t\t\t\t\t\tif( layout ) {\n\t\t\t\t\t\t\treturn layout;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Default to the first record in the layouts hash\n\t\t\t\t\tfor( var id in SPEAKER_LAYOUTS ) {\n\t\t\t\t\t\treturn id;\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tfunction zeroPadInteger( num ) {\n\n\t\t\t\t\tvar str = '00' + parseInt( num );\n\t\t\t\t\treturn str.substring( str.length - 2 );\n\n\t\t\t\t}\n\n\t\t\t\t/**\n\t\t\t\t * Limits the frequency at which a function can be called.\n\t\t\t\t */\n\t\t\t\tfunction debounce( fn, ms ) {\n\n\t\t\t\t\tvar lastTime = 0,\n\t\t\t\t\t\ttimeout;\n\n\t\t\t\t\treturn function() {\n\n\t\t\t\t\t\tvar args = arguments;\n\t\t\t\t\t\tvar context = this;\n\n\t\t\t\t\t\tclearTimeout( timeout );\n\n\t\t\t\t\t\tvar timeSinceLastCall = Date.now() - lastTime;\n\t\t\t\t\t\tif( timeSinceLastCall > ms ) {\n\t\t\t\t\t\t\tfn.apply( context, args );\n\t\t\t\t\t\t\tlastTime = Date.now();\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\ttimeout = setTimeout( function() {\n\t\t\t\t\t\t\t\tfn.apply( context, args );\n\t\t\t\t\t\t\t\tlastTime = Date.now();\n\t\t\t\t\t\t\t}, ms - timeSinceLastCall );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t})();\n\n\t\t</script>\n\t</body>\n</html>\n"
  },
  {
    "path": "decks/plugin/notes/notes.js",
    "content": "/**\n * Handles opening of and synchronization with the reveal.js\n * notes window.\n *\n * Handshake process:\n * 1. This window posts 'connect' to notes window\n *    - Includes URL of presentation to show\n * 2. Notes window responds with 'connected' when it is available\n * 3. This window proceeds to send the current presentation state\n *    to the notes window\n */\nvar RevealNotes = (function() {\n\n\tfunction openNotes( notesFilePath ) {\n\n\t\tif( !notesFilePath ) {\n\t\t\tvar jsFileLocation = document.querySelector('script[src$=\"notes.js\"]').src;  // this js file path\n\t\t\tjsFileLocation = jsFileLocation.replace(/notes\\.js(\\?.*)?$/, '');   // the js folder path\n\t\t\tnotesFilePath = jsFileLocation + 'notes.html';\n\t\t}\n\n\t\tvar notesPopup = window.open( notesFilePath, 'reveal.js - Notes', 'width=1100,height=700' );\n\n\t\t/**\n\t\t * Connect to the notes window through a postmessage handshake.\n\t\t * Using postmessage enables us to work in situations where the\n\t\t * origins differ, such as a presentation being opened from the\n\t\t * file system.\n\t\t */\n\t\tfunction connect() {\n\t\t\t// Keep trying to connect until we get a 'connected' message back\n\t\t\tvar connectInterval = setInterval( function() {\n\t\t\t\tnotesPopup.postMessage( JSON.stringify( {\n\t\t\t\t\tnamespace: 'reveal-notes',\n\t\t\t\t\ttype: 'connect',\n\t\t\t\t\turl: window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search,\n\t\t\t\t\tstate: Reveal.getState()\n\t\t\t\t} ), '*' );\n\t\t\t}, 500 );\n\n\t\t\twindow.addEventListener( 'message', function( event ) {\n\t\t\t\tvar data = JSON.parse( event.data );\n\t\t\t\tif( data && data.namespace === 'reveal-notes' && data.type === 'connected' ) {\n\t\t\t\t\tclearInterval( connectInterval );\n\t\t\t\t\tonConnected();\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\n\t\t/**\n\t\t * Posts the current slide data to the notes window\n\t\t */\n\t\tfunction post(event) {\n\n\t\t\tvar slideElement = Reveal.getCurrentSlide(),\n\t\t\t\tnotesElement = slideElement.querySelector( 'aside.notes' );\n\n\t\t\tvar messageData = {\n\t\t\t\tnamespace: 'reveal-notes',\n\t\t\t\ttype: 'state',\n\t\t\t\tnotes: '',\n\t\t\t\tmarkdown: false,\n\t\t\t\twhitespace: 'normal',\n\t\t\t\tstate: Reveal.getState()\n\t\t\t};\n\n\t\t\t// Look for notes defined in a fragment, if it is a fragmentshown event\n\t\t\tif (event && event.hasOwnProperty('fragment')) {\n\t\t\t\tvar innerNotes = event.fragment.querySelector( 'aside.notes' );\n\n\t\t\t\tif ( innerNotes) {\n\t\t\t\t\tnotesElement = innerNotes;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Look for notes defined in a slide attribute\n\t\t\tif( slideElement.hasAttribute( 'data-notes' ) ) {\n\t\t\t\tmessageData.notes = slideElement.getAttribute( 'data-notes' );\n\t\t\t\tmessageData.whitespace = 'pre-wrap';\n\t\t\t}\n\n\t\t\t// Look for notes defined in an aside element\n\t\t\tif( notesElement ) {\n\t\t\t\tmessageData.notes = notesElement.innerHTML;\n\t\t\t\tmessageData.markdown = typeof notesElement.getAttribute( 'data-markdown' ) === 'string';\n\t\t\t}\n\n\t\t\tnotesPopup.postMessage( JSON.stringify( messageData ), '*' );\n\n\t\t}\n\n\t\t/**\n\t\t * Called once we have established a connection to the notes\n\t\t * window.\n\t\t */\n\t\tfunction onConnected() {\n\n\t\t\t// Monitor events that trigger a change in state\n\t\t\tReveal.addEventListener( 'slidechanged', post );\n\t\t\tReveal.addEventListener( 'fragmentshown', post );\n\t\t\tReveal.addEventListener( 'fragmenthidden', post );\n\t\t\tReveal.addEventListener( 'overviewhidden', post );\n\t\t\tReveal.addEventListener( 'overviewshown', post );\n\t\t\tReveal.addEventListener( 'paused', post );\n\t\t\tReveal.addEventListener( 'resumed', post );\n\n\t\t\t// Post the initial state\n\t\t\tpost();\n\n\t\t}\n\n\t\tconnect();\n\n\t}\n\n\tif( !/receiver/i.test( window.location.search ) ) {\n\n\t\t// If the there's a 'notes' query set, open directly\n\t\tif( window.location.search.match( /(\\?|\\&)notes/gi ) !== null ) {\n\t\t\topenNotes();\n\t\t}\n\n\t\t// Open the notes when the 's' key is hit\n\t\tdocument.addEventListener( 'keydown', function( event ) {\n\t\t\t// Disregard the event if the target is editable or a\n\t\t\t// modifier is present\n\t\t\tif ( document.querySelector( ':focus' ) !== null || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey ) return;\n\n\t\t\t// Disregard the event if keyboard is disabled\n\t\t\tif ( Reveal.getConfig().keyboard === false ) return;\n\n\t\t\tif( event.keyCode === 83 ) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\topenNotes();\n\t\t\t}\n\t\t}, false );\n\n\t\t// Show our keyboard shortcut in the reveal.js help overlay\n\t\tif( window.Reveal ) Reveal.registerKeyboardShortcut( 'S', 'Speaker notes view' );\n\n\t}\n\n\treturn { open: openNotes };\n\n})();\n"
  },
  {
    "path": "decks/your_first_pb.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">\n\n    <title>Ansible Essentials Workshop</title>\n\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/css/reveal.css\">\n\n    <!-- Printing and PDF exports -->\n    <script>\n      var link = document.createElement( 'link' );\n      link.rel = 'stylesheet';\n      link.type = 'text/css';\n      link.href = window.location.search.match( /print-pdf/gi ) ? 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/css/print/pdf.css' : 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/css/print/paper.css';\n      document.getElementsByTagName( 'head' )[0].appendChild( link );\n    </script>\n\n\n    <link rel=\"stylesheet\" href=\"css/theme/ansible.css\">\n\n    <!-- Theme used for syntax highlighting of code -->\n    <!--link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/lib/css/zenburn.css\"-->\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/themes/prism.min.css\">\n\n\n  </head>\n  <body>\n  <div class=\"ans-mark\">\n    <svg version=\"1.1\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" viewBox=\"-449 450 125 125\" style=\"enable-background:new -449 450 125 125;\" xml:space=\"preserve\">\n      <g id=\"XMLID_3_\">\n        <circle id=\"XMLID_7_\" class=\"circle\" cx=\"-386.5\" cy=\"512.5\" r=\"62\"/>\n        <path id=\"XMLID_4_\" class=\"a-mark\" d=\"M-356.9,537.1l-24.7-59.4c-0.7-1.7-2.1-2.6-3.9-2.6c-1.7,0-3.2,0.9-4,2.6l-27.1,65.2h9.2 l10.7-26.9l32,25.9c1.3,1,2.2,1.5,3.4,1.5c2.4,0,4.6-1.8,4.6-4.5C-356.5,538.5-356.6,537.8-356.9,537.1z M-385.4,488.4l16.1,39.6 l-24.2-19L-385.4,488.4z\"/>\n      </g>\n    </svg>\n  </div>\n    <div class=\"reveal\">\n      <div class=\"slides\">\n        <section data-state=\"cover\">\n          <p class=\"ans-logo\"><img src=\"images/ansible-wordmark-white.svg\" width=\"260\" alt=\"\" /></p>\n          <h1>Writing your first playbook</h1>\n          <!--p>NAME HERE, TITLE HERE</p>\n          <p>COMPANY HERE</p-->\n        </section>\n        <section>\n          <h2>Agenda</h2>\n          <ul>\n            <li>What is a playbook?</li>\n            <li>What are we Automating</li>\n            <li>What makes up a playbook</li>\n            <li>Roles and Sharing Automation</li>\n          </ul>\n          <aside class=\"notes\">\n            <p>Speaker notes here </p>\n          </aside>\n        </section>\n        <section>\n            <h2>What are you automating?</h2>\n            <ul>\n              <li>What OS? What kinds of security measures are on the managed node?</li>\n              <li>Ansible will do what you tell it to.</li>\n            <ul>\n            <aside class=\"notes\">\n              <p>What you are automating is crucial because it determines how you will write your playbooks.</p>\n              <p>Are there multiple OSs that you wish to automate at once? What does the security on those machines look like?</p>\n              <p>The answers to these questions all affect how you will structure your playbook.</p>\n              <p>To follow that up, ansible assumes you know what you are doing. It will do exactly what you tell it do and exactly that order.</p>\n              <p>So if you need to do something, lets say change a line in a conf file, remember to call a handler to restart that service if it needs to be. Ansible won't know to do that automatically.</p>\n            </aside>\n        </section>\n        <section>\n            <h2>What is a playbook?</h2>\n              <p>A set of instructions to do something on a remote host(s)</p>\n            <aside class=\"notes\">\n              <p>Imagine a set of instructions to do something, like building a piece of furniture. When those instructions are done in the exact order, the piece of furniture looks like the picture on the outside of the box.</p>\n            </aside>\n        </section>\n        <section>\n          <h2>What makes up a playbook?</h2>\n          <ul>\n            <li>Header</li>\n            <li>Tasks</li>\n            <li>Handlers</li>\n          <aside class=\"notes\">\n            <p>There are 3 parts to a playbook. The header, tasks and then if you need them, handlers at the end.</p>\n            <p>The header contains all of the necessary information in regards to what ansible is going to target and run against. The host information is contained here, the applicable variables etc.</p>\n            <p>The tasks are the meat, the actual bits that describe and do the automating. The lineinfile actions, the yum bits. The next couple of slides, we will dig deeper into this.</p>\n            <p>And finally, handlers, handling any restarts or notifies of things that might need some clean up after something is done.</p>\n          </aside>\n        </section>\n        <section>\n          <pre class=\"language-yaml\"><code data-noescape>\n---\n- hosts: webservers\n  vars:\n    http_port: 80\n    max_clients: 200\n  remote_user: root\n  tasks:\n  - name: ensure apache is at the latest version\n    yum:\n      name: httpd\n      state: latest\n          </code></pre>\n          <aside class=\"notes\">\n            <p>Here is a basic example of a playbook. Do note that the handlers are cut off here purely for the amount of space.</p>\n            <p>At the top here you can see the header, stating what hosts the playbook is targeting and what vars to use. Note that for this example is using root but that you don't need to use root, just a user that has the correct privileges to make the changes.</p>\n            <p>Then you get to the tasks, the good stuff. The meat of the playbook, the stuff that is making the changes.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Forming a task</h2>\n            <ul>\n            <li>Starts with a name</li>\n            <li>The module and specific arguments</li>\n            <li>Your variables can be used in arguments</li>\n          </ul>\n          <aside class=\"notes\">\n            <p>It all starts with a name. Naming a task is something very important because it is displayed during the execution of ansible. Name it something that is close to what the task is doing so that in case it fails, you know immediately how you can start debugging it.</p>\n            <p>Next, you will need to pick a module. Your selection will depend on what you task you are trying to accomplish. You can find a list of all available modules at docs.ansible.com </p>\n            <p>All modules include documentation on how they can be used and what are the required arguments for that module. There are also examples included on that same documentation page showing the module in use.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Conditionals</h2>\n            <p>Yes, you can use them in playbooks</p>\n            <p>Some of the most popular are:\n              <ul>\n                <li>When Statement</li>\n                <li>Register</li>\n            </p>\n          <aside class=\"notes\">\n            <p>Diving deeper into the meat of tasks, you can use conditionals within tasks. Sometimes tasks depend on other things being present, or maybe you just want some tasks to run on certain hosts when a particular criteria is met.</p>\n            <p>In come conditionals. We are only going to talk about the basic uses of them but if you want to see more in depth examples, there are a bunch of examples in the playbook section of our documentation.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>When Statement</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\ntasks:\n  - name: \"shut down Debian flavored systems\"\n    command: /sbin/shutdown -t now\n    when: ansible_facts['os_family'] == \"Debian\"\n          </code></pre>\n          <aside class=\"notes\">\n            <p>This could be something as simple as not installing a certain package if the operating system is a particular version.</p>\n            <p>Above is an example of a simple when statement showing a task tha will only shutdown when the ansible_facts shows an OS family of Debian.</p>\n          </aside>\n        </section>\n        <section>\n          <h2>Using Loops</h2>\n            <ul>\n              <li>Loops can be used to save some typing.</li>\n              <li>Short handed task writing to automate your keystrokes</li>\n            <ul>\n          <aside class=\"notes\">\n            <p>In Ansible you can use loops to loop through redundant tasks to automate away some extra keystrokes.</p>\n        </section>\n        <section>\n          <h2>Loop Example</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\n- name: add several users\n  user:\n    name: \"{{ item }}\"\n    state: present\n    groups: \"wheel\"\n  loop:\n     - testuser1\n     - testuser2\n          </code></pre>\n        <aside class=\"notes\">\n          <p>Similar to the use of loops in a general programitic way, loops in playbooks are similar but...as the ansible way is...a little more simpler on the eye.</p>\n          <p>This example shows a task looping through creating testuser 1 and 2, then adding them to the wheel group.</p>\n        </aside>\n        </section>\n        <section>\n          <h2>Handlers</h2>\n            <ul>\n              <li>The quicker restarter, the cleanup crew.</li>\n              <li> Regardless of how many tasks notify a handler, it will run only ONCE.</li>\n            </ul>\n        <aside class=\"notes\">\n          <p>Handlers are lists of tasks, not really any different from regular tasks, that are referenced by a globally unique name, and are notified by notifiers. If nothing notifies a handler, it will not run. Regardless of how many tasks notify a handler, it will run only once, after all of the tasks complete in a particular play.</p>\n          <p>Imagine clean up tasks, if you need them. Not every task needs them but sometimes you might have to restart something.</p>\n        </aside>\n        </section>\n        <section>\n          <h2>What a handler looks like</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\n- name: restart apache\n      service:\n        name: httpd\n        state: restarted\n        </code></pre>\n        <aside class=\"notes\">\n          <p>This example shows what a handler looks like. Not so different from a regular task right?</p>\n          <p>Simple and straight to the point. Notifying a handler is a little different but super easy to plug straight into a task if you need.</p>\n        </aside>\n        </section>\n        <section>\n          <h2>Notifying a handler</h2>\n          <pre class=\"language-yaml\"><code data-noescape>\n- name: write the apache config file\n    template:\n      src: /srv/httpd.j2\n      dest: /etc/httpd.conf\n    notify:\n    - restart apache\n        </code></pre>\n        <aside class=\"notes\">\n          <p>Here we can see how you notify the handler from the above example. That one being called \"restart apache\"</p>\n          <p>Handlers work great and serve a higher purpose but one last time, remember that they only run ONCE!</p>\n        </aside>\n        </section>\n        <section>\n          <h2>Roles and Sharing Automation</h2>\n            <ul>\n              <li>What are roles?</li>\n              <li>Playbook vs Role</li>\n          </ul>\n        <aside class=\"notes\">\n          <p>Roles are ways of automatically loading certain vars_files, tasks, and handlers based on a known file structure. Grouping content by roles also allows easy sharing of roles with other users.</p>\n          <p>Imagine you need to provision a server for a web app and coworker needs a db machine. At the end of the day, both machines are doing different jobs but if you are a part of a large organization, you might have a standard image.</p>\n          <p>Using roles, you can write a provisioning role that provisions the machine exactly as your organization outlines it and then share that role with a co-worker and it will work exactly the same way.</p>\n          <p>Playbooks and roles are different, playbooks is the larger picture, provisioning the machine AND installing the app while roles are small bite size chunks of automation. ONLY provisioning the machine or ONLY installing nginx.</p>\n        </aside>\n        </section>\n        <section>\n          <h2>Ansible Galaxy</h2>\n            <ul>\n              <li>What is it?</li>\n              <li>How can I use it?</li>\n              <li>https://galaxy.ansible.com/</li>\n            <ul>\n        <aside class=\"notes\">\n          <p>Ansible Galaxy is a hub for finding, reusing and sharing Ansible content. You can find some of the most popular roles there, like geerlingguy's nginx role.</p>\n          <p>Ansible Galaxy is also a command line tool that comes with ansible. You can use it to do a bunch of things like create a role and the directory structure needed for a functioning role. You can do this by running the ansible-galaxy init name_role_here.</p>\n        </aside>\n      </section>\n        <section>\n          <h2>Thanks all!</h2>\n        </section>\n      </div>\n    </div>\n\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/lib/js/head.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/js/reveal.js\"></script>\n\n    <script>\n      // More info https://github.com/hakimel/reveal.js#configuration\n      Reveal.initialize({\n        history: true,\n        width: \"85%\",\n        height: \"90%\",\n        transition: \"fade\",\n\n        // More info https://github.com/hakimel/reveal.js#dependencies\n        // Notes plugin must remain local for now.\n        // See https://github.com/ansible/lightbulb/issues/125\n        dependencies: [\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/plugin/markdown/marked.js' },\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/plugin/markdown/markdown.js' },\n          { src: 'plugin/notes/notes.js', async: true },\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/prism.min.js'},\n          { src: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.6.0/components/prism-yaml.min.js'}\n          //{ src: 'https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.4.1/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }\n        ]\n      });\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/README.md",
    "content": "# Ansible Lightbulb Examples\n\nThis content is a collection of complete Ansible solutions that demonstrate the most essential features and common use patterns.\n\nThese examples are an excellent educational reference for communicating how Ansible works in a clear, focused and consistent manner using recommended best practices.\n\nIt is a great source for canned demos or something you can walk-thru to illustrate automating with Ansible to a group. Some of the examples also serve as the solutions to the workshop assignments.\n\n## Documentation of examples\n\nThe documentation of the examples must follow the example structure - or a subset of it - shown below:\n\n```\n# cloud-aws\n\nA basic description explaining the reasoning behind the example as well as what best practices and techniques are shown by it.\n\n## Requirements\n\nAny fundamental requirements like an AWS account or the need for Cisco IOS devices are mentioned here.\n\n## Variables\n\nIdentify all variables used in the playbook here.\n\n### Required\n\nThis optional paragraph highlights required variables that need to be provided.\n\n### Optional\n\nThis optional paragraph highlights optional variables that might be provided to further alter the execution of the example.\n\n## Usage\n\nNecessary and notable commands to execute the example playbook are listed here.\n\n### One More Thing\n\nSpecial tips and advanced tricks regarding the example can be put here.\n\n```\n\nA goog example of a sophisticated and complete `README.md` can be found in the [cloud-aws example](cloud-aws/README.md).\n\n## Naming of examples\n\nExamples should be named in a short and clear way focussing on the use case they cover.\n"
  },
  {
    "path": "examples/apache-basic-playbook/README.md",
    "content": "# apache-basic-playbook\n\nThis example represents a basic yet complete playbook approximating the typical tasks used to deploy and configure a single application service (Apache) on a host running a Red Hat family linux.\n\nThis playbook assures the hosts in a group called \"web\" has the Apache web server (httpd) present and is started. The play also generates a basic configuration and custom home page using templates. If the configuration is changed, a handler task will execute to restart the apache httpd service.\n"
  },
  {
    "path": "examples/apache-basic-playbook/site.yml",
    "content": "---\n- name: Ensure apache is installed and started\n  hosts: web\n  become: yes\n  vars:\n    httpd_packages:\n      - httpd\n      - mod_wsgi\n    apache_test_message: This is a test message\n    apache_webserver_port: 80\n\n  tasks:\n    - name: Ensure httpd packages are present\n      yum:\n        name: \"{{ item }}\"\n        state: present\n      with_items: \"{{ httpd_packages }}\"\n      notify: restart-apache-service\n\n    - name: Ensure site-enabled directory is present\n      file:\n        name: /etc/httpd/conf/sites-enabled\n        state: directory\n\n    - name: Ensure latest httpd.conf is present\n      template:\n        src: templates/httpd.conf.j2\n        dest: /etc/httpd/conf/httpd.conf\n      notify: restart-apache-service\n\n    - name: Ensure latest index.html is present\n      template:\n        src: templates/index.html.j2\n        dest: /var/www/html/index.html\n\n    - name: Ensure httpd is started and enabled\n      service:\n        name: httpd\n        state: started\n        enabled: yes\n\n  handlers:\n    - name: restart-apache-service\n      service:\n        name: httpd\n        state: restarted\n"
  },
  {
    "path": "examples/apache-basic-playbook/templates/httpd.conf.j2",
    "content": "#\n# This is the main Apache HTTP server configuration file.  It contains the\n# configuration directives that give the server its instructions.\n# See <URL:http://httpd.apache.org/docs/2.4/> for detailed information.\n# In particular, see\n# <URL:http://httpd.apache.org/docs/2.4/mod/directives.html>\n# for a discussion of each configuration directive.\n#\n# Do NOT simply read the instructions in here without understanding\n# what they do.  They're here only as hints or reminders.  If you are unsure\n# consult the online docs. You have been warned.\n#\n# Configuration and logfile names: If the filenames you specify for many\n# of the server's control files begin with \"/\" (or \"drive:/\" for Win32), the\n# server will use that explicit path.  If the filenames do *not* begin\n# with \"/\", the value of ServerRoot is prepended -- so 'log/access_log'\n# with ServerRoot set to '/www' will be interpreted by the\n# server as '/www/log/access_log', where as '/log/access_log' will be\n# interpreted as '/log/access_log'.\n\n#\n# ServerRoot: The top of the directory tree under which the server's\n# configuration, error, and log files are kept.\n#\n# Do not add a slash at the end of the directory path.  If you point\n# ServerRoot at a non-local disk, be sure to specify a local disk on the\n# Mutex directive, if file-based mutexes are used.  If you wish to share the\n# same ServerRoot for multiple httpd daemons, you will need to change at\n# least PidFile.\n#\nServerRoot \"/etc/httpd\"\n\n#\n# Listen: Allows you to bind Apache to specific IP addresses and/or\n# ports, instead of the default. See also the <VirtualHost>\n# directive.\n#\n# Change this to Listen on specific IP addresses as shown below to\n# prevent Apache from glomming onto all bound IP addresses.\n#\n#Listen 12.34.56.78:80\nListen {{ apache_webserver_port }}\n\n#\n# Dynamic Shared Object (DSO) Support\n#\n# To be able to use the functionality of a module which was built as a DSO you\n# have to place corresponding `LoadModule' lines at this location so the\n# directives contained in it are actually available _before_ they are used.\n# Statically compiled modules (those listed by `httpd -l') do not need\n# to be loaded here.\n#\n# Example:\n# LoadModule foo_module modules/mod_foo.so\n#\nInclude conf.modules.d/*.conf\n\n#\n# If you wish httpd to run as a different user or group, you must run\n# httpd as root initially and it will switch.\n#\n# User/Group: The name (or #number) of the user/group to run httpd as.\n# It is usually good practice to create a dedicated user and group for\n# running httpd, as with most system services.\n#\nUser apache\nGroup apache\n\n# 'Main' server configuration\n#\n# The directives in this section set up the values used by the 'main'\n# server, which responds to any requests that aren't handled by a\n# <VirtualHost> definition.  These values also provide defaults for\n# any <VirtualHost> containers you may define later in the file.\n#\n# All of these directives may appear inside <VirtualHost> containers,\n# in which case these default settings will be overridden for the\n# virtual host being defined.\n#\n\n#\n# ServerAdmin: Your address, where problems with the server should be\n# e-mailed.  This address appears on some server-generated pages, such\n# as error documents.  e.g. admin@your-domain.com\n#\nServerAdmin root@localhost\n\n#\n# ServerName gives the name and port that the server uses to identify itself.\n# This can often be determined automatically, but we recommend you specify\n# it explicitly to prevent problems during startup.\n#\n# If your host doesn't have a registered DNS name, enter its IP address here.\n#\n#ServerName www.example.com:80\n\n#\n# Deny access to the entirety of your server's filesystem. You must\n# explicitly permit access to web content directories in other\n# <Directory> blocks below.\n#\n<Directory />\n    AllowOverride none\n    Require all denied\n</Directory>\n\n#\n# Note that from this point forward you must specifically allow\n# particular features to be enabled - so if something's not working as\n# you might expect, make sure that you have specifically enabled it\n# below.\n#\n\n#\n# DocumentRoot: The directory out of which you will serve your\n# documents. By default, all requests are taken from this directory, but\n# symbolic links and aliases may be used to point to other locations.\n#\nDocumentRoot \"/var/www/html\"\n\n#\n# Relax access to content within /var/www.\n#\n<Directory \"/var/www\">\n    AllowOverride None\n    # Allow open access:\n    Require all granted\n</Directory>\n\n# Further relax access to the default document root:\n<Directory \"/var/www/html\">\n    #\n    # Possible values for the Options directive are \"None\", \"All\",\n    # or any combination of:\n    #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews\n    #\n    # Note that \"MultiViews\" must be named *explicitly* --- \"Options All\"\n    # doesn't give it to you.\n    #\n    # The Options directive is both complicated and important.  Please see\n    # http://httpd.apache.org/docs/2.4/mod/core.html#options\n    # for more information.\n    #\n    Options Indexes FollowSymLinks\n\n    #\n    # AllowOverride controls what directives may be placed in .htaccess files.\n    # It can be \"All\", \"None\", or any combination of the keywords:\n    #   Options FileInfo AuthConfig Limit\n    #\n    AllowOverride None\n\n    #\n    # Controls who can get stuff from this server.\n    #\n    Require all granted\n</Directory>\n\n#\n# DirectoryIndex: sets the file that Apache will serve if a directory\n# is requested.\n#\n<IfModule dir_module>\n    DirectoryIndex index.html\n</IfModule>\n\n#\n# The following lines prevent .htaccess and .htpasswd files from being\n# viewed by Web clients.\n#\n<Files \".ht*\">\n    Require all denied\n</Files>\n\n#\n# ErrorLog: The location of the error log file.\n# If you do not specify an ErrorLog directive within a <VirtualHost>\n# container, error messages relating to that virtual host will be\n# logged here.  If you *do* define an error logfile for a <VirtualHost>\n# container, that host's errors will be logged there and not here.\n#\nErrorLog \"logs/error_log\"\nMaxKeepAliveRequests 115\n#\n# LogLevel: Control the number of messages logged to the error_log.\n# Possible values include: debug, info, notice, warn, error, crit,\n# alert, emerg.\n#\nLogLevel warn\n\n<IfModule log_config_module>\n    #\n    # The following directives define some format nicknames for use with\n    # a CustomLog directive (see below).\n    #\n    LogFormat \"%h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\"\" combined\n    LogFormat \"%h %l %u %t \\\"%r\\\" %>s %b\" common\n\n    <IfModule logio_module>\n      # You need to enable mod_logio.c to use %I and %O\n      LogFormat \"%h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\" %I %O\" combinedio\n    </IfModule>\n\n    #\n    # The location and format of the access logfile (Common Logfile Format).\n    # If you do not define any access logfiles within a <VirtualHost>\n    # container, they will be logged here.  Contrariwise, if you *do*\n    # define per-<VirtualHost> access logfiles, transactions will be\n    # logged therein and *not* in this file.\n    #\n    #CustomLog \"logs/access_log\" common\n\n    #\n    # If you prefer a logfile with access, agent, and referer information\n    # (Combined Logfile Format) you can use the following directive.\n    #\n    CustomLog \"logs/access_log\" combined\n</IfModule>\n\n<IfModule alias_module>\n    #\n    # Redirect: Allows you to tell clients about documents that used to\n    # exist in your server's namespace, but do not anymore. The client\n    # will make a new request for the document at its new location.\n    # Example:\n    # Redirect permanent /foo http://www.example.com/bar\n\n    #\n    # Alias: Maps web paths into filesystem paths and is used to\n    # access content that does not live under the DocumentRoot.\n    # Example:\n    # Alias /webpath /full/filesystem/path\n    #\n    # If you include a trailing / on /webpath then the server will\n    # require it to be present in the URL.  You will also likely\n    # need to provide a <Directory> section to allow access to\n    # the filesystem path.\n\n    #\n    # ScriptAlias: This controls which directories contain server scripts.\n    # ScriptAliases are essentially the same as Aliases, except that\n    # documents in the target directory are treated as applications and\n    # run by the server when requested rather than as documents sent to the\n    # client.  The same rules about trailing \"/\" apply to ScriptAlias\n    # directives as to Alias.\n    #\n    ScriptAlias /cgi-bin/ \"/var/www/cgi-bin/\"\n\n</IfModule>\n\n#\n# \"/var/www/cgi-bin\" should be changed to whatever your ScriptAliased\n# CGI directory exists, if you have that configured.\n#\n<Directory \"/var/www/cgi-bin\">\n    AllowOverride None\n    Options None\n    Require all granted\n</Directory>\n\n<IfModule mime_module>\n    #\n    # TypesConfig points to the file containing the list of mappings from\n    # filename extension to MIME-type.\n    #\n    TypesConfig /etc/mime.types\n\n    #\n    # AddType allows you to add to or override the MIME configuration\n    # file specified in TypesConfig for specific file types.\n    #\n    #AddType application/x-gzip .tgz\n    #\n    # AddEncoding allows you to have certain browsers uncompress\n    # information on the fly. Note: Not all browsers support this.\n    #\n    #AddEncoding x-compress .Z\n    #AddEncoding x-gzip .gz .tgz\n    #\n    # If the AddEncoding directives above are commented-out, then you\n    # probably should define those extensions to indicate media types:\n    #\n    AddType application/x-compress .Z\n    AddType application/x-gzip .gz .tgz\n\n    #\n    # AddHandler allows you to map certain file extensions to \"handlers\":\n    # actions unrelated to filetype. These can be either built into the server\n    # or added with the Action directive (see below)\n    #\n    # To use CGI scripts outside of ScriptAliased directories:\n    # (You will also need to add \"ExecCGI\" to the \"Options\" directive.)\n    #\n    #AddHandler cgi-script .cgi\n\n    # For type maps (negotiated resources):\n    #AddHandler type-map var\n\n    #\n    # Filters allow you to process content before it is sent to the client.\n    #\n    # To parse .shtml files for server-side includes (SSI):\n    # (You will also need to add \"Includes\" to the \"Options\" directive.)\n    #\n    AddType text/html .shtml\n    AddOutputFilter INCLUDES .shtml\n</IfModule>\n\n#\n# Specify a default charset for all content served; this enables\n# interpretation of all content as UTF-8 by default.  To use the\n# default browser choice (ISO-8859-1), or to allow the META tags\n# in HTML content to override this choice, comment out this\n# directive:\n#\nAddDefaultCharset UTF-8\n\n<IfModule mime_magic_module>\n    #\n    # The mod_mime_magic module allows the server to use various hints from the\n    # contents of the file itself to determine its type.  The MIMEMagicFile\n    # directive tells the module where the hint definitions are located.\n    #\n    MIMEMagicFile conf/magic\n</IfModule>\n\n#\n# Customizable error responses come in three flavors:\n# 1) plain text 2) local redirects 3) external redirects\n#\n# Some examples:\n#ErrorDocument 500 \"The server made a boo boo.\"\n#ErrorDocument 404 /missing.html\n#ErrorDocument 404 \"/cgi-bin/missing_handler.pl\"\n#ErrorDocument 402 http://www.example.com/subscription_info.html\n#\n\n#\n# EnableMMAP and EnableSendfile: On systems that support it,\n# memory-mapping or the sendfile syscall may be used to deliver\n# files.  This usually improves server performance, but must\n# be turned off when serving from networked-mounted\n# filesystems or if support for these functions is otherwise\n# broken on your system.\n# Defaults if commented: EnableMMAP On, EnableSendfile Off\n#\n#EnableMMAP off\nEnableSendfile on\n\n# Supplemental configuration\n#\n# Load config files in the \"/etc/httpd/conf.d\" directory, if any.\nIncludeOptional conf.d/*.conf\n"
  },
  {
    "path": "examples/apache-basic-playbook/templates/index.html.j2",
    "content": "<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>Ansible: Automation for Everyone</title>\n  <link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>\n  <style>\nbody {\n    font-family: 'Open Sans', sans-serif;\n    text-align: center;\n}\n.container {\n    position: absolute;\n    top: 50%;\n    left: 50%;\n    -moz-transform: translateX(-50%) translateY(-50%);\n    -webkit-transform: translateX(-50%) translateY(-50%);\n    transform: translateX(-50%) translateY(-50%);\n    font-size: 200%;\n}\nfooter {\n    width: 100%;\n    bottom: 0;\n    position: fixed;\n    font-size: 75%;\n}\nimg {\n    margin: 0 auto;\n}\n  </style>\n</head>\n<body>\n<div class=\"container\">\n    <img src=\"https://www.ansible.com/hubfs/2017_Images/BrandPage/Brand-Assets/Ansible_RH_AnsibleAutomation_RGB_RedBlack.png\" width=\"75%\"/>\n    <p>{{ apache_test_message }}</p>\n</div>\n<footer>{{ inventory_hostname }}<br />Red Hat Ansible</footer>\n</body>\n</html>\n"
  },
  {
    "path": "examples/apache-role/README.md",
    "content": "# apache-roles\n\nThis example demonstrates how roles are structured and used within a playbook. In communicating these concepts, this example is simply a refactoring of the `apache-basic-playbook` example into a role.\n"
  },
  {
    "path": "examples/apache-role/roles/apache-simple/defaults/main.yml",
    "content": "---\n# defaults file for apache\napache_test_message: This is a test message\napache_webserver_port: 80\n"
  },
  {
    "path": "examples/apache-role/roles/apache-simple/handlers/main.yml",
    "content": "---\n# handlers file for apache\n- name: restart-apache-service\n  service:\n    name: httpd\n    state: restarted\n"
  },
  {
    "path": "examples/apache-role/roles/apache-simple/tasks/main.yml",
    "content": "---\n# tasks file for apache\n- name: Ensure httpd packages are present\n  yum:\n    name: \"{{ item }}\"\n    state: present\n  with_items: \"{{ httpd_packages }}\"\n  notify: restart-apache-service\n\n- name: Ensure latest httpd.conf file is present\n  template:\n    src: httpd.conf.j2\n    dest: /etc/httpd/conf/httpd.conf\n  notify: restart-apache-service\n\n- name: Ensure latest index.html file is present\n  template:\n    src: index.html.j2\n    dest: /var/www/html/index.html\n\n- name: Ensure httpd service is started and enabled\n  service:\n    name: httpd\n    state: started\n    enabled: yes\n"
  },
  {
    "path": "examples/apache-role/roles/apache-simple/templates/httpd.conf.j2",
    "content": "#\n# This is the main Apache HTTP server configuration file.  It contains the\n# configuration directives that give the server its instructions.\n# See <URL:http://httpd.apache.org/docs/2.4/> for detailed information.\n# In particular, see\n# <URL:http://httpd.apache.org/docs/2.4/mod/directives.html>\n# for a discussion of each configuration directive.\n#\n# Do NOT simply read the instructions in here without understanding\n# what they do.  They're here only as hints or reminders.  If you are unsure\n# consult the online docs. You have been warned.\n#\n# Configuration and logfile names: If the filenames you specify for many\n# of the server's control files begin with \"/\" (or \"drive:/\" for Win32), the\n# server will use that explicit path.  If the filenames do *not* begin\n# with \"/\", the value of ServerRoot is prepended -- so 'log/access_log'\n# with ServerRoot set to '/www' will be interpreted by the\n# server as '/www/log/access_log', where as '/log/access_log' will be\n# interpreted as '/log/access_log'.\n\n#\n# ServerRoot: The top of the directory tree under which the server's\n# configuration, error, and log files are kept.\n#\n# Do not add a slash at the end of the directory path.  If you point\n# ServerRoot at a non-local disk, be sure to specify a local disk on the\n# Mutex directive, if file-based mutexes are used.  If you wish to share the\n# same ServerRoot for multiple httpd daemons, you will need to change at\n# least PidFile.\n#\nServerRoot \"/etc/httpd\"\n\n#\n# Listen: Allows you to bind Apache to specific IP addresses and/or\n# ports, instead of the default. See also the <VirtualHost>\n# directive.\n#\n# Change this to Listen on specific IP addresses as shown below to\n# prevent Apache from glomming onto all bound IP addresses.\n#\n#Listen 12.34.56.78:80\nListen {{ apache_webserver_port }}\n\n#\n# Dynamic Shared Object (DSO) Support\n#\n# To be able to use the functionality of a module which was built as a DSO you\n# have to place corresponding `LoadModule' lines at this location so the\n# directives contained in it are actually available _before_ they are used.\n# Statically compiled modules (those listed by `httpd -l') do not need\n# to be loaded here.\n#\n# Example:\n# LoadModule foo_module modules/mod_foo.so\n#\nInclude conf.modules.d/*.conf\n\n#\n# If you wish httpd to run as a different user or group, you must run\n# httpd as root initially and it will switch.\n#\n# User/Group: The name (or #number) of the user/group to run httpd as.\n# It is usually good practice to create a dedicated user and group for\n# running httpd, as with most system services.\n#\nUser apache\nGroup apache\n\n# 'Main' server configuration\n#\n# The directives in this section set up the values used by the 'main'\n# server, which responds to any requests that aren't handled by a\n# <VirtualHost> definition.  These values also provide defaults for\n# any <VirtualHost> containers you may define later in the file.\n#\n# All of these directives may appear inside <VirtualHost> containers,\n# in which case these default settings will be overridden for the\n# virtual host being defined.\n#\n\n#\n# ServerAdmin: Your address, where problems with the server should be\n# e-mailed.  This address appears on some server-generated pages, such\n# as error documents.  e.g. admin@your-domain.com\n#\nServerAdmin root@localhost\n\n#\n# ServerName gives the name and port that the server uses to identify itself.\n# This can often be determined automatically, but we recommend you specify\n# it explicitly to prevent problems during startup.\n#\n# If your host doesn't have a registered DNS name, enter its IP address here.\n#\n#ServerName www.example.com:80\n\n#\n# Deny access to the entirety of your server's filesystem. You must\n# explicitly permit access to web content directories in other\n# <Directory> blocks below.\n#\n<Directory />\n    AllowOverride none\n    Require all denied\n</Directory>\n\n#\n# Note that from this point forward you must specifically allow\n# particular features to be enabled - so if something's not working as\n# you might expect, make sure that you have specifically enabled it\n# below.\n#\n\n#\n# DocumentRoot: The directory out of which you will serve your\n# documents. By default, all requests are taken from this directory, but\n# symbolic links and aliases may be used to point to other locations.\n#\nDocumentRoot \"/var/www/html\"\n\n#\n# Relax access to content within /var/www.\n#\n<Directory \"/var/www\">\n    AllowOverride None\n    # Allow open access:\n    Require all granted\n</Directory>\n\n# Further relax access to the default document root:\n<Directory \"/var/www/html\">\n    #\n    # Possible values for the Options directive are \"None\", \"All\",\n    # or any combination of:\n    #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews\n    #\n    # Note that \"MultiViews\" must be named *explicitly* --- \"Options All\"\n    # doesn't give it to you.\n    #\n    # The Options directive is both complicated and important.  Please see\n    # http://httpd.apache.org/docs/2.4/mod/core.html#options\n    # for more information.\n    #\n    Options Indexes FollowSymLinks\n\n    #\n    # AllowOverride controls what directives may be placed in .htaccess files.\n    # It can be \"All\", \"None\", or any combination of the keywords:\n    #   Options FileInfo AuthConfig Limit\n    #\n    AllowOverride None\n\n    #\n    # Controls who can get stuff from this server.\n    #\n    Require all granted\n</Directory>\n\n#\n# DirectoryIndex: sets the file that Apache will serve if a directory\n# is requested.\n#\n<IfModule dir_module>\n    DirectoryIndex index.html\n</IfModule>\n\n#\n# The following lines prevent .htaccess and .htpasswd files from being\n# viewed by Web clients.\n#\n<Files \".ht*\">\n    Require all denied\n</Files>\n\n#\n# ErrorLog: The location of the error log file.\n# If you do not specify an ErrorLog directive within a <VirtualHost>\n# container, error messages relating to that virtual host will be\n# logged here.  If you *do* define an error logfile for a <VirtualHost>\n# container, that host's errors will be logged there and not here.\n#\nErrorLog \"logs/error_log\"\nMaxKeepAliveRequests 115\n#\n# LogLevel: Control the number of messages logged to the error_log.\n# Possible values include: debug, info, notice, warn, error, crit,\n# alert, emerg.\n#\nLogLevel warn\n\n<IfModule log_config_module>\n    #\n    # The following directives define some format nicknames for use with\n    # a CustomLog directive (see below).\n    #\n    LogFormat \"%h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\"\" combined\n    LogFormat \"%h %l %u %t \\\"%r\\\" %>s %b\" common\n\n    <IfModule logio_module>\n      # You need to enable mod_logio.c to use %I and %O\n      LogFormat \"%h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\" %I %O\" combinedio\n    </IfModule>\n\n    #\n    # The location and format of the access logfile (Common Logfile Format).\n    # If you do not define any access logfiles within a <VirtualHost>\n    # container, they will be logged here.  Contrariwise, if you *do*\n    # define per-<VirtualHost> access logfiles, transactions will be\n    # logged therein and *not* in this file.\n    #\n    #CustomLog \"logs/access_log\" common\n\n    #\n    # If you prefer a logfile with access, agent, and referer information\n    # (Combined Logfile Format) you can use the following directive.\n    #\n    CustomLog \"logs/access_log\" combined\n</IfModule>\n\n<IfModule alias_module>\n    #\n    # Redirect: Allows you to tell clients about documents that used to\n    # exist in your server's namespace, but do not anymore. The client\n    # will make a new request for the document at its new location.\n    # Example:\n    # Redirect permanent /foo http://www.example.com/bar\n\n    #\n    # Alias: Maps web paths into filesystem paths and is used to\n    # access content that does not live under the DocumentRoot.\n    # Example:\n    # Alias /webpath /full/filesystem/path\n    #\n    # If you include a trailing / on /webpath then the server will\n    # require it to be present in the URL.  You will also likely\n    # need to provide a <Directory> section to allow access to\n    # the filesystem path.\n\n    #\n    # ScriptAlias: This controls which directories contain server scripts.\n    # ScriptAliases are essentially the same as Aliases, except that\n    # documents in the target directory are treated as applications and\n    # run by the server when requested rather than as documents sent to the\n    # client.  The same rules about trailing \"/\" apply to ScriptAlias\n    # directives as to Alias.\n    #\n    ScriptAlias /cgi-bin/ \"/var/www/cgi-bin/\"\n\n</IfModule>\n\n#\n# \"/var/www/cgi-bin\" should be changed to whatever your ScriptAliased\n# CGI directory exists, if you have that configured.\n#\n<Directory \"/var/www/cgi-bin\">\n    AllowOverride None\n    Options None\n    Require all granted\n</Directory>\n\n<IfModule mime_module>\n    #\n    # TypesConfig points to the file containing the list of mappings from\n    # filename extension to MIME-type.\n    #\n    TypesConfig /etc/mime.types\n\n    #\n    # AddType allows you to add to or override the MIME configuration\n    # file specified in TypesConfig for specific file types.\n    #\n    #AddType application/x-gzip .tgz\n    #\n    # AddEncoding allows you to have certain browsers uncompress\n    # information on the fly. Note: Not all browsers support this.\n    #\n    #AddEncoding x-compress .Z\n    #AddEncoding x-gzip .gz .tgz\n    #\n    # If the AddEncoding directives above are commented-out, then you\n    # probably should define those extensions to indicate media types:\n    #\n    AddType application/x-compress .Z\n    AddType application/x-gzip .gz .tgz\n\n    #\n    # AddHandler allows you to map certain file extensions to \"handlers\":\n    # actions unrelated to filetype. These can be either built into the server\n    # or added with the Action directive (see below)\n    #\n    # To use CGI scripts outside of ScriptAliased directories:\n    # (You will also need to add \"ExecCGI\" to the \"Options\" directive.)\n    #\n    #AddHandler cgi-script .cgi\n\n    # For type maps (negotiated resources):\n    #AddHandler type-map var\n\n    #\n    # Filters allow you to process content before it is sent to the client.\n    #\n    # To parse .shtml files for server-side includes (SSI):\n    # (You will also need to add \"Includes\" to the \"Options\" directive.)\n    #\n    AddType text/html .shtml\n    AddOutputFilter INCLUDES .shtml\n</IfModule>\n\n#\n# Specify a default charset for all content served; this enables\n# interpretation of all content as UTF-8 by default.  To use the\n# default browser choice (ISO-8859-1), or to allow the META tags\n# in HTML content to override this choice, comment out this\n# directive:\n#\nAddDefaultCharset UTF-8\n\n<IfModule mime_magic_module>\n    #\n    # The mod_mime_magic module allows the server to use various hints from the\n    # contents of the file itself to determine its type.  The MIMEMagicFile\n    # directive tells the module where the hint definitions are located.\n    #\n    MIMEMagicFile conf/magic\n</IfModule>\n\n#\n# Customizable error responses come in three flavors:\n# 1) plain text 2) local redirects 3) external redirects\n#\n# Some examples:\n#ErrorDocument 500 \"The server made a boo boo.\"\n#ErrorDocument 404 /missing.html\n#ErrorDocument 404 \"/cgi-bin/missing_handler.pl\"\n#ErrorDocument 402 http://www.example.com/subscription_info.html\n#\n\n#\n# EnableMMAP and EnableSendfile: On systems that support it,\n# memory-mapping or the sendfile syscall may be used to deliver\n# files.  This usually improves server performance, but must\n# be turned off when serving from networked-mounted\n# filesystems or if support for these functions is otherwise\n# broken on your system.\n# Defaults if commented: EnableMMAP On, EnableSendfile Off\n#\n#EnableMMAP off\nEnableSendfile on\n\n# Supplemental configuration\n#\n# Load config files in the \"/etc/httpd/conf.d\" directory, if any.\nIncludeOptional conf.d/*.conf\n"
  },
  {
    "path": "examples/apache-role/roles/apache-simple/templates/index.html.j2",
    "content": "<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>Ansible: Automation for Everyone</title>\n  <link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>\n  <style>\nbody {\n    font-family: 'Open Sans', sans-serif;\n    text-align: center;\n}\n.container {\n    position: absolute;\n    top: 50%;\n    left: 50%;\n    -moz-transform: translateX(-50%) translateY(-50%);\n    -webkit-transform: translateX(-50%) translateY(-50%);\n    transform: translateX(-50%) translateY(-50%);\n    font-size: 200%;\n}\nfooter {\n    width: 100%;\n    bottom: 0;\n    position: fixed;\n    font-size: 75%;\n}\nimg {\n    margin: 0 auto;\n}\n  </style>\n</head>\n<body>\n<div class=\"container\">\n    <img src=\"https://www.ansible.com/hubfs/2017_Images/BrandPage/Brand-Assets/Ansible_RH_AnsibleAutomation_RGB_RedBlack.png\" width=\"75%\"/>\n    <p>{{ apache_test_message }}</p>\n</div>\n<footer>{{ inventory_hostname }}<br />Red Hat Ansible</footer>\n</body>\n</html>\n"
  },
  {
    "path": "examples/apache-role/roles/apache-simple/vars/main.yml",
    "content": "---\n# vars file for apache\nhttpd_packages:\n  - httpd\n  - mod_wsgi\n"
  },
  {
    "path": "examples/apache-role/site.yml",
    "content": "---\n- name: Ensure apache is installed and started via role\n  hosts: web\n  become: yes\n  roles:\n    - apache-simple\n"
  },
  {
    "path": "examples/apache-simple-playbook/README.md",
    "content": "# apache-simple-playbook\n\nThis example is designed to be used as a quick introduction to playbook structure that can easily fit on one slide deck that demonstrates how Ansible works.\n\nThis playbook assures the hosts in a group called \"web\" has the Apache web server (httpd) present and is started with a static custom home page. The hosts are presumed to be running a Red Hat family linux.\n\nThe playbook is intended for demonstration and instructional purpose. In reality it's too simplistic to be really useful. See `examples/apache-basic-playbook` for a more complete example using Apache.\n"
  },
  {
    "path": "examples/apache-simple-playbook/files/index.html",
    "content": "<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>Ansible: Automation for Everyone</title>\n  <link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>\n  <style>\nbody {\n    font-family: 'Open Sans', sans-serif;\n    text-align: center;\n}\n.container {\n    position: absolute;\n    top: 50%;\n    left: 50%;\n    -moz-transform: translateX(-50%) translateY(-50%);\n    -webkit-transform: translateX(-50%) translateY(-50%);\n    transform: translateX(-50%) translateY(-50%);\n    font-size: 200%;\n}\nfooter {\n    width: 100%;\n    bottom: 0;\n    position: fixed;\n    font-size: 75%;\n}\nimg {\n    margin: 0 auto;\n}\n  </style>\n</head>\n<body>\n<div class=\"container\">\n    <img src=\"https://www.ansible.com/hubfs/2017_Images/BrandPage/Brand-Assets/Ansible_RH_AnsibleAutomation_RGB_RedBlack.png\" width=\"75%\"/>\n</div>\n<footer>Red Hat Ansible</footer>\n</body>\n</html>\n"
  },
  {
    "path": "examples/apache-simple-playbook/site.yml",
    "content": "---\n- name: Ensure apache is installed and started\n  hosts: web\n  become: yes\n\n  tasks:\n  - name: Ensure httpd package is present\n    yum:\n      name: httpd\n      state: present\n\n  - name: Ensure latest index.html file is present\n    copy:\n      src: files/index.html\n      dest: /var/www/html/\n\n  - name: Ensure httpd is started\n    service:\n      name: httpd\n      state: started\n"
  },
  {
    "path": "examples/cloud-aws/README.md",
    "content": "# cloud-aws\n\nThis intermediate-level Ansible playbook example demonstrates the common tasks for provisioning EC2 server instances into an Amazon Web Services VPC. Once provisioned this example will use an existing role (apache-role) to deploy and setup an application service on the instances in the stack.\n\nThis example demonstrates a few other best practices and intermediate techniques:\n\n* **Compound Playbook.** This example shows the use of a playbook, `site.yml`, combining other playbooks, `provision.yml` and `setup.yml`, using play level 'include' statements. Splitting provisioning and configuration plays into separate files is a recommended best practice. By doing so, users have more flexibility to what the run without needing tags and wrapping blocks of tasks with conditionals. This separation can be used to promote reuse, but enabling the mixing and matching of various playbooks.\n* **Controlling Debug Message Display.** There is a debug task in `provison.yml` using the optional `verbosity` parameter. Avoiding the unnecessary display of debugging messages avoids confusion and is just good hygiene. In this implementation, the debug message won't be displayed unless the playbook is run in verbose mode level 1 or greater.\n\n## Requirements\n\nTo use this example you will need to have an AWS account setup and properly configured on your control machine. You will also need the boto, boto3 and botocore modules installed. See the [Ansible AWS Guide](http://docs.ansible.com/ansible/guide_aws.html) for more details.\n\n## Variables\n\nBefore running this playbook example, you should know about what variables it uses and how they effect the execution of the AWS provisioning process.\n\n### Required\n\nThe following variables must be properly set for this example to run properly.\n\n* `ec2_stack_name`: A unique name for your application stack. The stack name is used as a prefix for many other resources that will get created using this playbook.\n\n* `ec2_region`: A valid AWS region name such as \"us-east-2\" or \"ap-southeast-1\" or \"eu-west-2.\"\n\n* `ec2_key_name`: An existing AWS keyname from your account to use when provisioning instances.\n\n### Optional\n\nThere are a few other variables present whose value you can override if needed.\n\n* `ec2_az`: The EC2 availability zone to use in the region. Default: a\n\n* `ec2_vpcidr`: The VPC CIDR value. Default: 10.251.0.0/16\n\n* `ec2_subnetcidr`: The VPC Subnet CIDR value. Default: 10.251.1.0/24\n\n* `ec2_exact_count`: The number of EC2 instances that should be present. Using the `ec2` module, this playbook will create and terminate instances as needed. Default: 1\n\n* `ec2_private_key_file`: The path to the private key associated with the `ec2_key_name` used to launch the instances if you need it. If undefined, it is omitted from dynamic inventory group \"web\".\n\n## Usage\n\nTo execute this example, use the `site.yml` playbook and pass in all the required variables:\n\n```\nansible-playbook site.yml -e \"ec2_stack_name=lightbulb ec2_region=us-east-2 ec2_key_name=engima\"\n```\n\nUsing verbose mode of any type will emit a debugging message that displays information about the provisioned instances in the stack.\n\n```\nansible-playbook site.yml -e \"ec2_stack_name=lightbulb ec2_region=us-east-2 ec2_key_name=engima\" -v\n```\n\nRemember you can put your variables in a YAML formatted file and feed it into the play with the @ operator.\n\n```\nansible-playbook site.yml -e @extra_vars.yml\n```\n\n### One More Thing\n\nSince we are reusing the apache-simple roles from `examples/apache-role`, we can override the default value of `apache_test_message` to change the message that gets inserted onto the generated home page by the role.\n\n```\nansible-playbook site.yml -e \"ec2_stack_name=lightbulb ec2_region=us-east-2 ec2_key_name=engima apache_test_message=Hello_World\"\n```\n"
  },
  {
    "path": "examples/cloud-aws/provision.yml",
    "content": "---\n- name: Ensure servers are provisioned in vpc\n  hosts: localhost\n  gather_facts: false\n  vars:\n    # requires ec2_stack_name, ec2_region and ec2_key_name\n    ec2_exact_count: 1\n    ec2_vpcidr: 10.251.0.0/16\n    ec2_subnetcidr: 10.251.1.0/24\n    ec2_az: a\n\n  tasks:\n  - name: Ensure vpc is present\n    ec2_vpc_net:\n      state: present\n      region: \"{{ ec2_region }}\"\n      cidr_block: \"{{ ec2_vpcidr }}\"\n      name: \"{{ ec2_stack_name }}-vpc\"\n      tags:\n        tool: ansible\n    register: network\n\n  - name: Ensure vpc subnet is present\n    ec2_vpc_subnet:\n      region: \"{{ ec2_region }}\"\n      state: present\n      cidr: \"{{ ec2_subnetcidr }}\"\n      az: \"{{ ec2_region }}{{ ec2_az }}\"\n      resource_tags:\n        tool: ansible\n        Name: \"{{ ec2_stack_name }}-subnet-{{ ec2_az }}\"\n      vpc_id: \"{{ network.vpc.id }}\"\n    register: vpc_subnet\n\n  - name: Ensure vpc internet gateway is present\n    ec2_vpc_igw:\n      region: \"{{ ec2_region }}\"\n      vpc_id: \"{{ network.vpc.id }}\"\n      state: present\n    register: igw\n\n  - name: Ensure vpc public subnet route table is present\n    ec2_vpc_route_table:\n      region: \"{{ ec2_region }}\"\n      vpc_id: \"{{ network.vpc.id }}\"\n      tags:\n        Name: \"{{ ec2_stack_name }}-public\"\n      subnets:\n        - \"{{ vpc_subnet.subnet.id }}\"\n      routes:\n        - dest: 0.0.0.0/0\n          gateway_id: \"{{ igw.gateway_id }}\"\n\n  - name: Ensure vpc security group is present\n    ec2_group:\n      name: \"{{ ec2_stack_name }}-webservers\"\n      region: \"{{ ec2_region }}\"\n      description: SSH and HTTP/HTTPS\n      vpc_id: \"{{ network.vpc.id }}\"\n      rules:\n        - proto: tcp\n          from_port: 22\n          to_port: 22\n          cidr_ip: 0.0.0.0/0\n        - proto: tcp\n          from_port: 80\n          to_port: 80\n          cidr_ip: 0.0.0.0/0\n        - proto: tcp\n          from_port: 443\n          to_port: 443\n          cidr_ip: 0.0.0.0/0\n\n  - name: Search for the latest centos7 ami\n    ec2_ami_find:\n      owner: \"410186602215\"\n      region: \"{{ ec2_region }}\"\n      name: \"CentOS Linux 7 x86_64 HVM EBS*\"\n    register: find_results\n\n  - name: Get exact count of stack ec2 instances running\n    ec2:\n      key_name: \"{{ ec2_key_name }}\"\n      group: \"{{ ec2_stack_name }}-webservers\"\n      volumes:\n      - device_name: /dev/sda1\n        volume_type: gp2\n        volume_size: 8\n        delete_on_termination: true\n      vpc_subnet_id: \"{{ vpc_subnet.subnet.id }}\"\n      instance_type: t2.micro\n      image: \"{{ find_results.results[0].ami_id }}\"\n      wait: true\n      region: \"{{ ec2_region }}\"\n      exact_count: \"{{ ec2_exact_count }}\"\n      count_tag:\n        Count: \"{{ ec2_stack_name }}\"\n      instance_tags:\n        Name: \"{{ ec2_stack_name }}\"\n        Count: \"{{ ec2_stack_name }}\"\n      assign_public_ip: true\n    register: ec2\n\n  - name: Display stack instances info\n    debug:\n      var: ec2\n      verbosity: 1\n\n  - name: Ensure all instances are ready\n    wait_for:\n      port: 22\n      host: \"{{ item.public_ip }}\"\n      search_regex: OpenSSH\n    with_items: \"{{ ec2.tagged_instances }}\"\n\n  - name: Pause to let cloud init to complete\n    pause:\n      seconds: 90\n\n  - name: Build group of instances\n    add_host:\n      name: \"{{ item.public_dns_name }}\"\n      groups: web\n      ansible_user: centos\n      ansible_host: \"{{ item.public_ip }}\"\n      ansible_ssh_private_key_file: \"{{ ec2_private_key_file | default(omit) }}\"\n    with_items: \"{{ ec2.tagged_instances }}\"\n\n"
  },
  {
    "path": "examples/cloud-aws/setup.yml",
    "content": "---\n- name: Ensure apache server is present and running with example home page\n  hosts: web\n  become: yes\n  roles:\n    - apache-simple\n"
  },
  {
    "path": "examples/cloud-aws/site.yml",
    "content": "---\n- include: provision.yml\n- include: setup.yml\n"
  },
  {
    "path": "examples/nginx-basic-playbook/README.md",
    "content": "# nginx-basic-playbook\n\nThis example represents a basic yet complete playbook approximating the typical tasks used to deploy and configure a single application service (Nginx) on a host running a Red Hat family linux.\n\nThis playbook assures the hosts in a group called \"web\" has the Nginx web server along with uwsgi and other build dependencies are present and nginx is started. The play also generates a basic configuration and custom home page using templates. If the configuration is changed, a handler task will execute to restart the nginx service.\n\nThis example assumes that the EPEL repo has already be enabled on each host. This was intentionally left out to keep this example focused.\n\nThe playbook is also the solution to the primary assignment and one of the extra credit assignments in `workshop/basic_playbook`.\n"
  },
  {
    "path": "examples/nginx-basic-playbook/site.yml",
    "content": "# In keeping things simple, this example assumes the epel repo is enabled on each node\n---\n- name: Ensure nginx is installed and started with wsgi\n  hosts: web\n  become: yes\n  vars:\n    nginx_packages:\n      - nginx\n      - python-pip\n      - python-devel\n      - gcc\n    nginx_test_message: This is a test message\n    nginx_webserver_port: 80\n\n  tasks:\n    - name: Ensure nginx packages are present\n      yum:\n        name: \"{{ item }}\"\n        state: present\n      with_items: \"{{ nginx_packages }}\"\n      notify: restart-nginx-service\n\n    - name: Ensure uwsgi package is present\n      pip:\n        name: uwsgi\n        state: present\n      notify: restart-nginx-service\n\n    - name: Ensure latest default.conf is present\n      template:\n        src: templates/nginx.conf.j2\n        dest: /etc/nginx/nginx.conf\n        backup: yes\n      notify: restart-nginx-service\n\n    - name: Ensure latest index.html is present\n      template:\n        src: templates/index.html.j2\n        dest: /usr/share/nginx/html/index.html\n\n    - name: Ensure nginx service is started and enabled\n      service:\n        name: nginx\n        state: started\n        enabled: yes\n\n    # smoke test that nginx came up and is serving home page\n    - name: Ensure proper response from localhost can be received\n      uri:\n        url: \"http://localhost:{{ nginx_webserver_port }}/\"\n        return_content: yes\n      register: response\n      until: 'nginx_test_message in response.content'\n      retries: 10\n      delay: 1\n\n  handlers:\n    - name: restart-nginx-service\n      service:\n        name: nginx\n        state: restarted\n"
  },
  {
    "path": "examples/nginx-basic-playbook/templates/index.html.j2",
    "content": "<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>Ansible: Automation for Everyone</title>\n  <link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>\n  <style>\nbody {\n    font-family: 'Open Sans', sans-serif;\n    text-align: center;\n}\n.container {\n    position: absolute;\n    top: 50%;\n    left: 50%;\n    -moz-transform: translateX(-50%) translateY(-50%);\n    -webkit-transform: translateX(-50%) translateY(-50%);\n    transform: translateX(-50%) translateY(-50%);\n    font-size: 200%;\n}\nfooter {\n    width: 100%;\n    bottom: 0;\n    position: fixed;\n    font-size: 75%;\n}\nimg {\n    margin: 0 auto;\n}\n  </style>\n</head>\n<body>\n<div class=\"container\">\n    <img src=\"https://www.ansible.com/hubfs/2017_Images/BrandPage/Brand-Assets/Ansible_RH_AnsibleAutomation_RGB_RedBlack.png\" width=\"75%\"/>\n    <p>{{ nginx_test_message }}</p>\n</div>\n<footer>{{ inventory_hostname }}<br />Red Hat Ansible</footer>\n</body>\n</html>\n"
  },
  {
    "path": "examples/nginx-basic-playbook/templates/nginx.conf.j2",
    "content": "# Based on nginx version: nginx/1.10.1\n# For more information on configuration, see:\n#   * Official English Documentation: http://nginx.org/en/docs/\n#   * Official Russian Documentation: http://nginx.org/ru/docs/\n\nuser nginx;\nworker_processes auto;\nerror_log /var/log/nginx/error.log;\npid /run/nginx.pid;\n\n# Load dynamic modules. See /usr/share/nginx/README.dynamic.\ninclude /usr/share/nginx/modules/*.conf;\n\nevents {\n    worker_connections 1024;\n}\n\nhttp {\n    log_format  main  '$remote_addr - $remote_user [$time_local] \"$request\" '\n                      '$status $body_bytes_sent \"$http_referer\" '\n                      '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\n    access_log  /var/log/nginx/access.log  main;\n\n    sendfile            on;\n    tcp_nopush          on;\n    tcp_nodelay         on;\n    keepalive_timeout   115;\n    types_hash_max_size 2048;\n\n    include             /etc/nginx/mime.types;\n    default_type        application/octet-stream;\n\n    # Load modular configuration files from the /etc/nginx/conf.d directory.\n    # See http://nginx.org/en/docs/ngx_core_module.html#include\n    # for more information.\n    include /etc/nginx/conf.d/*.conf;\n\n    server {\n        listen       {{ nginx_webserver_port }} default_server;\n        listen       [::]:{{ nginx_webserver_port }} default_server;\n        server_name  _;\n        root         /usr/share/nginx/html;\n\n        # Load configuration files for the default server block.\n        include /etc/nginx/default.d/*.conf;\n\n        location / {\n        }\n\n        error_page 404 /404.html;\n            location = /40x.html {\n        }\n\n        error_page 500 502 503 504 /50x.html;\n            location = /50x.html {\n        }\n    }\n\n# Settings for a TLS enabled server.\n#\n#    server {\n#        listen       443 ssl http2 default_server;\n#        listen       [::]:443 ssl http2 default_server;\n#        server_name  _;\n#        root         /usr/share/nginx/html;\n#\n#        ssl_certificate \"/etc/pki/nginx/server.crt\";\n#        ssl_certificate_key \"/etc/pki/nginx/private/server.key\";\n#        ssl_session_cache shared:SSL:1m;\n#        ssl_session_timeout  10m;\n#        ssl_ciphers HIGH:!aNULL:!MD5;\n#        ssl_prefer_server_ciphers on;\n#\n#        # Load configuration files for the default server block.\n#        include /etc/nginx/default.d/*.conf;\n#\n#        location / {\n#        }\n#\n#        error_page 404 /404.html;\n#            location = /40x.html {\n#        }\n#\n#        error_page 500 502 503 504 /50x.html;\n#            location = /50x.html {\n#        }\n#    }\n\n}\n\n"
  },
  {
    "path": "examples/nginx-remove-playbook/README.md",
    "content": "# nginx-remove-playbook\n\nThis example demonstrates how to you would typically shutdown and remove an application service (nginx) from the hosts in a group called \"web\". The hosts are presumed to be running a Red Hat family linux.\n\nThis example was specifically developed as the solution to one of the extra credit assignments in `workshop/basic_playbook`.\n"
  },
  {
    "path": "examples/nginx-remove-playbook/site.yml",
    "content": "---\n- name: Ensure nginx with wsgi is removed\n  hosts: web\n  become: yes\n\n  tasks:\n    - name: Ensure nginx service is stopped\n      service:\n        name: nginx\n        state: stopped\n      ignore_errors: yes\n\n    - name: Ensure nginx package is absent\n      yum:\n        name: nginx\n        state: absent\n\n    - name: Ensure uwsgi package is absent\n      pip:\n        name: uwsgi\n        state: absent\n\n    - name: Ensure files created by nginx-simple are absent\n      file:\n        name: \"{{ item }}\"\n        state: absent\n      with_items:\n        - /etc/nginx/nginx.conf\n        - /usr/share/nginx/html/index.html\n"
  },
  {
    "path": "examples/nginx-role/README.md",
    "content": "# nginx-roles\n\nThis example demonstrates how roles are structured and used in a playbook. In communicating these concepts, this example is simply a refactoring of the `nginx-basic-playbook` and `nginx-remove-playbook` examples into a role.\n\nThis example requires Ansible v2.2 or later. It uses the `include_role` module that introduced in that version.\n\nThis also example assumes that the EPEL repo has already be enabled on each host. This was intentionally left out to keep this example focused.\n\nThis example is also the solution to the primary assignment and extra credit assignments in `workshop/roles`.\n"
  },
  {
    "path": "examples/nginx-role/remove.yml",
    "content": "---\n- name: Removes nginx and uwsgi\n  hosts: web\n  become: yes\n\n  tasks:\n    - name: Run remove tasks from nginx-simple role\n      include_role:\n        name: nginx-simple\n        tasks_from: remove\n"
  },
  {
    "path": "examples/nginx-role/roles/nginx-simple/defaults/main.yml",
    "content": "---\n# defaults file for nginx\nnginx_test_message: This is a test message\nnginx_webserver_port: 80\n"
  },
  {
    "path": "examples/nginx-role/roles/nginx-simple/handlers/main.yml",
    "content": "---\n# handlers file for nginx\n- name: restart-nginx-service\n  service:\n    name: nginx\n    state: restarted\n"
  },
  {
    "path": "examples/nginx-role/roles/nginx-simple/tasks/main.yml",
    "content": "---\n# tasks file for nginx\n- name: Ensure nginx packages are present\n  yum:\n    name: \"{{ item }}\"\n    state: present\n  with_items: \"{{ nginx_packages }}\"\n  notify: restart-nginx-service\n\n- name: Ensure uwsgi package is present\n  pip:\n    name: uwsgi\n    state: present\n  notify: restart-nginx-service\n\n- name: Ensure latest default.conf is present\n  template:\n    src: templates/nginx.conf.j2\n    dest: /etc/nginx/nginx.conf\n    backup: yes\n  notify: restart-nginx-service\n\n- name: Ensure latest index.html is present\n  template:\n    src: templates/index.html.j2\n    dest: /usr/share/nginx/html/index.html\n\n- name: Ensure nginx service is started and enabled\n  service:\n    name: nginx\n    state: started\n    enabled: yes\n\n# smoke test that nginx came up and is serving home page\n- name: Ensure proper response from localhost is received\n  uri:\n    url: http://localhost/\n    return_content: yes\n  register: response\n  until: 'nginx_test_message in response.content'\n  retries: 10\n  delay: 1\n"
  },
  {
    "path": "examples/nginx-role/roles/nginx-simple/tasks/remove.yml",
    "content": "---\n# tasks file the removes nginx and uwsgi\n# derived from examples/nginx-remove-playbook\n- name: Ensure nginx service is stopped\n  service:\n    name: nginx\n    state: stopped\n  ignore_errors: yes\n\n- name: Ensure nginx package is removed\n  yum:\n    name: nginx\n    state: absent\n\n- name: Ensure uwsgi is removed\n  pip:\n    name: uwsgi\n    state: absent\n\n- name: Clean up files created by nginx-simple\n  file:\n    name: \"{{ item }}\"\n    state: absent\n  with_items:\n    - /etc/nginx/nginx.conf\n    - /usr/share/nginx/html/index.html\n"
  },
  {
    "path": "examples/nginx-role/roles/nginx-simple/templates/index.html.j2",
    "content": "<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>Ansible: Automation for Everyone</title>\n  <link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>\n  <style>\nbody {\n    font-family: 'Open Sans', sans-serif;\n    text-align: center;\n}\n.container {\n    position: absolute;\n    top: 50%;\n    left: 50%;\n    -moz-transform: translateX(-50%) translateY(-50%);\n    -webkit-transform: translateX(-50%) translateY(-50%);\n    transform: translateX(-50%) translateY(-50%);\n    font-size: 200%;\n}\nfooter {\n    width: 100%;\n    bottom: 0;\n    position: fixed;\n    font-size: 75%;\n}\nimg {\n    margin: 0 auto;\n}\n  </style>\n</head>\n<body>\n<div class=\"container\">\n    <img src=\"https://www.ansible.com/hubfs/2017_Images/BrandPage/Brand-Assets/Ansible_RH_AnsibleAutomation_RGB_RedBlack.png\" width=\"75%\"/>\n    <p>{{ nginx_test_message }}</p>\n</div>\n<footer>{{ inventory_hostname }}<br />Red Hat Ansible</footer>\n</body>\n</html>\n"
  },
  {
    "path": "examples/nginx-role/roles/nginx-simple/templates/nginx.conf.j2",
    "content": "# Based on nginx version: nginx/1.10.1\n# For more information on configuration, see:\n#   * Official English Documentation: http://nginx.org/en/docs/\n#   * Official Russian Documentation: http://nginx.org/ru/docs/\n\nuser nginx;\nworker_processes auto;\nerror_log /var/log/nginx/error.log;\npid /run/nginx.pid;\n\n# Load dynamic modules. See /usr/share/nginx/README.dynamic.\ninclude /usr/share/nginx/modules/*.conf;\n\nevents {\n    worker_connections 1024;\n}\n\nhttp {\n    log_format  main  '$remote_addr - $remote_user [$time_local] \"$request\" '\n                      '$status $body_bytes_sent \"$http_referer\" '\n                      '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\n    access_log  /var/log/nginx/access.log  main;\n\n    sendfile            on;\n    tcp_nopush          on;\n    tcp_nodelay         on;\n    keepalive_timeout   115;\n    types_hash_max_size 2048;\n\n    include             /etc/nginx/mime.types;\n    default_type        application/octet-stream;\n\n    # Load modular configuration files from the /etc/nginx/conf.d directory.\n    # See http://nginx.org/en/docs/ngx_core_module.html#include\n    # for more information.\n    include /etc/nginx/conf.d/*.conf;\n\n    server {\n        listen       {{ nginx_webserver_port }} default_server;\n        listen       [::]:{{ nginx_webserver_port }} default_server;\n        server_name  _;\n        root         /usr/share/nginx/html;\n\n        # Load configuration files for the default server block.\n        include /etc/nginx/default.d/*.conf;\n\n        location / {\n        }\n\n        error_page 404 /404.html;\n            location = /40x.html {\n        }\n\n        error_page 500 502 503 504 /50x.html;\n            location = /50x.html {\n        }\n    }\n\n# Settings for a TLS enabled server.\n#\n#    server {\n#        listen       443 ssl http2 default_server;\n#        listen       [::]:443 ssl http2 default_server;\n#        server_name  _;\n#        root         /usr/share/nginx/html;\n#\n#        ssl_certificate \"/etc/pki/nginx/server.crt\";\n#        ssl_certificate_key \"/etc/pki/nginx/private/server.key\";\n#        ssl_session_cache shared:SSL:1m;\n#        ssl_session_timeout  10m;\n#        ssl_ciphers HIGH:!aNULL:!MD5;\n#        ssl_prefer_server_ciphers on;\n#\n#        # Load configuration files for the default server block.\n#        include /etc/nginx/default.d/*.conf;\n#\n#        location / {\n#        }\n#\n#        error_page 404 /404.html;\n#            location = /40x.html {\n#        }\n#\n#        error_page 500 502 503 504 /50x.html;\n#            location = /50x.html {\n#        }\n#    }\n\n}\n\n"
  },
  {
    "path": "examples/nginx-role/roles/nginx-simple/vars/main.yml",
    "content": "---\n# vars file for nginx\nnginx_packages:\n  - nginx\n  - python-pip\n  - python-devel\n  - gcc\n"
  },
  {
    "path": "examples/nginx-role/site.yml",
    "content": "# In keeping things simple, this example assumes the epel repo is enabled on each node\n---\n- name: Ensure nginx is installed and started via role\n  hosts: web\n  become: yes\n  roles:\n    - nginx-simple\n"
  },
  {
    "path": "examples/nginx-simple-playbook/README.md",
    "content": "# nginx-simple-playbook\n\nThis example is designed to be used as a quick introduction to playbook structure that can easily fit on one slide deck that demonstrates how Ansible works.\n\nThis playbook assures the hosts in a group called \"web\" has the Nginx web server present and is started with a static custom home page. The hosts are presumed to be running a Red Hat family linux.\n\nThis example assumes that the EPEL repo has already be enabled on each host. This was intentionally left out to keep this example simple and focused.\n\nThe playbook is intended for demonstration and instructional purpose. In reality it's too simplistic to be really useful. See `examples/nginx-basic-playbook` for a more complete example using nginx.\n"
  },
  {
    "path": "examples/nginx-simple-playbook/files/index.html",
    "content": "<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>Ansible: Automation for Everyone</title>\n  <link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>\n  <style>\nbody {\n    font-family: 'Open Sans', sans-serif;\n    text-align: center;\n}\n.container {\n    position: absolute;\n    top: 50%;\n    left: 50%;\n    -moz-transform: translateX(-50%) translateY(-50%);\n    -webkit-transform: translateX(-50%) translateY(-50%);\n    transform: translateX(-50%) translateY(-50%);\n    font-size: 200%;\n}\nfooter {\n    width: 100%;\n    bottom: 0;\n    position: fixed;\n    font-size: 75%;\n}\nimg {\n    margin: 0 auto;\n}\n  </style>\n</head>\n<body>\n<div class=\"container\">\n    <img src=\"https://www.ansible.com/hubfs/2017_Images/BrandPage/Brand-Assets/Ansible_RH_AnsibleAutomation_RGB_RedBlack.png\" width=\"75%\"/>\n</div>\n<footer>Red Hat Ansible</footer>\n</body>\n</html>\n"
  },
  {
    "path": "examples/nginx-simple-playbook/site.yml",
    "content": "# In keeping things simple, this example assumes the epel repo is enabled on each node\n---\n- name: Ensure nginx is installed and started\n  hosts: web\n  become: yes\n\n  tasks:\n  - name: Ensure nginx package is present\n    yum:\n      name: nginx\n      state: present\n\n  - name: Ensure latest index.html is present\n    copy:\n      src: files/index.html\n      dest: /usr/share/nginx/html\n\n  - name: Ensure nginx service is started\n    service:\n      name: nginx\n      state: started\n"
  },
  {
    "path": "facilitator/README.md",
    "content": "# The Ansible Lightbulb Facilitator's Guide\n\nThe Ansible Lightbulb project is an effort to provide a reference and educational content for rapidly communicating and learning Ansible and Ansible Tower essentials. Lightbulb has been designed as a multi-purpose toolkit for effectively demonstrating Ansible's capabilities or providing informal workshop training in various forms -- instructor-led, hands-on or self-paced.\n\nThis guide covers different ways Ansible suggests they are assembled for various scenarios.\n\n## Getting Started\n\nIf you haven't read it already, start with the [Ansible Lightbulb README](../README.md). There you will find what tools are in the toolkit and the requirements for using this content.\n\nWe also recommend reading about [the Lightbulb philosophy](../PHILOSOPHY.md) for effectively communicating and delivering informal training on Ansible in the same spirit that it has become known for and highly successful.\n\nIf you are already somewhat familiar with automating with Ansible, we recommend that you are also familiar with the best practices this content incorporates. See [this blog post](https://www.ansible.com/blog/ansible-best-practices-essentials) and [video presentation from AnsibleFest Brooklyn (October 2016)](https://www.ansible.com/ansible-best-practices).\n\nThe Lightbulb project provides a lab provisioner tool for creating an environment to present and work with Lightbulb content. Currently the provisioner only supports provisioning lab environments using Amazon Web Services. (A Vagrant option will be developed particularly for self-paced learning.) See the [Ansible AWS Lab Provisioner README](../tools/aws_lab_setup/README.md) for details on requirements, setup and configuration.\n\n## Lightbulb Modules\n\nLightbulb modules are collections of this content that have been bundled to communicate and/or teach automating with Ansible based on objectives and scenarios. These modules are the \"use cases\" that all Lightbulb content was specifically developed. The project has endeavored to be as modular and flexible to make other uses possible.\n\n### Live Demonstration\n\n**Scenario**: Quickly demonstrating how Ansible or Ansible Tower works to an audience that has little to no knowledge on the topic.\n\n**Run Time**: ~5-10 minutes to briefly explain and execute one example.\n\nThe content in `examples/` can be used to effectively demonstrate Ansible's features to supplement a presentation. These examples have been carefully curated to communicate how Ansible works in a simple and focused way and are easy to demonstrate live and in-action.\n\nFor a basic linux server automation demonstration we recommend `examples/apache-basic-playbook`. This example is a single playbook you can put up on a screen with limited scrolling and virtually no file system navigation. For a slightly more sophisticated linux server example, use the Nginx variant in `examples/nginx-basic-playbook`.\n\n### Ansible Essentials Workshop\n\n**Scenario**: Providing instruction on the core essentials to students that have little to no experience automating with Ansible on linux servers.\n\n**Run Time**: 2 Hours\n\nThis module is designed to provide some valuable instruction with a limited amount of time and/or resources. It is also ideal for addressing large audiences where the overhead of provisioning and supporting the use of individual labs is not feasible. This module is also ideal for remote instruction like webinars.\n\n#### Presentation Deck\n\nFor a basic linux server automation use `decks/ansible-essentials.html` and only navigate to the right. Going down will take you through the more in-depth content and workshops of the Ansible Essentials Hands-on Workshop.\n\n#### Examples & Workshops\n\n##### Examples\n\n* apache-simple-playbook\n* apache-basic-playbook\n* apache-roles\n\nBesides walking students through these examples, the facilitator of this module will also be asked to install Ansible using pip and demonstrate some ad-hoc commands use. Installing Ansible is optional though highly recommended as it shows how easy it is to get started.\n\nAn alternative option for delivering this module to a more sophisticated audience is to use the Nginx variants of the Apache examples instead:\n\n* nginx-simple-playbook\n* nginx-basic-playbook\n* nginx-roles\n\n##### Workshops\n\nNone.\n\n#### Lab Environment\n\nOnly the facilitator of the workshop needs a lab environment making this module ideal for scenarios where time and facilities are limited. We recommend the instructor's lab environment be made up of a single \"control\" host in a group of the same name and 3 CentOS or RHEL linux hosts in a group called \"web\". The examples used by this module require very few system resources to run. Something akin to a \"micro\" instance is all that is needed for this particular module. You will need to consider the requirements of other modules if you are going to chain this one with others though.\n\n### Ansible Essentials Hands-On Workshop\n\n**Scenario**: Providing hands-on instruction on the core essentials to students with limited experience automating with Ansible on linux servers.\n\n**Run Time**: 4 Hours\n\nThis module is designed to provide students with direct introductory instruction and guidance to beginning to automate with Ansible. It is the starting point for students intent on becoming more proficient with Ansible through other Lightbulb modules and their own usage.\n\nIt is ideal for addressing small to medium size audiences of students that are committed to learning how to automate with Ansible.\n\nThis module is the 2 hour workshop with a bit of extra depth and hands-on workshops. Delivering this module means providing each student with a lab environment and depending on the size and skill-level of the students in your audience, having one or more assistants to help students during the workshops.\n\n#### Presentation Deck\n\nFor a basic linux server automation use `decks/ansible-essentials.html` navigating down when given the option.\n\n#### Examples & Workshops\n\nUnlike the 2 hour non-interactive Ansible Essentials Workshop, this module makes use of both the apache and nginx set of examples. The apache examples are used by the facilitator to demonstrate and walk-thru with the class. The nginx examples are the solutions to the workshops they will be assigned.\n\n##### Examples\n\n* apache-simple-playbook (optional)\n* nginx-simple-playbook (optional)\n* apache-basic-playbook\n* nginx-basic-playbook\n* apache-roles\n* nginx-roles\n\n##### Workshops\n\n* ansible_install (optional though highly recommended)\n* adhoc_commands\n* simple_playbook (optional)\n* basic_playbook\n* roles\n\nWhen pressed for time or dealing with a more technical audience you can opt to skip the \"simple\" examples and workshops. The \"basic\" ones cover the same topics and more.\n\nWhen applicable, most workshops provide \"extra credit\" assignments for students that are more advanced or fast-learners.\n\nFacilitators can opt to pre-install Ansible on each control machine for the students allowing you to skip that workshop. We recommend you don't do that though, to show how easy it is to get started using Ansible. It should take 10 minutes or less.\n\n#### Lab Environment\n\nThis lab requires each student have their own lab environment in order to perform the workshop assignments. Use the Lightbulb provisioner tool to provision these student labs in advance.\n\nWe generally recommend **NOT** using Vagrant with groups. In our experience too much time gets spent helping students install, configure and troubleshoot Vagrant along with their lab environment that could be better spent on Ansible teaching.\n\nSharing a single lab environment amongst a group of students simply will not work.\n\nWe recommend the lab environments be made up of a single \"control\" host in a group of the same name and 3 CentOS or RHEL linux hosts in a group called \"web\". The examples used by this module require very few system resources to run. Something akin to a \"micro\" instance is all that is needed. You will need to consider the requirements of other modules if you are going to chain this one with others though.\n\n### Introduction to Ansible Tower Workshop\n\n**Scenario**: Providing basic instruction to students with limited experience using Ansible Tower on how it works and can be used to facilitate and manage Ansible automation in their organization.\n\n**Run Time**: 1 Hour\n\n**Prerequisites**: Ansible Essentials Workshop or Ansible Essentials Hands-On Workshop\n\nThis module is designed to provide some valuable instruction on using Ansible Tower with a limited amount of time and/or resources. It is also ideal for addressing large audiences where the overhead of provisioning and supporting the use of individual labs required to execute hands-on workshops is not feasible. This module is also ideal for remote instruction like webinars.\n\n**NOTE**: The first rule of learning Ansible Tower is to learn Ansible core first. The second rule is to see the first rule. Don't start here.\n\n#### Presentation Deck\n\nFor automating with Ansible Tower use `decks/intro-to-ansible-tower.html` and only navigate to the right. Going down will take you through the more in-depth content and workshops of the Introduction to Ansible Tower Hands-On Workshop.\n\n#### Examples & Workshops\n\n##### Examples\n\nThe example(s) here are mostly at the facilitator's discretion. We recommend the apache-roles or nginx-roles example since those are the most sophisticated one students are exposed to in the Essentials workshops that should have preceded this module.\n\n##### Workshops\n\nNone.\n\n#### Lab Environment\n\nOnly the facilitator of the workshop needs a lab environment making this module ideal for scenarios where time and facilities are limited. We recommend the instructor's lab environment be made up of a single \"control\" host in a group of the same name and 3 CentOS or RHEL linux hosts in a group called \"web\". While the web hosts need only minimal resources, the control instance will take additional memory and storage requirements required by Ansible Tower system on it.\n\n### Introduction to Ansible Tower Hands-On Workshop\n\n**Scenario**: Providing hands-on instruction to students with limited experience with Ansible Tower on how it works and how it can be used to facilitate and manage Ansible automation in their organization.\n\n**Run Time**: 2 Hours\n\n**Prerequisites**: Ansible Essentials Workshop or Ansible Essentials Hands-On Workshop\n\nThis module is designed to provide some valuable instruction on using Ansible Tower with a limited amount of time and/or resources. It is also ideal for addressing large audiences where the overhead of provisioning and supporting the use of individual labs required to execute hands-on workshops is not feasible. This module is also ideal for remote instruction like webinars.\n\n**NOTE**: The first rule of learning Ansible Tower is to learn Ansible core first. The second rule is to see the first rule. Don't start here.\n\n#### Presentation Deck\n\nFor a basic linux server automation use `decks/intro-to-ansible-tower.html` navigating down when given the option.\n\n#### Examples & Workshops\n\n##### Examples\n\nThe example(s) here are mostly at the facilitator's discretion. We recommend the apache-roles or nginx-roles example since those are the most sophisticated one students are exposed to in the Essentials workshops that should have preceded this module.\n\n##### Workshops\n\n* tower_install (optional though highly recommended)\n* tower_basic_setup\n\nWhen applicable, most workshops provide \"extra credit\" assignments for students that are more advanced or fast-learners.\n\nFacilitators can opt to pre-install Ansible Tower on each control machine for the students allowing you to skip that workshop.\n\n#### Lab Environment\n\nThis lab requires each student to have their own lab environment in order to perform the workshop assignments. Use the Lightbulb provisioner tool to provision these student labs in advance.\n\nWe generally recommend **NOT** using Vagrant with groups. In our experience too much time gets spent helping students install, configure and troubleshoot Vagrant along with their lab environment that could be better spent on Ansible teaching.\n\nSharing a single lab environment amongst a group of students simply will not work.\n\nWe recommend the lab environments be made up of a single \"control\" host in a group of the same name and 3 CentOS or RHEL linux hosts in a group called \"web\". While the web hosts need only minimal resources, the control instance will take additional memory and storage requirements required by Ansible Tower system on it.\n\n### Introduction to Ansible and Ansible Tower Workshop\n\n**Scenario**: Providing an in-depth overview to an audience with limited Ansible and Ansible Tower experience on how they can be used to facilitate and manage Ansible automation in their organization.\n\n**Run Time**: 3 Hours\n\nThis module is designed to provide basic instruction on using Ansible and Ansible Tower with a limited amount of time and/or resources.\n\nThis module is the combination of two modules:\n\n* Ansible Essentials\n* Introduction to Ansible Tower\n\nRefer to the docs on those modules and run concurrently.\n\n### Introduction to Ansible and Ansible Tower Hands-On Workshop\n\n**Scenario**: Providing hands-on instruction on the core essentials and Ansible Tower to students with limited experience automating with Ansible on linux servers.\n\n**Run Time**: 6 Hours\n\nThis module is designed to provide hands-on interactive instruction on using Ansible and Ansible Tower.\n\nThis module is the combination of two modules:\n\n* Ansible Essentials Hands-On\n* Introduction to Ansible Tower Hands-On\n\nRefer to the docs on those modules and run concurrently.\n"
  },
  {
    "path": "facilitator/solutions/adhoc_commands.md",
    "content": "# Workshop: Ad-Hoc Commands\n\nThis brief exercise demonstrates Ansible in-action at it's most basic and simple level. Thru ad-hoc commands, students are exposed to Ansible modules and usage and will apply to their understanding of tasks and playbooks. This exercise also begins to expose students to the concepts of Ansible facts and inventory.\n\nThis workshop is also a good way to verify their lab environments are properly configured before going forward.\n\n## Solution\n\nThe following commands are the solution for the workshop and extra credit assignments.\n\n```bash\nansible all -m ping\n\nansible all -m setup\n\nansible web -b -m yum -a \"name=epel-release state=present\"\nansible web -b -m yum -a \"name=https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm state=present\"\n\n---\n\nansible all -m setup -a \"gather_subset=virtual\"\n\nansible all -m setup -a \"filter=ansible_fqdn*\"\n\nansible all -m command -a \"uptime\"\n\nansible all -m ping --limit '!control'\n```\n\n### NOTE\n\nYou will need to make sure each student successfully installed the EPEL repo here. Later workshop assignments ask students to install Nginx. Without EPEL enabled on each web host `yum` will not be able to locate the package.\n"
  },
  {
    "path": "facilitator/solutions/ansible_install.md",
    "content": "# Workshop: Installing Ansible\n\nThis brief exercise demonstrates how easy it can be to install and configure Ansible and begin automating.\n\nNOTE: If and how you conduct this workshop depends on how you configured the provisioner to run. To save time, you can have lab control machines setup with Ansible. If so, you can skip with workshop.\n\nWe use pip because it's OS independent and always has the absolute latest stable release of Ansible. Other package repos such as EPEL can sometime lag behind for days or even weeks. For this reason, Ansible by Red Hat recommends using PIP.\n\n## Solution\n\nEach student will need to SSH into their \"control\" machine using the host IP, user and password provided to them in their lab environment inventory file.\n\nFrom that control machine:\n\n```bash\nsudo yum install -y ansible\n\nansible --version\n\nansible --help\n\nmkdir -p ~/.ansible/retry-files\nvi ~/.ansible.cfg\n# add forks and retry_files_save_path\n\n```\n\n## NOTE\n\nDepending how the lab provisioner was run, students may already have Ansible on their control machines. You can still have them run Ansible with `--version` and `--help` options to check they can ssh into their control box and prove Ansible is indeed present.\n\nWhatever the case, you should make sure each student has the core Ansible software before proceeding.\n\n"
  },
  {
    "path": "facilitator/solutions/basic_playbook.md",
    "content": "# Workshop: Basic Playbook\n\nHere students are tasked with developing their first complete playbook. The assignment approximates the tasks they will typical need to take in order to deploy and configure a single application service using Nginx.\n\nThe \"simple\" Lightbulb examples, students may or may not have done depending on agenda, provide a quick introduction to playbook structure to give them a feel for how Ansible works, but in practice are too simplistic to be useful.\n\n## Tips\n\nDon't rush this workshop. Give students ample time to do this workshop themselves to completion. This workshop covers the essential concepts and features of effectively automation with Ansible and builds up their core skills for further exploration.\n\n## Solution\n\nThe solution to this workshop is `nginx-basic-playbook` under `examples/`.\n\nThe `nginx-basic-playbook` example also includes the first extra credit assignment via the last task using the `uri` module. We intentionally only check that the web server is responding with the home page we generated by searching for the value of `nginx_test_message`. We are not checking if the server is accessible to outside world in keeping things basic.\n\nThe solution to the second extra credit assignment is `nginx-remove-playbook` under `examples/`.\n"
  },
  {
    "path": "facilitator/solutions/roles.md",
    "content": "# Workshop: Roles\n\nHere students are tasked with refactoring their previous work from the Basic Playbook workshop into a role and modifying their playbook accordingly. We intentionally avoid introducing any new tasks or functionality otherwise. The objective here is to focus students specifically on how roles are structured and develop.\n\nYou should emphasize the value of roles in better organizing playbooks as they grow in sophistication and making Ansible automation more portable and reusable than a basic playbook.\n\n## Defaults vs. Vars\n\nA common question that is asked \"when do I put a variable in defaults instead of a vars?\" It depends on the usage of a variable in the context of a role. It all comes down to variable precedence.\n\nIf a variable holds a data that someone using the role may want to override anyway numbers of ways, then it's best stored under `defaults/`. If a variable holds data that is internal to the function of the role and rarely (or in practice should never) be modified, then it's best stored under `vars/` where its much hard to be overridden by other variables sources.\n\nApplying these guidelines to this assignment, `nginx_packages` would go in `vars/` while `nginx_test_message` and `nginx_webserver_port` would go in `defaults/`.\n\n## NOTE\n\nThe extra credit assignment should use the `include_role` module that was introduced in version 2.2. Students working with older versions of Ansible will have trouble completing the assignment.\n\n## Solution\n\nThe solution to this workshop is `nginx-role` under `examples/`. This example also includes the solution to the extra credit assignment. See `remove.yml` and `roles/nginx-basic/tasks/remove.yml`.\n"
  },
  {
    "path": "facilitator/solutions/simple_playbook.md",
    "content": "# Workshop: Simple Playbook\n\nThis assignment provides a quick introduction to playbook structure to give them a feel for how Ansible works, but in practice is too simplistic to be useful.\n\n## Tips\n\nThis workshop is a subset of the basic_playbook workshop students will do next.  Depending on the technical aptitude of students and time available you can opt to skip this workshop assignment. Everything here and a lot more will in the next workshop assignment.\n\n## Solution\n\nThe solution to this workshop is `nginx-simple-playbook` under `examples/`.\n"
  },
  {
    "path": "facilitator/solutions/tower_basic_setup.md",
    "content": "# Workshop: Ansible Tower Basic Setup\n\nHere students are tasked with installing and activating a new single integrated instance of Ansible Tower. The assignment will show students how they can setup an instance of Ansible Tower themselves to run an existing playbook for testing, demos and their own proof-of-concepts.\n\n## Tips\n\nThis workshop can be presented in a classroom setting as a guided exercises to save a bit of time. The facilitator has the students follow along as they step thru each part of the corresponding demo.\n\nStepping thru the solution provides an excellent opportunity to briefly mention other features in Tower such as schedule jobs not explored in this workshop assignment.\n\n## Solution\n\nThe focus of this exercise is to familiarize students with how to use the Ansible Tower UI. The following screenshots approximate the steps required to complete the workshop.\n\n### 1\n\nFirst we will need to create the inventory in which our hosts will be housed. To do so, simply select that Inventory tile at the top of your Ansible Tower dashboard\n![creating inventory](../images/creating_inventory.png)\n\n### 1.a\n\nAs we spoke about before, this step is something that is not widely done in production as you can take advantage of Ansible Tower's dynamic inventory support. To create a host, simply select \"+Add Host\"\n![creating hosts](../images/creating_host.png)\n\n### 1.b\n\nOnce you have put in the necessary information for one host, click Save and it will return you to your respective Inventory page. From here, you can either add more hosts or move on to the things needed to connect to the machines, credentials.\n![final inventory](../images/finalized_inv.png)\n\n### 2\n\nCredentials are essential in automating with ansible. To create a credential within Ansible Tower, select the gear in the top right hand corner of the screen. This will take you to the admin where you can select credentials box. This will display any current credentials you have in your Tower instance. To create a new credential, simply click +Add. This will bring up a new page where you can enter the relevant information for your credential. Since we are creating a machine credential, we will select machine from the Type dropdown and enter the needed information. Once done, select save and your credential will be saved.\n\n![creating machine credential](../images/LBcreatingcred.png)\n\n### 3\n\nAdding a project to Ansible Tower so that you can use your playbooks is very simple. To start the process, simply select the Project tile from the top of your Ansible Tower dashboard. This will display any current projects that you have added to your Tower Instance. From here, select \"+Add\" to create a new project. This will then display the New Project page. From here, you can add the required information to get your repository added as a project.\n![creating a project](../images/project_creation.png)\n\n### 4\n\nJob templates are a visual realization of the ansible-playbook command and all flags you can utilize when executing from the command line. Everything you used on the command line, such as calling the invenotry or specifying a credential is done here. Creating a job template is simple and direct. Everything you need for it is outlined but has extra options and capabilities to make it even more powerful. To create a Job Template, simply select \"Templates\" tile from the top of your Ansible Tower dashboard. This will bring you to the Templates page where any current templates you have created will be shown. To create a new one, click the +Add drop down box and select Job Templates. Here, everything that you can add to your Job Templates can be found here. The portions that are denoted with a * are a required option, things such as credentials, which project and playbook you want to use are all needed before you can save the Template.\n![creating job template](../images/job_template.png)\n\n### 5\n\nLaunching a Job Template in Ansible Tower is easy. If you are starting from your Ansible Tower dashboard, just select Templates and the templates that you have created will be there (including the one that you just created for the previous example) Once you are at the templates page, next to each template, there is a rocket. Once you click that, Ansible Tower will start to execute that job. Launching a task in ansible tower is that simple.\n\n![launch job template](../images/running_job.png)\n\n### 5.a\n\nSeeing a successful job means that the changes that were outlined were completed. On the details panel, everything that you need to know from the date to who launched the job to what inventory the template was run against can be found there. You can also search through previous job runs and see this exact information for each time this job was run.\n![successful job completion](../images/succes_job.png)\n\n### 6\n\nExtra Variables are variables that can be passed in many parts of Ansible Tower. From Inventory configuration, Job Templates and Surveys, extra variables can be tricky if they are not kept track of. For example, say that you have a defined variable for an inventory for debug = true. It is entirely possible that this variable, debug = true, can be overridden in a job template survey.\n\nTo ensure that the variables you need to pass are not overridden, ensure they are included by redefining them in the survey. Keep in mind that extra variables can be defined at the inventory, group, and host levels.\n![adding extra vars](../images/extra_variables.png)\n\nNOTE: These screenshots where taken using Ansible Tower 3.1.1.\n\n* Settings Icon and Credentials Card\n* Create Machine Credential\n* Create Group\n* Create Hosts (under Group)\n* Create Project\n* Create Template\n* Extra Variables\n\n## Extra Credit\n\n* Create Users\n* Assign Execution Permission to User in Job Templates\n* User Surveys\n\n### Create Users\n\nCreating users is simple and easy. To get other people on your team involved, select the settings gear in the top right and select users. This will take you to the users page. Select the green “+Add” button and a user creation page will be displayed. From here, you will need to enter some information about the user such as their name and email address. Once you have added the necessary information, click save and the user will be added to your Ansible Tower instance!\n![adding extra vars](../images/create_user.png)\n\n### Assign Execution Permission\n\nAssigning permissions to users on what they can and cannot do, what they can and cannot see is crucial to the Ansible Tower story. Role Based Access Control (RBAC) is at the core of how Tower scales organizations while increasing the security of automating. By delegating access to resources within tower, you are removing an element of human error.\n\nAssigning Execution permissions is a big step in implementing RBAC throughout your team. To add execution permissions for users on a Job Template, navigate to the job template that you would like the user(s) to have execution permission on. Once you are on the editing page for this job template (pencil) select the \"Permissions\" box at the top then click the \"+Add\" box on the right hand side.\n\nThis will display a box that displays users and teams within your Organization. Select the Users box (Tower does by default) then select the user you wish to add to the Job Template. This will prompt a drop down selection to appear for the role. From that drop-down box, select \"Execute\" and hit Save.\n\nIt is done! That user know has execution privileges on that Job Template!\n![adding extra vars](../images/add_perms.png)\n\n### User Surveys\n\nJob types of Run or Check will provide a way to set up surveys in the Job Template creation or editing screens. Surveys set extra variables for the playbook similar to ‘Prompt for Extra Variables’ does, but in a user-friendly question and answer way. Surveys also allow for validation of user input. (Don't forget our earlier discussion about Extra variables, survey answers are passed as Extra Variables so make sure you don't over ride variables with Survey Questions)\n\nUse cases for surveys are numerous. An example might be if operations wanted to give developers a “push to stage” button they could run without advanced Ansible knowledge. When launched, this task could prompt for answers to questions such as, “What tag should we release?” Many types of questions can be asked, including multiple-choice questions.\n\nTo create a survey, navigate to the Job Template you wish to add the survey to and select the blue \"Add Survey\" button from the top. From here, a blank survey form will appear for you to fill out.\n![adding extra vars](../images/blank_survey.png)\nFrom here you will fill out the prompt, the Answer Variable name and the type of answer that will be provided. Examples can include Integers, Text, and passwords.\n\nOnce you have everything filled out to your liking, click the Add button and the survey will now be prompt each user that attempts to execute the Job Template.\n![adding extra vars](../images/filled_out_survey.png)\n"
  },
  {
    "path": "facilitator/solutions/tower_install.md",
    "content": "# Workshop: Installing Tower\n\nHere students are tasked with installing and activating a new single integrated instance of Ansible Tower. The assignment will show students how they can run an instance of Tower themselves for testing, demos and their own proof-of-concepts.\n\n## Tips\n\nIf you are working in an environment that does not have access repos such as EPEL, you can opt to use the [Ansible Tower bundle installer](http://releases.ansible.com/ansible-tower/setup-bundle/ansible-tower-setup-bundle-latest.el7.tar.gz) instead. This bundle includes all of the RPMs from these repositories. It does not include **all** of the RPMs required.\n\nOnce downloaded, configured and the `setup.sh` is started, is will take a few minutes to install all of the Ansible Tower dependencies. Students can take a short break while that runs.\n\n## Solution\n\n1. Retrieve the latest version of Tower from the Ansible Tower releases archive.\n1. Unarchive the retrieved Ansible Tower archive.\n    * i.e. `tar -vxzf towerlatest`\n1. Change directories to the one created by the expanded Tower software archive.\n1. Edit the `inventory` file with passwords for admin, message queue and the postgres database will use while setting up. These are represented by these inventory variables:\n    * `admin_password`\n    * `rabbitmq_password`\n    * `pg_password`\n1. Run the setup.sh script with sudo.\n1. Verify Ansible has been successfully installed and started, load the Ansible Tower UI in a web browser using the control machine IP address.\n1. Sign in as the admin user with the password created in the installation process. You will be prompted for a license.\n1. Request a trial license if one hasn't been provided already. (Either trial license type will suffice.)\n1. Import/upload the license file.\n1. Extra Credit: Open the ping API end point in a web browser -- the URL will be something like like [https://tower.host.ip/v1/api/ping](https://tower.host.ip/v1/api/ping)\n\nThese are the approximate BASH commands to do this:\n\n```bash\n$ wget https://bit.ly/towerlatest\n$ tar -xzvf towerlatest\n$ cd ansible-tower-setup-x.x.x\n$ vi inventory\n# (make edits to inventory variables like in step 4)\n$ sudo ./setup.sh\n```\n\nNOTE: If you don't have access to EPEL you can use [https://bit.ly/towerbundlelatest](https://bit.ly/towerbundlelatest).\n\nOnce completed switch to your browser to sign into the Ansible Tower UI as admin and setup the instance with a license file.\n"
  },
  {
    "path": "guides/README.md",
    "content": "# Ansible Lightbulb Guides\n\nThese guides are part of lightbulb, a multi-purpose toolkit for effectively demonstrating Ansible's capabilities or providing informal trainings in various forms -- instructor-led, hands-on or self-paced.\n\n## Available Guides\n\nCurrently there are two guides available:\n\n* [Ansible Engine](ansible_engine/README.md)\n* [Ansible Tower](ansible_tower/README.md)\n\n## Additional information\n\nAdditional information about Ansible, how to get started and where to proceed after these guides, have a look at the following sources:\n\n* [Ansible Getting Started](http://docs.ansible.com/ansible/latest/intro_getting_started.html)\n"
  },
  {
    "path": "guides/ansible_engine/1-adhoc/README.md",
    "content": "# Exercise - Running Ad-hoc commands\n\nFor our first exercise, we are going to run some ad-hoc commands to help you get a feel for how Ansible works.  Ansible Ad-Hoc commands enable you to perform tasks on remote nodes without having to write a playbook.  They are very useful when you simply need to do one or two things quickly and often, to many remote nodes.\n\n## Section 1: Definition of the inventory\n\nInventories are crucial to Ansible as they define remote machines on which you wish to run commands or your playbook(s). In this lab the inventory is provided by your instructor. The inventory is an ini formatted file listing your hosts, sorted in groups, additionally providing some variables. It looks like:\n\n```bash\n[all:vars]\nansible_user=rwolters\nansible_ssh_pass=averysecretpassword\nansible_port=22\n\n[web]\nnode-ansible_host=.22.33.44\nnode-2 ansible_host=22.33.44.55\nnode-3 ansible_host=33.44.55.66\n\n[control]\nansible ansible_host=44.55.66.77\n```\n\nYour individual inventory is already present in your lab environment underneath `lightbulb/lessons/lab_inventory/$USERNAME-instances.txt`, where `$USERNAME` is replaced by your individual username.\n\n## Section 2: Basic Ansible configuration\n\nAnsible needs to know which inventory file should be used. By default it checks for the inventory file at `/etc/ansible/hosts`. You need to point Ansible to your own, lab specific inventory file. One way to do this is to specify the path to the inventory file with the `-i` option to the ansible command:\n\n```bash\nansible -i hosts ...\n```\n\nTo avoid the need to specify the inventory file each time, the inventory can also be configured in the configuration file `.ansible.cfg`. If this file is present, Ansible will read configuration parameters like the inventory location and others from this file.\n\nOn your control node, such a file is already present right in the home directory of your user. It is already customized to point to your inventory file, so there is nothing further you have to do.\n\n## Section 3: Ping a host\n\nLet's start with something basic and use the `ping` module to test that Ansible has been installed and configured correctly and all hosts in your inventory are responsive.\n\n```bash\nansible all -m ping\n```\n\n## Section 4: Run a typical command\n\nNow let's see how we can run a typical Linux shell command and format the output using the `command` module.\n\n```bash\nansible web -m command -a \"uptime\" -o\n```\n\n## Section 5: Gather facts about target node\n\nWe can use an ad-hoc command with the `setup` module to display the many facts Ansible can discover about each node in its inventory.\n\n```bash\nansible all -m setup\n```\n\n## Section 6: Install package\n\nNow, let's install Apache using the `yum` module.\n\n```bash\nansible web -m yum -a \"name=httpd state=present\" -b\n```\n\nUsually, only root users are allowed to install packages. So this is a case where we need privilege escalation and a sudo that has to be setup properly. We need to instruct ansible to use sudo to run the command as root by using the parameter `-b` (think \"become\").\n\n## Section 7: Start service\n\nOK, Apache is installed now so let's start it up using the `service` module.\n\n```bash\nansible web -m service -a \"name=httpd state=started\" -b\n```\n\n## Section 8: Stop service\n\nFinally, let's clean up after ourselves.  First, stop the httpd service.\n\n```bash\nansible web -m service -a \"name=httpd state=stopped\" -b\n```\n\n## Section 9: Remove package\n\nNext, remove the Apache package.\n\n```bash\nansible web -m yum -a \"name=httpd state=absent\" -b\n```\n\n---\n**NOTE:** Additional `ansible` options\n\nThe way `ansible` works can be influenced by various options which can be listed via `ansible --help`:\n\n```bash\nansible --help\nUsage: ansible <host-pattern> [options]\n\nDefine and run a single task 'playbook' against a set of hosts\n\nOptions:\n  -a MODULE_ARGS, --args=MODULE_ARGS\n                        module arguments\n  --ask-vault-pass      ask for vault password\n  -B SECONDS, --background=SECONDS\n                        run asynchronously, failing after X seconds\n                        (default=N/A)\n  -C, --check           don't make any changes; instead, try to predict some\n                        of the changes that may occur\n  -D, --diff            when changing (small) files and templates, show the\n                        differences in those files; works great with --check\n[...]\n```\n\n---\n\n[Click Here to return to the Ansible Lightbulb - Ansible Engine Guide](../README.md)\n"
  },
  {
    "path": "guides/ansible_engine/2-playbook/README.md",
    "content": "# Exercise 2 - Writing Your First playbook\n\nNow that you've gotten a sense of how ansible works, we are going to write our first ansible *playbook*. Playbooks are Ansible’s configuration, deployment, and orchestration language. They are written in YAML and are used to invoke Ansible modules to perform tasks that are executed sequentially i.e top to bottom. They can describe a policy you want your remote systems to enforce, or a set of steps in a general IT workflow. Playbooks are like an instruction manual and describe the state of environment.\n\nA playbook can have multiple plays and a play can have one or multiple tasks.  The goal of a *play* is to map a group of hosts.  The goal of a *task* is to implement modules against those hosts.\n\nFor our first playbook, we are only going to write one play and two tasks.\n\n## Section 1: Creating a Directory Structure and Files for your Playbook\n\nThere is a [best practices](http://docs.ansible.com/ansible/playbooks_best_practices.html) on the preferred directory structures for playbooks.  We strongly encourage you to read and understand these practices as you develop your Ansible skills.\n\nInstead, we are going to create a very simple directory structure for our playbook, and add just a couple of files to it.\n\n### Step 1: Create a directory called `apache_basic` in your home directory and change directories into it\n\n```bash\nmkdir ~/apache-simple-playbook\ncd ~/apache-simple-playbook\n```\n\n### Step 2: Use `vi` or `vim` to open a file called `site.yml`\n\n## Section 2: Defining Your Play\n\nNow that you are editing `site.yml`, let's begin by defining the play and then understanding what each line accomplishes\n\n```yml\n---\n- name: Ensure apache is installed and started\n  hosts: web\n  become: yes\n```\n\n* `---` Defines the beginning of YAML\n* `name: install and start apache` This describes our play\n* `hosts: web` Defines the host group in your inventory on which this play will run against\n* `become: yes` Enables user privilege escalation.  The default is sudo, but su, pbrun, and [several others](http://docs.ansible.com/ansible/become.html) are also supported.\n\n## Section 3: Adding Tasks to Your Play\n\nNow that we've defined your play, let's add some tasks to get some things done.  Align (vertically) the *t* in `task` with the *b* `become`. Yes, it does actually matter.  In fact, you should make sure all of your playbook statements are aligned in the way shown here. If you want to see the entire playbook for reference, skip to the bottom of this exercise.\n\n```yml\n  tasks:\n  - name: Ensure httpd package is present\n    yum:\n      name: httpd\n      state: present\n\n  - name: Ensure latest index.html file is present\n    copy:\n      src: files/index.html\n      dest: /var/www/html/\n\n  - name: Ensure httpd is started\n    service:\n      name: httpd\n      state: started\n```\n\n* `tasks:` This denotes that one or more tasks are about to be defined\n* `- name:` Each task requires a name which will print to standard output when you run your playbook. It's considered best practice to give all your plays and tasks concise and human-meaningful descriptions.\n\n```yml\n- name: Ensure httpd package is present\n  yum:\n    name: httpd\n    state: present\n```\n\n* These four lines are calling the Ansible module *yum* to install httpd. [Click here](http://docs.ansible.com/ansible/yum_module.html) to see all options for the yum module.\n\n```yml\n- name: Ensure latest index.html file is present\n  copy:\n    src: files/index.html\n    dest: /var/www/html/\n```\n\n* These four lines ensure that the `index.html` file is copied over to the target node. [Click here](http://docs.ansible.com/ansible/copy_module.html) to see all options for the copy module.\n\n```yml\n- name: Ensure httpd is started\n  service:\n    name: httpd\n    state: started\n```\n\n* The next few lines are using the ansible module `service` to start the httpd service right now. The service module is the preferred way of controlling services on remote hosts. [Click here](http://docs.ansible.com/ansible/service_module.html) to learn more about the `service` module.\n\n## Section 4: Saving your Playbook\n\nNow that you've completed writing your playbook, it would be a shame not to keep it.\n\nUse the `write/quit` method in `vi` or `vim` to save your playbook, i.e. `Esc :wq!`\n\nAlso, you have to create the above mentioned `index.html` file. For this, first create the proper directory:\n\n```bash\nmkdir ~/apache-simple-playbook/files\ncd ~/apache-simple-playbook/files\n```\n\nNext, create the file and add content: Use `vi` or `vim` to open a file called `index.html` and add the following content:\n\n```html\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>Ansible: Automation for Everyone</title>\n  <link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>\n  <style>\nbody {\n    font-family: 'Open Sans', sans-serif;\n    text-align: center;\n}\n.container {\n    position: absolute;\n    top: 50%;\n    left: 50%;\n    -moz-transform: translateX(-50%) translateY(-50%);\n    -webkit-transform: translateX(-50%) translateY(-50%);\n    transform: translateX(-50%) translateY(-50%);\n    font-size: 200%;\n}\nfooter {\n    width: 100%;\n    bottom: 0;\n    position: fixed;\n    font-size: 75%;\n}\nimg {\n    margin: 0 auto;\n}\n  </style>\n</head>\n<body>\n<div class=\"container\">\n    <img src=\"https://www.ansible.com/hubfs/2017_Images/BrandPage/Brand-Assets/Ansible_RH_AnsibleAutomation_RGB_RedBlack.png\" width=\"75%\"/>\n</div>\n<footer>Red Hat Ansible</footer>\n</body>\n</html>\n```\n\nUse the `write/quit` method in `vi` or `vim` to save your playbook, i.e. `Esc :wq!`\n\nAnd that should do it.  You should now have a fully written playbook called `site.yml`. You are ready to automate!\n\n---\n**NOTE**\nAnsible playbooks are essential YAML and YAML is a bit particular about formatting especially regards to whitespace. If you are unfamiliar with this, we recommend reading up on [YAML Syntax](http://docs.ansible.com/ansible/YAMLSyntax.html) in the Ansible docs.\n\nIn the meantime, your completed playbook should look like this example below. (Take note of the spacing and alignment.)\n\n---\n\n```yml\n---\n- name: Ensure apache is installed and started\n  hosts: web\n  become: yes\n\n  tasks:\n  - name: Ensure httpd package is present\n    yum:\n      name: httpd\n      state: present\n\n  - name: Ensure latest index.html file is present\n    copy:\n      src: files/index.html\n      dest: /var/www/html/\n\n  - name: Ensure httpd is started\n    service:\n      name: httpd\n      state: started\n```\n\n---\n\n[Click Here to return to the Ansible Lightbulb - Ansible Engine Guide](../README.md)\n"
  },
  {
    "path": "guides/ansible_engine/3-variables/README.md",
    "content": "# Exercise 3 - Using Variables, Loops, and Handlers\n\nPrevious exercises showed you the basics of Ansible Engine.  In the next few exercises, we are going to teach some more advanced ansible skills that will add flexibility and power to your playbooks.\n\nAnsible exists to make tasks simple and repeatable.  We also know that not all systems are exactly alike and often require some slight change to the way an Ansible playbook is run.  Enter variables.\n\nVariables are how we deal with differences between your systems, allowing you to account for a change in port, IP address or directory.\n\nLoops enable us to repeat the same task over and over again.  For example, lets say you want to install 10 packages. By using an ansible loop, you can do that in a single task.\n\nHandlers are the way in which we restart services.  Did you just deploy a new config file, install a new package? If so, you may need to restart a service for those changes to take effect.  We do that with a handler.\n\nFor a full understanding of variables, loops, and handlers; check out our Ansible documentation on these subjects.\n\n* [Ansible Variables](http://docs.ansible.com/ansible/latest/playbooks_variables.html)\n* [Ansible Loops](http://docs.ansible.com/ansible/latest/playbooks_loops.html)\n* [Ansible Handlers](http://docs.ansible.com/ansible/latest/playbooks_intro.html#handlers-running-operations-on-change)\n\n## Section 1: Running the Playbook\n\nTo begin, we are going to create a new playbook, but it should look very familiar to the one you created in exercise [1.2](../2-playbook/README.md)\n\nWe are now going to run you're brand new playbook on your two web nodes.  To do this, you are going to use the `ansible-playbook` command.\n\n### Step 1\n\nNavigate to your home directory create a new project and playbook.\n\n```bash\ncd\nmkdir apache-basic-playbook\ncd apache-basic-playbook\nvim site.yml\n```\n\n### Step 2\n\nAdd a play definition and some variables to your playbook.  These include addtional packages your playbook will install on your web servers, plus some web server specific configurations.\n\n```yml\n---\n- name: Ensure apache is installed and started\n  hosts: web\n  become: yes\n  vars:\n    httpd_packages:\n      - httpd\n      - mod_wsgi\n    apache_test_message: This is a test message\n    apache_max_keep_alive_requests: 115\n    apache_webserver_port: 80\n```\n\n### Step 3\n\nAdd a new task called *httpd packages are present*:\n\n```yml\n{% raw %}\n  tasks:\n    - name: Ensure httpd packages are present\n      yum:\n        name: \"{{ item }}\"\n        state: present\n      with_items: \"{{ httpd_packages }}\"\n      notify: restart-apache-service\n{% endraw %}\n```\n\n---\n**NOTE**\n> Let's see what is happening here.\n\n* `vars:` You've told Ansible the next thing it sees will be a variable name\n* `httpd_packages` You are defining a list-type variable called `httpd_packages**.  What follows is a list of those packages\n* {% raw %}`{{ item }}`{% endraw %} You are telling Ansible that this will expand into a list item like `httpd` and `mod_wsgi`.\n* {% raw %}`with_items: \"{{ httpd_packages }}`{% endraw %} This is your loop which is instructing Ansible to perform this task on every `item` in `httpd_packages`\n* `notify: restart-apache-service` This statement is a `handler`, so we'll come back to it in Section 3.\n\n---\n\n## Section 2: Deploying Files and Starting a Service\n\nWhen you need to do something with files or directories on a system, use one of the [Ansible Files](http://docs.ansible.com/ansible/latest/list_of_files_modules.html) modules.  In this case, we'll leverage the `file` and `template` modules.\n\n### Step 1\n\nCreate a `templates` directory in your project directory and download two files.\n\n```bash\nmkdir templates\ncd templates\ncurl -O https://raw.githubusercontent.com/ansible/lightbulb/master/examples/apache-basic-playbook/templates/httpd.conf.j2\ncurl -O https://raw.githubusercontent.com/ansible/lightbulb/master/examples/apache-basic-playbook/templates/index.html.j2\n```\n\n### Step 2\n\nAdd some file tasks and a service task to your playbook.\n\n```yml\n- name: Ensure site-enabled directory is present\n  file:\n    name: /etc/httpd/conf/sites-enabled\n    state: directory\n\n- name: Ensure latest httpd.conf is present\n  template:\n    src: templates/httpd.conf.j2\n    dest: /etc/httpd/conf/httpd.conf\n  notify: restart-apache-service\n\n- name: Ensure latest index.html is present\n  template:\n    src: templates/index.html.j2\n    dest: /var/www/html/index.html\n\n- name: Ensure httpd is started and enabled\n  service:\n    name: httpd\n    state: started\n    enabled: yes\n```\n\n---\n**NOTE:** Recap of used modules\n\n> What did we just write?\n\n* `file:` This module is used to create, modify, delete files, directories, and symlinks.\n* `template:` This module specifies that a [jinja2](http://docs.ansible.com/ansible/latest/playbooks_templating.html) template is being used and deployed. `template` is part of the `Files`\n  module family and we encourage you to check out all of the other [file-management modules here](http://docs.ansible.com/ansible/latest/list_of_files_modules.html).\n* `service` - The Service module starts/stops/restarts services.\n\n---\n\n## Section 3: Defining and Using Handlers\n\nThere are any number of reasons we often need to restart a service/process including the deployment of a configuration file, installing a new package, etc.  There are really two parts to this Section; adding a handler to the playbook and calling the handler after the a task.  We will start with the former.\n\n### Step 1\n\nDefine a handler.\n\n```yml\nhandlers:\n  - name: restart-apache-service\n    service:\n      name: httpd\n      state: restarted\n```\n\n---\n**NOTE:** Handlers\n\n> A handler is only executed when it is triggered by a task.\n\n* `handlers:` This is telling the *play* that the `tasks:` are over, and now we are defining `handlers:`. Everything below that looks the same as any other task, i.e. you give it a name, a module, and the options for that module.  This is the definition of a handler.\n* `notify: restart-apache-service` This triggers the handler as mentioned above. The `nofify` statement is the invocation of a handler by name. In the above created playbook, a `notify` statement was already added to the `latest httpd.conf is present` task.\n* naming of handlers: We do recommend to name handlers in a consistent way. A tested and proven method is to name all handlers in lower case and with dashes instead of spaces.\n\n---\n\n## Section 4: Review\n\nYour new, improved playbook is done! Let's take a second look to make sure everything looks the way you intended.  If not, now is the time for us to fix it up. The figure below shows line counts and spacing.\n\n```yml\n{% raw %}\n---\n- name: Ensure apache is installed and started\n  hosts: web\n  become: yes\n  vars:\n    httpd_packages:\n      - httpd\n      - mod_wsgi\n    apache_test_message: This is a test message\n    apache_max_keep_alive_requests: 115\n    apache_webserver_port: 80\n\n  tasks:\n    - name: Ensure httpd packages are present\n      yum:\n        name: \"{{ item }}\"\n        state: present\n      with_items: \"{{ httpd_packages }}\"\n      notify: restart-apache-service\n\n    - name: Ensure site-enabled directory is present\n      file:\n        name: /etc/httpd/conf/sites-enabled\n        state: directory\n\n    - name: Ensure latest httpd.conf is present\n      template:\n        src: templates/httpd.conf.j2\n        dest: /etc/httpd/conf/httpd.conf\n      notify: restart-apache-service\n\n    - name: Ensure latest index.html is present\n      template:\n        src: templates/index.html.j2\n        dest: /var/www/html/index.html\n\n    - name: Ensure httpd is started and enabled\n      service:\n        name: httpd\n        state: started\n        enabled: yes\n\n  handlers:\n    - name: restart-apache-service\n      service:\n        name: httpd\n        state: restarted\n{% endraw %}\n```\n\n## Section 5: Running your new apache playbook\n\nCongratulations!  You just wrote a playbook that incorporates some key Ansible concepts that you use in most if not all of your future playbooks.  Before you get too excited though, we should probably make sure it actually runs.\n\nSo, lets do that now.\n\n### Step 1\n\nMake sure you are in the right directory and create a host file.\n\n```bash\ncd ~/apache-basic-playbook\n```\n\n### Step 2\n\nRun your playbook\n\n```bash\nansible-playbook site.yml\n```\n\n## Section 2: Review\n\nIf successful, you should see standard output that looks very similar to the following.  If not, just let us know.  We'll help get things fixed up.\n\n![Stdout](stdout_2.png)\n\n---\n\n[Click Here to return to the Ansible Lightbulb - Ansible Engine Guide](../README.md)\n"
  },
  {
    "path": "guides/ansible_engine/4-role/README.md",
    "content": "# Exercise 1.4 - Roles: Making your playbooks reusable\n\nWhile it is possible to write a Playbook in one very large file, eventually you’ll want to reuse files and start to organize things. At a basic level, including task files allows you to break up bits of configuration policy into smaller files. Task includes pull in tasks from other files. Since handlers are tasks too, you can also include handler files.\n\nWhen you start to think about it – tasks, handlers, variables, and so on – begin to form larger concepts. You start to think about modeling what something is,\n\n* It’s no longer \"apply THIS to these hosts\"\n* You say \"these hosts are webservers\".\n\nRoles build on the idea of include files and provide Ansible with a way to load tasks, handlers, and variables from external files. The files that define a role have specific names and are organized in a rigid directory structure.\n\nUse of Ansible roles has the following benefits:\n\n* Roles group content, allowing easy sharing of code with others\n* Roles can be written that define the essential elements of a system type: web server, database server...\n* Roles make larger projects more manageable\n* Roles can be developed in parallel by different administrators\n\nFor this exercise, you are going to take the playbook you just wrote and refactor it into a role. In addition, you'll learn to use `ansible-galaxy` to jumpstart development of a role.\n\nLet's begin with seeing how the new playbook structure `apache-role`, which we are going to create in this exercise,  will break down into a role.\n\n```bash\napache-role/\n├── roles\n│   └── apache-simple\n│       ├── defaults\n│       │   └── main.yml\n│       ├── files\n│       ├── handlers\n│       │   └── main.yml\n│       ├── meta\n│       │   └── main.yml\n│       ├── README.md\n│       ├── tasks\n│       │   └── main.yml\n│       ├── templates\n│       ├── tests\n│       │   ├── inventory\n│       │   └── test.yml\n│       └── vars\n│           └── main.yml\n├── inventory.ini\n└── site.yml\n```\n\nFortunately, you don't have to create all of these directories and files by hand.  That's where `ansible-galaxy` comes in.\n\n## Section 1: Using ansible-galaxy to initialize a new role\n\nAnsible Galaxy is a free site for finding, downloading, and sharing roles. Ansible includes the tool `ansible-galaxy` which can interact with the site and the roles hosted there.  It's also pretty handy for creating them locally which is what we are about to do here.\n\n### Step 1\n\nCreate a new directory, `apache-role`.\n\n```bash\ncd\nmkdir apache-role\ncd ~/apache-role\n```\n\n### Step 2\n\nCreate a directory called `roles` and `cd` into it.\n\n```bash\nmkdir roles\ncd roles\n```\n\n### Step 3\n\nUse the `ansible-galaxy` command to initialize a new role called `apache-simple`.\n\n```bash\nansible-galaxy init apache-simple\n```\n\nTake a look around the structure you just created.  It should look a lot like Figure 1 above.  However, we need to complete one more step before moving onto section 2.  It is Ansible best practice to clean out role directories and files you won't be using.  For this role, we won't be using anything from `files`, `tests`.\n\n### Step 4\n\nRemove the `files` and `tests` directories\n\n```bash\ncd ~/apache-role/roles/apache-simple/\nrm -rf files tests\n```\n\n## Section 2: Breaking Your `site.yml` Playbook into the Newly Created `apache-simple` Role\n\nIn this section, we will separate out the major parts of your playbook including `vars:`, `tasks:`, `template:`, and `handlers:`.\n\n### Step 1\n\nMake a copy of `site.yml` which was written in the last exercise to the current directory.\n\n```bash\ncd ~/apache-role\ncp ~/apache-basic-playbook/site.yml site.yml\nvim site.yml\n```\n\n### Step 2\n\nAdd the play definition and the invocation of a single role.\n\n```yml\n---\n- name: Ensure apache is installed and started via role\n  hosts: web\n  become: yes\n\n  roles:\n    - apache-simple\n```\n\n### Step 3\n\nAdd some default variables to your role in `roles/apache-simple/defaults/main.yml`.\n\n```yml\n---\n# defaults file for apache-simple\napache_test_message: This is a test message\napache_max_keep_alive_requests: 115\n```\n\n### Step 4\n\nAdd some role-specific variables to your role in `roles/apache-simple/vars/main.yml`.\n\n```yml\n---\n# vars file for apache-simple\nhttpd_packages:\n  - httpd\n  - mod_wsgi\n```\n\n---\n**NOTE**\n####\nAs can be seen above, we added variables at another place than the previously directly in the playbook. Variables can be defined in a variety of places and have a clear [precedence](http://docs.ansible.com/ansible/latest/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable). Here are some examples where variables can be placed:\n\n* vars directory\n* defaults directory\n* group_vars directory\n* In the playbook under the `vars:` section\n* In any file which can be specified on the command line using the `--extra_vars` option\n\nIn this exercise, we are using role defaults to define a couple of variables and these are the most malleable.  After that, we defined some variables in `/vars` which have a higher precedence than defaults and can't be overridden as a default variable.\n\n---\n\n### Step 5\n\nCreate your role handler in `roles/apache-simple/handlers/main.yml`.\n\n```yml\n---\n# handlers file for apache-simple\n- name: restart-apache-service\n  service:\n    name: httpd\n    state: restarted\n    enabled: yes\n```\n\n### Step 6\n\nAdd tasks to your role in `roles/apache-simple/tasks/main.yml`.\n\n```yml\n{% raw %}\n---\n# tasks file for apache-simple\n- name: Ensure httpd packages are installed\n  yum:\n    name: \"{{ item }}\"\n    state: present\n  with_items: \"{{ httpd_packages }}\"\n  notify: restart-apache-service\n\n- name: Ensure site-enabled directory is created\n  file:\n    name: /etc/httpd/conf/sites-enabled\n    state: directory\n\n- name: Copy httpd.conf\n  template:\n    src: templates/httpd.conf.j2\n    dest: /etc/httpd/conf/httpd.conf\n  notify: restart-apache-service\n\n- name: Copy index.html\n  template:\n    src: templates/index.html.j2\n    dest: /var/www/html/index.html\n\n- name: Ensure httpd is started\n  service:\n    name: httpd\n    state: started\n    enabled: yes\n{% endraw %}\n```\n\n### Step 7\n\nDownload a couple of templates into `roles/apache-simple/templates/`.  And right after that, let's clean up from exercise 2.1 by removing the old templates directory.\n\n```bash\nmkdir -p ~/apache-role/roles/apache-simple/templates/\ncd ~/apache-role/roles/apache-simple/templates/\ncurl -O https://raw.githubusercontent.com/ansible/lightbulb/master/examples/apache-role/roles/apache-simple/templates/httpd.conf.j2\ncurl -O https://raw.githubusercontent.com/ansible/lightbulb/master/examples/apache-role/roles/apache-simple/templates/index.html.j2\n```\n\n## Section 3: Running your new role-based playbook\n\nNow that you've successfully separated your original playbook into a role, let's run it and see how it works.\n\n### Step 1\n\nRun the playbook.\n\n```bash\nansible-playbook site.yml\n```\n\nIf successful, your standard output should look similar to the figure below.\n\n![Role based stdout](stdout_3.png)\n\n## Section 4: Review\n\nYou should now have a completed playbook, `site.yml` with a single role called `apache-simple`.  The advantage of structuring your playbook into roles is that you can now add new roles to the playbook using Ansible Galaxy or simply writing your own.  In addition, roles simplify changes to variables, tasks, templates, etc.\n\n---\n\n[Click Here to return to the Ansible Lightbulb - Ansible Engine Guide](../README.md)\n"
  },
  {
    "path": "guides/ansible_engine/README.md",
    "content": "# Ansible Lightbulb - Ansible Engine Guide\n\nThis guide is part of lightbulb, a multi-purpose toolkit for effectively demonstrating Ansible's capabilities or providing informal trainings in various forms -- instructor-led, hands-on or self-paced.\n\nThe focus of this guide is on the set up and usage of Ansible Engine.\n\n## Ansible Engine Exercises\n\nThere are multiple exercises available. It is strongly recommended to run them one after the other. They are self-explaining and can be run at self pace.\n\n* [Exercise 1 - Running Ad-hoc commands](1-adhoc)\n* [Exercise 2 - Writing Your First playbook](2-playbook)\n* [Exercise 3 - Using Variables, Loops, and Handlers](3-variables)\n* [Exercise 4 - Roles: Making your playbooks reusable](4-role)\n\n## Additional information\n\nAdditional information about Ansible, how to get started and where to proceed after this guide, have a look at the following sources:\n\n* [Ansible Getting Started](http://docs.ansible.com/ansible/latest/intro_getting_started.html)\n\n---\n![Ansible Red Hat Engine](ansible-engine-small.png)\n\nIn addition to open source Ansible, there is Red Hat® Ansible® Engine which includes support and an SLA for the networking modules shown in these exercises\n\nRed Hat® Ansible® Engine is a fully supported product built on the simple, powerful and agentless foundation capabilities derived from the Ansible project.  Please visit [ansible.com](https://www.ansible.com/ansible-engine) for more information.\n"
  },
  {
    "path": "guides/ansible_tower/1-install/README.md",
    "content": "# Exercise 1 - Installing Ansible Tower\n\nIn this exercise, we are going to get Ansible Tower installed on your control node\n\n## Installing Ansible Tower\n\n### Step 1\n\nOn your control node, change directories to /tmp\n\n```bash\ncd /tmp\n```\n\n### Step 2\n\nDownload the latest Ansible Tower package\n\n```bash\ncurl -O http://releases.ansible.com/ansible-tower/setup/ansible-tower-setup-latest.tar.gz\n```\n\n### Step 3\n\nUntar and unzip the package file\n\n```bash\ntar xvfz /tmp/ansible-tower-setup-latest.tar.gz\n```\n\n### Step 4\n\nChange directories into the ansible tower package\n\n```bash\ncd /tmp/ansible-tower-setup-*/\n```\n\n### Step 5\n\nUsing an editor of your choice, open the inventory file\n\n```bash\nvi inventory\n```\n\n### Step 6\n\nFill a few variables out in an inventory file: `admin_password, pg_password, rabbitmq_password`\n\n```ini\n[tower]\nlocalhost ansible_connection=local\n\n[database]\n\n[all:vars]\nadmin_password=*'ansibleWS'*\n\npg_host=''\npg_port=''\n\npg_database='awx'\npg_username='awx'\npg_password=*'ansibleWS'*\n\nrabbitmq_port=5672\nrabbitmq_vhost=tower\nrabbitmq_username=tower\nrabbitmq_password=*'ansibleWS'*\nrabbitmq_cookie=cookiemonster\n\n= Needs to be true for fqdns and ip addresses\nrabbitmq_use_long_name=false\n```\n\n### Step 7\n\nRun the Ansible Tower setup script\n\n```bash\nsudo ./setup.sh\n```\n\n---\n**NOTE:** Installation will take some time\nStep 7 will take approx. 10-15 minutes to complete.  This may be a good time to take a break.\n\n---\n\n### End Result\n\nAt this point, your Ansible Tower installation should be complete. You can access your Tower at the IP of the control node:\n\n```bash\nhttp://<IP_of_control_node>\n```\n\n### Ensuring Installation Success\n\nYou know you were successful if you are able to browse to your Ansible Tower's url (_control node's IP address_) and get something like this\n\n![Ansible Tower Login Screen](ansible-lab-figure01-logon-screen.png)\n\n---\n\n[Click Here to return to the Ansible Lightbulb - Ansible Tower Guide](../README.md)\n"
  },
  {
    "path": "guides/ansible_tower/2-config/README.md",
    "content": "# Exercise 2 - Configuring Ansible Tower\n\nIn this exercise, we are going to configure Tower so that we can run a playbook.\n\n## The Tower UI\n\nThere are a number of concepts in the Ansible Tower UI that enable multi-tenancy, notifications, scheduling, etc. However, we are only going to focus on a few of the key contructs that are required for this workshop today.\n\n* Credentials\n* Projects\n* Inventory\n* Job Template\n\n## Logging into Tower and Installing the License Key\n\n### Step 1\n\nTo log in, use the username `admin` and and the password `ansibleWS`.\n\n![Ansible Tower Login Screen](ansible-lab-figure01-logon-screen.png)\n\nAs soon as you login, you will prompted to request a license or browse for an existing license file\n\n![Uploading a License](at_lic_prompt.png)\n\n### Step 2\n\nIn a seperate browser tab, browse to [https://www.ansible.com/workshop-license](https://www.ansible.com/workshop-license) to request a workshop license. These are special workshop licenses which are valid for only 5 nodes for 5 days.\n\n### Step 3\n\nBack in the Tower UI, choose BROWSE ![Browse button](at_browse.png) and upload the license file you received via e-mail.\n\n### Step 4\n\nSelect \"_I agree to the End User License Agreement_\"\n\n### Step 5\n\nClick on SUBMIT ![Submit button](at_submit.png)\n\n## Creating a Credential\n\nCredentials are utilized by Tower for authentication when launching jobs against machines, synchronizing with inventory sources, and importing project content from a version control system.\n\nThere are many [types of credentials](http://docs.ansible.com/ansible-tower/latest/html/userguide/credentials.html#credential-types) including machine, network, and various cloud providers.  In this workshop, we are using a *machine* credential.\n\n### Step 1\n\nSelect the gear icon ![Gear button](at_gear.png)\n\n### Step 2\n\nSelect CREDENTIALS\n\n### Step 3\n\nClick on ADD ![Add button](at_add.png)\n\n### Step 4\n\nComplete the form using the following entries.\n\n|OPTION|VALUE\n|------|-----\n|NAME |Ansible Workshop Credential\n|DESCRIPTION|Machine credential for run job templates during workshop\n|ORGANIZATION|Default\n|TYPE|Machine\n|USERNAME|Your workshop username - Student(x)\n|PASSWORD|Your workshop password\n|PRIVILEGE ESCALATION|Sudo\n\n![Adding a Credential](at_cred_detail.png)\n\n### Step 5\n\nSelect SAVE ![Save button](at_save.png)\n\n## Creating a Project\n\nA Project is a logical collection of Ansible playbooks, represented in Tower. You can manage playbooks and playbook directories by either placing them manually under the Project Base Path on your Tower server, or by placing your playbooks into a source code management (SCM) system supported by Tower, including Git, Subversion, and Mercurial.\n\n### Step 1\n\nClick on PROJECTS\n\n### Step 2\n\nClick on ADD ![Add button](at_add.png).\n\n### Step 3\n\nComplete the form using the following entries\n\n|OPTION|VALUE\n|------|-----\n|NAME |Ansible Workshop Project\n|DESCRIPTION|Workshop playbooks\n|ORGANIZATION|Default\n|SCM TYPE|Git\n|SCM URL| [https://github.com/ansible/lightbulb](https://github.com/ansible/lightbulb)\n|SCM BRANCH|\n|SCM UPDATE OPTIONS|Mark _Clean_ and _Update on Launch_\n\n![Defining a Project](at_project_detail.png)\n\n### Step 4\n\nSelect SAVE ![Save button](at_save.png)\n\n## Creating a Inventory\n\nAn inventory is a collection of hosts against which jobs may be launched. Inventories are divided into groups and these groups contain the actual hosts. Groups may be sourced manually, by entering host names into Tower, or from one of Ansible Tower’s supported cloud providers.\n\nAn Inventory can also be imported into Tower using the `tower-manage` command and this is how we are going to add an inventory for this workshop.\n\n### Step 1\n\nClick on INVENTORIES\n\n### Step 2\n\nSelect ADD ![Add button](at_add.png)\n\n### Step 3\n\nComplete the form using the following entries\n\n|OPTION|VALUE\n|------|-----\n|NAME |Ansible Workshop Inventory\n|DESCRIPTION|Ansible Inventory\n|ORGANIZATION|Default\n\n![Create an Inventory](at_inv_create.png)\n\n### Step 4\n\nSelect SAVE ![Save button](at_save.png)\n\n### Step 5\n\nUsing ssh, login to your control node\n\n```bash\nssh <username>@<IP_Address_of_your_control_node>\n```\n\n### Step 6\n\nUse the `tower-manage` command to import an existing inventory.  (_Be sure to replace <username> with your actual username_)\n\n```\nsudo tower-manage inventory_import --source=/home/<username>/lightbulb/lessons/lab_inventory/<username>-instances.txt --inventory-name=\"Ansible Workshop Inventory\"\n```\n\nYou should see output similar to the following:\n\n![Importing an inventory with tower-manage](at_tm_stdout.png)\n\nFeel free to browse your inventory in Tower.  You should now notice that the inventory has been populated with Groups and that each of those groups contain hosts.\n\n![Inventory with Groups](at_inv_group.png)\n\n### End Result\n\nAt this point, we are doing with our basic configuration of Ansible Tower.  In exercise 2.2, we will be soley focused on creating and running a job template so you can see Tower in action.\n\n---\n\n[Click Here to return to the Ansible Lightbulb - Ansible Tower Guide](../README.md)\n"
  },
  {
    "path": "guides/ansible_tower/3-create/README.md",
    "content": "# Exercise 3 - Creating and Running a Job Template\n\nA job template is a definition and set of parameters for running an Ansible job. Job templates are useful to execute the same job many times.\n\n## Creating a Job Template\n\n### Step 1\n\nSelect TEMPLATES\n\n### Step 2\n\nClick on ADD ![Add button](at_add.png), and select JOB TEMPLATE\n\n### Step 3\n\nComplete the form using the following values\n\n|OPTION|VALUE\n|------|-----\n|NAME |Apache Basic Job Template\n|DESCRIPTION|Template for the apache-basic-playbook\n|JOB TYPE|Run\n|INVENTORY|Ansible Workshop Inventory\n|PROJECT|Ansible Workshop Project\n|PLAYBOOK|examples/apache-basic-playbook/site.yml\n|MACHINE CREDENTIAL|Ansible Workshop Credential\n|LIMIT|web\n|OPTIONS|Check _Enable Privilege Escalation_\n\n![Job Template Form](at_jt_detail.png)\n\n### Step 4\n\nClick SAVE ![Save button](at_save.png) and then select ADD SURVEY ![Add](at_addsurvey.png)\n\n### Step 5\n\nComplete the survey form with following values\n\n|OPTION|VALUE\n|------|-----\n|PROMPT|Please enter a test message for your new website\n|DESCRIPTION|Website test message prompt\n|ANSWER VARIABLE NAME|apache_test_message\n|ANSWER TYPE|Text\n|MINIMUM/MAXIMUM LENGTH| Use the defaults\n|DEFAULT ANSWER| Be creative, keep it clean, we're all professionals here\n\n![Survey Form](at_survey_detail.png)\n\n### Step 6\n\nSelect ADD ![Add button](at_add.png)\n\n### Step 7\n\nSelect SAVE ![Add button](at_save.png)\n\n### Step 8\n\nBack on the main Job Template page, select SAVE ![Add button](at_save.png) again.\n\n## Running a Job Template\n\nNow that you’ve sucessfully created your Job Template, you are ready to launch it. Once you do, you will be redirected to a job screen which is refreshing in realtime showing you the status of the job.\n\n### Step 1\n\nSelect TEMPLATES\n\n---\n**NOTE**: Alternative way to navigate to TEMPLATES:\n\nIf you haven't navigated away from the job templates creation page you can scroll down to see all existing job templates\n\n---\n\n### Step 2\n\nClick on the rocketship icon ![Launch button](at_launch_icon.png) for the *Apache Basic Job Template*\n\n### Step 3\n\nWhen prompted, enter your desired test message\n\n![Survey Prompt](at_survey_prompt.png)\n\n### Step 4\n\nSelect LAUNCH ![Survey launch button](at_survey_launch.png)\n\n### Step 5\n\nWait and watch the execution of the job.\n\nOne of the first things you will notice is the summary section.  This gives you details about your job such as who launched it, what playbook it's running, what the status is, i.e. pending, running, or complete. You’ll also notice the `apache_test_message` being passed in the field EXTRA VARIABLES in the text box on the left bottom side. To the right, you can view standard output; the same way you could if you were running `ansible-playbook` from the command line.\n\n![Job Summary](at_job_status.png)\n\nYou can also click on each task in the standard output to get more details.\n\n### Step 6\n\nOnce your job is sucessful, navigate to your new website:\n\n```bash\nhttp://<IP_of_node-1_or_node-2>\n```\n\nIf all went well, you should see something like this, but with your own custom message:\n\n![New Website with Personalized Test Message](at_web_tm.png)\n\n## End Result\n\nAt this point in the workshop, you've experienced the core functionality of Ansible Tower. To read more about advanced features of Tower, take a look at the resources page in this guide.\n\n---\n\n[Click Here to return to the Ansible Lightbulb - Ansible Tower Guide](../README.md)\n"
  },
  {
    "path": "guides/ansible_tower/README.md",
    "content": "# Ansible Lightbulb - Ansible Tower Guide\n\nThis guide is part of lightbulb, a multi-purpose toolkit for effectively demonstrating Ansible's capabilities or providing informal trainings in various forms -- instructor-led, hands-on or self-paced.\n\nThe focus of this guide is on the set up and usage of Ansible Tower.\n\n## Ansible Tower Exercises\n\nThere are multiple exercises available. It is strongly recommended to run them one after the other. They are self-explaining and can be run at self pace.\n\n* [Exercise 1 - Installing Ansible Tower](1-install)\n* [Exercise 2 - Configuring Ansible Tower](2-config)\n* [Exercise 3 - Creating and Running a Job Template](3-create)\n\n## Additional information\n\nAdditional information about Ansible, how to get started and where to proceed after this guide, have a look at the following sources:\n\n* [Ansible Tower User Guide](http://docs.ansible.com/ansible-tower/latest/html/userguide/index.html)\n* [Ansible Tower Administration Guide](http://docs.ansible.com/ansible-tower/latest/html/administration/index.html)\n\n---\n![Ansible Tower](ansible_tower_logo.png)\n\nRed Hat® Ansible® Tower is a fully supported product built on the simple, powerful and agentless foundation capabilities derived from the Ansible project.  Please visit [ansible.com](https://www.ansible.com/tower) for more information.\n"
  },
  {
    "path": "tools/aws_lab_setup/.gitignore",
    "content": "*instances.txt\ninstructor_inventory\n"
  },
  {
    "path": "tools/aws_lab_setup/README.md",
    "content": "# Ansible AWS training provisioner\n\nThis provisioner automatically sets up lab environments for Ansible training. It creates four nodes per student/user.\n\n* One control node from which Ansible will be executed from and where Ansible Tower can be installed\n* Three web nodes that coincide with the three nodes in Lightbulb's original design\n* And one node where `haproxy` is installed (via Lightbulb lesson)\n\n## AWS Setup\n\nThe setup of the environments is done via Ansible playbooks, the actual VMs are set up on AWS. The `provision_lab.yml` playbook creates instances, configures them for password authentication and creates an inventory file for each user with their IPs and credentials. An instructor inventory file is also created in the current directory which will let the instructor access the nodes of any student by simply targeting the username as a host group. The lab is created in `us-east-1` by default.  Currently the setup only works with `us-east-1`, `us-west-1`, `eu-west-1`, `ap-southeast-1`, `ap-southeast-2`, `ap-south-1` and `ap-northeast-1`.\n\n## Email Options\n\nThis provisioner by default will send email to participants/students containing information about their lab environment including IPs and credentials. This configuration requires that each participant register for the workshop using their full name and email address.   Alternatively, you can use generic accounts for workshops.  This method offers the advantage of enabling the facilitator to handle \"walk-ins\" and is a simpler method overall in terms of collecting participant information.\n\nSteps included in this guide will be tagged with __(email)__ to denote it as a step required if you want to use email and __(no email)__ for steps you should follow if you chose not to use email\n\n**WARNING** Emails are sent _every_ time the playbook is run. To prevent emails from being sent on subsequent runs of the playbook, add `email: no` to `extra_vars.yml`.\n\n## How To Set Up Lab Environments\n\n### One-Time Preparations\n\nTo set up lab environments for Ansible training, follow these steps.\n\n1. Create an Amazon AWS account.\n\n1. In AWS, create an ssh key pair called 'ansible': In the AWS management console, click on \"Services\" on top of the page. In the menu that opens, click on \"EC2\". In the EC2 dashboard, on the left side, underneath \"Network & Security\" pick \"Key Pairs\". Download the private key to your `.ssh` directory, e.g. to `.ssh/ansible.pem`. Alternatively, you can upload your own public key into AWS.\n\n      If using an AWS generated key add it to the ssh-agent:\n\n        ssh-add ~/.ssh/ansible.pem\n\n1. Create an Access Key ID and Secret Access Key. Save the ID and key for later. See [AWSHELP](aws-directions/AWSHELP.md) for a detailed howto.\n\n1. Install `boto` and `boto3`.\n\n        pip install boto boto3\n\n1. Create a `boto` configuration file containing your AWS access key ID and secret access key.\n\n      Use the quickstart directions provided here: [http://boto3.readthedocs.io/en/latest/guide/quickstart.html](http://boto3.readthedocs.io/en/latest/guide/quickstart.html)\n\n1. __(email)__ Create a free [Sendgrid](http://sendgrid.com) account if you don't have one. Optionally, create an API key to use with this the playbook.\n\n1. __(email)__ Install the `sendgrid` python library:\n\n    **Note:** The `sendgrid` module does not work with `sendgrid >= 3`. Please install the latest `2.x` version.\n\n        pip install sendgrid==2.2.1\n\n1. Install the `passlib` library\n\n        pip install passlib\n\n1. Clone the lightbulb repo:\n\n        git clone https://github.com/ansible/lightbulb.git\n        cd lightbulb/tools/aws_lab_setup\n\n### Steps To Provision the Lab Environments\n\n1. Update your current installation of the lightbulb repo\n\n1. Define the following variables, either in a file passed in using `-e @extra_vars.yml` or directly in a `vars` section in `aws_lab_setup\\infra-aws.yml`:\n\n      ```yaml\n      ec2_key_name: username                # SSH key in AWS to put in all the instances\n      ec2_region: us-west-1                 # region where the nodes will live\n      ec2_az: us-east-1a                    # the availability zone\n      ec2_name_prefix: TRAINING-LAB         # name prefix for all the VMs\n      admin_password: changeme123           # Set this to something better if you'd like. Defaults to 'LearnAnsible[two digit month][two digit year]', e.g., LearnAnsible0416\n      ## Optional Variables\n      email: no                             # Set this if you wish to disable email\n      sendgrid_user: username               # username for the Sendgrid module.  Not required if \"email: no\" is set\n      sendgrid_pass: 'passwordgoeshere'     # sendgrid accound password.  Not required if \"email: no\" is set\n      sendgrid_api_key: 'APIkey'            # Instead of username and password, you may use an API key. Don't define both. Not required if \"email: no\" is set\n      instructor_email: 'Ansible Instructor <helloworld@acme.com>'  # address you want the emails to arrive from. Not required if \"email: no\" is set\n      ```\n\n1. Create a `users.yml` by copying `sample-users.yml` and adding all your students:\n\n    __(email)__\n\n    ```yaml\n    users:\n      - name: Bod Barker\n        username: bbarker\n        email: bbarker@acme.com\n\n      - name: Jane Smith\n        username: jsmith\n        email: jsmith@acme.com\n    ```\n\n    __(no email)__\n\n    ```yaml\n    users:\n      - name: Student01\n        username: student01\n        email: instructor@acme.com\n\n      - name: Student02\n        username: student02\n        email: instructor@acme.com\n    ```\n\n    **(no email) NOTE:**  If using generic users, you can generate the corresponding\n`users.yml` file from the command line by creating a 'STUDENTS' variable\ncontaining the number of \"environments\" you want, and then populating the file.\nFor example:\n\n        STUDENTS=30;\n        echo \"users:\" > users.yml &&\n        for NUM in $(seq -f \"%02g\" 1 $STUDENTS); do\n          echo \"  - name: Student${NUM}\" >> users.yml\n          echo \"    username: student${NUM}\" >> users.yml\n          echo \"    email: instructor@acme.com\" >> users.yml\n          echo >> users.yml\n        done\n\n1. Run the playbook:\n\n        ansible-playbook provision_lab.yml -e @extra_vars.yml -e @users.yml\n\n1. Check on the EC2 console and you should see instances being created like:\n\n        TRAINING-LAB-<student_username>-node1|2|3|haproxy|tower|control\n\nIn your EC2 console you will also see that a VPC was automatically created with a corresponding subnet.\n\n__(email)__ If successful all your students will be emailed the details of their hosts including addresses and credentials, and an `instructor_inventory.txt` file will be created listing all the student machines.\n\n__(no email)__ If you disabled email in your `extra_vars.yml` file, you will need to upload the instructor's inventory to a public URL which you will hand out to participants.\n\n1. Use [github gist](https://gist.github.com/) to upload `lightbulb/tools/aws_lab_setup/instructors_inventory`.\n1. Use [http://goo.gl](http://goo.gl) to shorten the URL to make it more consumable\n\n### Teardown The Lab Environments\n\nThe `teardown_lab.yml` playbook deletes all the training instances as well as local inventory files.\n\nTo destroy all the EC2 instances after training is complete:\n\n1. Run the playbook:\n\n        ansible-playbook teardown_lab.yml -e @extra_vars.yml -e @users.yml\n\n## Accessing student documentation and slides\n\n* A student guide and instructor slides are already hosted at [http://ansible-workshop.redhatgov.io](http://ansible-workshop.redhatgov.io) . (NOTE:  This guide is evolving and newer workshops can be previewed at [http://ansible.redhatgov.io](http://ansible.redhatgov.io) . This new version is currently being integrated with the Lightbulb project)\n* Here you will find student instructions broken down into exercises as well as the presentation decks under the __Additional Resources__ drop down.\n* During the workshop, it is recommended that you have a second device or printed copy of the student guide.  Previous workshops have demonstrated that unless you've memorized all of it, you'll likely need to refer to the guide, but your laptop will be projecting the slide decks.  Some students will fall behind and you'll need to refer back to other exercises/slides without having to change the projection for the entire class.\n\n\n"
  },
  {
    "path": "tools/aws_lab_setup/aws-directions/AWSHELP.md",
    "content": "# AWS DIRECTIONS HELP\n\nThese steps will walk you through where to create credentials (Access Key ID and Secret Access Key) on AWS to use for provisioning VMs with Ansible.\n\n## Login\n\nLogin to the AWS Console on [https://aws.amazon.com/](https://aws.amazon.com/)\n\n![login](login-window.png)\n\n## Go to IAM\n\nUnder Services click on IAM to reach the IAM Management Console\n\n![iam](iam.png)\n\n## Go to Users\n\nOn the left pane click on the users link\n\n![users](users.png)\n\nOn the right side, pick the tab \"Security credentials\". Now you need to either add a new user\n\n![add-user](add-user.png)\n\nOR find the user you want to add credentials <- this is more common\n\n## Add Credentials\n\nYou need to create a new access key, only then will the necessary key and key ID be shown. Make sure you save them somewhere!! (But don't upload to GitHub or someone will start mining Bitcoins on your account)\n\n![create-key](create-key.png)\n\nYou now have your Access Key ID and Secret Access Key!\n\n## AWS References\n\n- [Access Key ID and Secret Access Key](http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html)\n- [managing-aws-access-keys.html](http://docs.aws.amazon.com/general/latest/gr/managing-aws-access-keys.html).\n\n[Return to aws_lab_setup directions](../README.md)\n"
  },
  {
    "path": "tools/aws_lab_setup/inventory/ec2.ini",
    "content": "# Ansible EC2 external inventory script settings\n#\n\n[ec2]\n\n# to talk to a private eucalyptus instance uncomment these lines\n# and edit edit eucalyptus_host to be the host name of your cloud controller\n#eucalyptus = True\n#eucalyptus_host = clc.cloud.domain.org\n\n# AWS regions to make calls to. Set this to 'all' to make request to all regions\n# in AWS and merge the results together. Alternatively, set this to a comma\n# separated list of regions. E.g. 'us-east-1,us-west-1,us-west-2'\nregions = all\nregions_exclude = us-gov-west-1,cn-north-1\n\n# When generating inventory, Ansible needs to know how to address a server.\n# Each EC2 instance has a lot of variables associated with it. Here is the list:\n#   http://docs.pythonboto.org/en/latest/ref/ec2.html#module-boto.ec2.instance\n# Below are 2 variables that are used as the address of a server:\n#   - destination_variable\n#   - vpc_destination_variable\n\n# This is the normal destination variable to use. If you are running Ansible\n# from outside EC2, then 'public_dns_name' makes the most sense. If you are\n# running Ansible from within EC2, then perhaps you want to use the internal\n# address, and should set this to 'private_dns_name'. The key of an EC2 tag\n# may optionally be used; however the boto instance variables hold precedence\n# in the event of a collision.\ndestination_variable = public_dns_name\n\n# This allows you to override the inventory_name with an ec2 variable, instead\n# of using the destination_variable above. Addressing (aka ansible_ssh_host)\n# will still use destination_variable. Tags should be written as 'tag_TAGNAME'.\n#hostname_variable = tag_Name\n\n# For server inside a VPC, using DNS names may not make sense. When an instance\n# has 'subnet_id' set, this variable is used. If the subnet is public, setting\n# this to 'ip_address' will return the public IP address. For instances in a\n# private subnet, this should be set to 'private_ip_address', and Ansible must\n# be run from within EC2. The key of an EC2 tag may optionally be used; however\n# the boto instance variables hold precedence in the event of a collision.\n# WARNING: - instances that are in the private vpc, _without_ public ip address\n# will not be listed in the inventory until You set:\n# vpc_destination_variable = private_ip_address\nvpc_destination_variable = ip_address\n\n# The following two settings allow flexible ansible host naming based on a\n# python format string and a comma-separated list of ec2 tags.  Note that:\n#\n# 1) If the tags referenced are not present for some instances, empty strings\n#    will be substituted in the format string.\n# 2) This overrides both destination_variable and vpc_destination_variable.\n#\n#destination_format = {0}.{1}.example.com\n#destination_format_tags = Name,environment\n\n# To tag instances on EC2 with the resource records that point to them from\n# Route53, uncomment and set 'route53' to True.\nroute53 = False\n\n# To exclude RDS instances from the inventory, uncomment and set to False.\n#rds = False\n\n# To exclude ElastiCache instances from the inventory, uncomment and set to False.\n#elasticache = False\n\n# Additionally, you can specify the list of zones to exclude looking up in\n# 'route53_excluded_zones' as a comma-separated list.\n# route53_excluded_zones = samplezone1.com, samplezone2.com\n\n# By default, only EC2 instances in the 'running' state are returned. Set\n# 'all_instances' to True to return all instances regardless of state.\nall_instances = False\n\n# By default, only EC2 instances in the 'running' state are returned. Specify\n# EC2 instance states to return as a comma-separated list. This\n# option is overriden when 'all_instances' is True.\n# instance_states = pending, running, shutting-down, terminated, stopping, stopped\n\n# By default, only RDS instances in the 'available' state are returned.  Set\n# 'all_rds_instances' to True return all RDS instances regardless of state.\nall_rds_instances = False\n\n# By default, only ElastiCache clusters and nodes in the 'available' state\n# are returned. Set 'all_elasticache_clusters' and/or 'all_elastic_nodes'\n# to True return all ElastiCache clusters and nodes, regardless of state.\n#\n# Note that all_elasticache_nodes only applies to listed clusters. That means\n# if you set all_elastic_clusters to false, no node will be return from\n# unavailable clusters, regardless of the state and to what you set for\n# all_elasticache_nodes.\nall_elasticache_replication_groups = False\nall_elasticache_clusters = False\nall_elasticache_nodes = False\n\n# API calls to EC2 are slow. For this reason, we cache the results of an API\n# call. Set this to the path you want cache files to be written to. Two files\n# will be written to this directory:\n#   - ansible-ec2.cache\n#   - ansible-ec2.index\ncache_path = ~/.ansible/tmp\n\n# The number of seconds a cache file is considered valid. After this many\n# seconds, a new API call will be made, and the cache file will be updated.\n# To disable the cache, set this value to 0\ncache_max_age = 300\n\n# Organize groups into a nested/hierarchy instead of a flat namespace.\nnested_groups = False\n\n# Replace - tags when creating groups to avoid issues with ansible\nreplace_dash_in_groups = True\n\n# If set to true, any tag of the form \"a,b,c\" is expanded into a list\n# and the results are used to create additional tag_* inventory groups.\nexpand_csv_tags = False\n\n# The EC2 inventory output can become very large. To manage its size,\n# configure which groups should be created.\ngroup_by_instance_id = True\ngroup_by_region = True\ngroup_by_availability_zone = True\ngroup_by_ami_id = True\ngroup_by_instance_type = True\ngroup_by_key_pair = True\ngroup_by_vpc_id = True\ngroup_by_security_group = True\ngroup_by_tag_keys = True\ngroup_by_tag_none = True\ngroup_by_route53_names = True\ngroup_by_rds_engine = True\ngroup_by_rds_parameter_group = True\ngroup_by_elasticache_engine = True\ngroup_by_elasticache_cluster = True\ngroup_by_elasticache_parameter_group = True\ngroup_by_elasticache_replication_group = True\n\n# If you only want to include hosts that match a certain regular expression\n# pattern_include = staging-*\n\n# If you want to exclude any hosts that match a certain regular expression\n# pattern_exclude = staging-*\n\n# Instance filters can be used to control which instances are retrieved for\n# inventory. For the full list of possible filters, please read the EC2 API\n# docs: http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeInstances.html#query-DescribeInstances-filters\n# Filters are key/value pairs separated by '=', to list multiple filters use\n# a list separated by commas. See examples below.\n\n# Retrieve only instances with (key=value) env=staging tag\n# instance_filters = tag:env=staging\n\n# Retrieve only instances with role=webservers OR role=dbservers tag\n# instance_filters = tag:role=webservers,tag:role=dbservers\n\n# Retrieve only t1.micro instances OR instances with tag env=staging\n# instance_filters = instance-type=t1.micro,tag:env=staging\n\n# You can use wildcards in filter values also. Below will list instances which\n# tag Name value matches webservers1*\n# (ex. webservers15, webservers1a, webservers123 etc)\n# instance_filters = tag:Name=webservers1*\n\n# A boto configuration profile may be used to separate out credentials\n# see http://boto.readthedocs.org/en/latest/boto_config_tut.html\nboto_profile = default\n"
  },
  {
    "path": "tools/aws_lab_setup/inventory/ec2.py",
    "content": "#!/usr/bin/env python\n\n'''\nEC2 external inventory script\n=================================\n\nGenerates inventory that Ansible can understand by making API request to\nAWS EC2 using the Boto library.\n\nNOTE: This script assumes Ansible is being executed where the environment\nvariables needed for Boto have already been set:\n    export AWS_ACCESS_KEY_ID='AK123'\n    export AWS_SECRET_ACCESS_KEY='abc123'\n\nThis script also assumes there is an ec2.ini file alongside it.  To specify a\ndifferent path to ec2.ini, define the EC2_INI_PATH environment variable:\n\n    export EC2_INI_PATH=/path/to/my_ec2.ini\n\nIf you're using eucalyptus you need to set the above variables and\nyou need to define:\n\n    export EC2_URL=http://hostname_of_your_cc:port/services/Eucalyptus\n\nIf you're using boto profiles (requires boto>=2.24.0) you can choose a profile\nusing the --boto-profile command line argument (e.g. ec2.py --boto-profile prod) or using\nthe AWS_PROFILE variable:\n\n    AWS_PROFILE=prod ansible-playbook -i ec2.py myplaybook.yml\n\nFor more details, see: http://docs.pythonboto.org/en/latest/boto_config_tut.html\n\nWhen run against a specific host, this script returns the following variables:\n - ec2_ami_launch_index\n - ec2_architecture\n - ec2_association\n - ec2_attachTime\n - ec2_attachment\n - ec2_attachmentId\n - ec2_client_token\n - ec2_deleteOnTermination\n - ec2_description\n - ec2_deviceIndex\n - ec2_dns_name\n - ec2_eventsSet\n - ec2_group_name\n - ec2_hypervisor\n - ec2_id\n - ec2_image_id\n - ec2_instanceState\n - ec2_instance_type\n - ec2_ipOwnerId\n - ec2_ip_address\n - ec2_item\n - ec2_kernel\n - ec2_key_name\n - ec2_launch_time\n - ec2_monitored\n - ec2_monitoring\n - ec2_networkInterfaceId\n - ec2_ownerId\n - ec2_persistent\n - ec2_placement\n - ec2_platform\n - ec2_previous_state\n - ec2_private_dns_name\n - ec2_private_ip_address\n - ec2_publicIp\n - ec2_public_dns_name\n - ec2_ramdisk\n - ec2_reason\n - ec2_region\n - ec2_requester_id\n - ec2_root_device_name\n - ec2_root_device_type\n - ec2_security_group_ids\n - ec2_security_group_names\n - ec2_shutdown_state\n - ec2_sourceDestCheck\n - ec2_spot_instance_request_id\n - ec2_state\n - ec2_state_code\n - ec2_state_reason\n - ec2_status\n - ec2_subnet_id\n - ec2_tenancy\n - ec2_virtualization_type\n - ec2_vpc_id\n\nThese variables are pulled out of a boto.ec2.instance object. There is a lack of\nconsistency with variable spellings (camelCase and underscores) since this\njust loops through all variables the object exposes. It is preferred to use the\nones with underscores when multiple exist.\n\nIn addition, if an instance has AWS Tags associated with it, each tag is a new\nvariable named:\n - ec2_tag_[Key] = [Value]\n\nSecurity groups are comma-separated in 'ec2_security_group_ids' and\n'ec2_security_group_names'.\n'''\n\n# (c) 2012, Peter Sankauskas\n#\n# This file is part of Ansible,\n#\n# Ansible is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Ansible is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\n\n######################################################################\n\nimport sys\nimport os\nimport argparse\nimport re\nfrom time import time\nimport boto\nfrom boto import ec2\nfrom boto import rds\nfrom boto import elasticache\nfrom boto import route53\nimport six\n\nfrom six.moves import configparser\nfrom collections import defaultdict\n\ntry:\n    import json\nexcept ImportError:\n    import simplejson as json\n\n\nclass Ec2Inventory(object):\n\n    def _empty_inventory(self):\n        return {\"_meta\" : {\"hostvars\" : {}}}\n\n    def __init__(self):\n        ''' Main execution path '''\n\n        # Inventory grouped by instance IDs, tags, security groups, regions,\n        # and availability zones\n        self.inventory = self._empty_inventory()\n\n        # Index of hostname (address) to instance ID\n        self.index = {}\n\n        # Boto profile to use (if any)\n        self.boto_profile = None\n\n        # Read settings and parse CLI arguments\n        self.parse_cli_args()\n        self.read_settings()\n\n        # Make sure that profile_name is not passed at all if not set\n        # as pre 2.24 boto will fall over otherwise\n        if self.boto_profile:\n            if not hasattr(boto.ec2.EC2Connection, 'profile_name'):\n                self.fail_with_error(\"boto version must be >= 2.24 to use profile\")\n\n        # Cache\n        if self.args.refresh_cache:\n            self.do_api_calls_update_cache()\n        elif not self.is_cache_valid():\n            self.do_api_calls_update_cache()\n\n        # Data to print\n        if self.args.host:\n            data_to_print = self.get_host_info()\n\n        elif self.args.list:\n            # Display list of instances for inventory\n            if self.inventory == self._empty_inventory():\n                data_to_print = self.get_inventory_from_cache()\n            else:\n                data_to_print = self.json_format_dict(self.inventory, True)\n\n        print(data_to_print)\n\n\n    def is_cache_valid(self):\n        ''' Determines if the cache files have expired, or if it is still valid '''\n\n        if os.path.isfile(self.cache_path_cache):\n            mod_time = os.path.getmtime(self.cache_path_cache)\n            current_time = time()\n            if (mod_time + self.cache_max_age) > current_time:\n                if os.path.isfile(self.cache_path_index):\n                    return True\n\n        return False\n\n\n    def read_settings(self):\n        ''' Reads the settings from the ec2.ini file '''\n        if six.PY3:\n            config = configparser.ConfigParser()\n        else:\n            config = configparser.SafeConfigParser()\n        ec2_default_ini_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'ec2.ini')\n        ec2_ini_path = os.path.expanduser(os.path.expandvars(os.environ.get('EC2_INI_PATH', ec2_default_ini_path)))\n        config.read(ec2_ini_path)\n\n        # is eucalyptus?\n        self.eucalyptus_host = None\n        self.eucalyptus = False\n        if config.has_option('ec2', 'eucalyptus'):\n            self.eucalyptus = config.getboolean('ec2', 'eucalyptus')\n        if self.eucalyptus and config.has_option('ec2', 'eucalyptus_host'):\n            self.eucalyptus_host = config.get('ec2', 'eucalyptus_host')\n\n        # Regions\n        self.regions = []\n        configRegions = config.get('ec2', 'regions')\n        configRegions_exclude = config.get('ec2', 'regions_exclude')\n        if (configRegions == 'all'):\n            if self.eucalyptus_host:\n                self.regions.append(boto.connect_euca(host=self.eucalyptus_host).region.name)\n            else:\n                for regionInfo in ec2.regions():\n                    if regionInfo.name not in configRegions_exclude:\n                        self.regions.append(regionInfo.name)\n        else:\n            self.regions = configRegions.split(\",\")\n\n        # Destination addresses\n        self.destination_variable = config.get('ec2', 'destination_variable')\n        self.vpc_destination_variable = config.get('ec2', 'vpc_destination_variable')\n\n        if config.has_option('ec2', 'hostname_variable'):\n            self.hostname_variable = config.get('ec2', 'hostname_variable')\n        else:\n            self.hostname_variable = None\n\n        if config.has_option('ec2', 'destination_format') and \\\n           config.has_option('ec2', 'destination_format_tags'):\n            self.destination_format = config.get('ec2', 'destination_format')\n            self.destination_format_tags = config.get('ec2', 'destination_format_tags').split(',')\n        else:\n            self.destination_format = None\n            self.destination_format_tags = None\n\n        # Route53\n        self.route53_enabled = config.getboolean('ec2', 'route53')\n        self.route53_excluded_zones = []\n        if config.has_option('ec2', 'route53_excluded_zones'):\n            self.route53_excluded_zones.extend(\n                config.get('ec2', 'route53_excluded_zones', '').split(','))\n\n        # Include RDS instances?\n        self.rds_enabled = True\n        if config.has_option('ec2', 'rds'):\n            self.rds_enabled = config.getboolean('ec2', 'rds')\n\n        # Include ElastiCache instances?\n        self.elasticache_enabled = True\n        if config.has_option('ec2', 'elasticache'):\n            self.elasticache_enabled = config.getboolean('ec2', 'elasticache')\n\n        # Return all EC2 instances?\n        if config.has_option('ec2', 'all_instances'):\n            self.all_instances = config.getboolean('ec2', 'all_instances')\n        else:\n            self.all_instances = False\n\n        # Instance states to be gathered in inventory. Default is 'running'.\n        # Setting 'all_instances' to 'yes' overrides this option.\n        ec2_valid_instance_states = [\n            'pending',\n            'running',\n            'shutting-down',\n            'terminated',\n            'stopping',\n            'stopped'\n        ]\n        self.ec2_instance_states = []\n        if self.all_instances:\n            self.ec2_instance_states = ec2_valid_instance_states\n        elif config.has_option('ec2', 'instance_states'):\n          for instance_state in config.get('ec2', 'instance_states').split(','):\n            instance_state = instance_state.strip()\n            if instance_state not in ec2_valid_instance_states:\n              continue\n            self.ec2_instance_states.append(instance_state)\n        else:\n          self.ec2_instance_states = ['running']\n\n        # Return all RDS instances? (if RDS is enabled)\n        if config.has_option('ec2', 'all_rds_instances') and self.rds_enabled:\n            self.all_rds_instances = config.getboolean('ec2', 'all_rds_instances')\n        else:\n            self.all_rds_instances = False\n\n        # Return all ElastiCache replication groups? (if ElastiCache is enabled)\n        if config.has_option('ec2', 'all_elasticache_replication_groups') and self.elasticache_enabled:\n            self.all_elasticache_replication_groups = config.getboolean('ec2', 'all_elasticache_replication_groups')\n        else:\n            self.all_elasticache_replication_groups = False\n\n        # Return all ElastiCache clusters? (if ElastiCache is enabled)\n        if config.has_option('ec2', 'all_elasticache_clusters') and self.elasticache_enabled:\n            self.all_elasticache_clusters = config.getboolean('ec2', 'all_elasticache_clusters')\n        else:\n            self.all_elasticache_clusters = False\n\n        # Return all ElastiCache nodes? (if ElastiCache is enabled)\n        if config.has_option('ec2', 'all_elasticache_nodes') and self.elasticache_enabled:\n            self.all_elasticache_nodes = config.getboolean('ec2', 'all_elasticache_nodes')\n        else:\n            self.all_elasticache_nodes = False\n\n        # boto configuration profile (prefer CLI argument)\n        self.boto_profile = self.args.boto_profile\n        if config.has_option('ec2', 'boto_profile') and not self.boto_profile:\n            self.boto_profile = config.get('ec2', 'boto_profile')\n\n        # Cache related\n        cache_dir = os.path.expanduser(config.get('ec2', 'cache_path'))\n        if self.boto_profile:\n            cache_dir = os.path.join(cache_dir, 'profile_' + self.boto_profile)\n        if not os.path.exists(cache_dir):\n            os.makedirs(cache_dir)\n\n        self.cache_path_cache = cache_dir + \"/ansible-ec2.cache\"\n        self.cache_path_index = cache_dir + \"/ansible-ec2.index\"\n        self.cache_max_age = config.getint('ec2', 'cache_max_age')\n\n        if config.has_option('ec2', 'expand_csv_tags'):\n            self.expand_csv_tags = config.getboolean('ec2', 'expand_csv_tags')\n        else:\n            self.expand_csv_tags = False\n\n        # Configure nested groups instead of flat namespace.\n        if config.has_option('ec2', 'nested_groups'):\n            self.nested_groups = config.getboolean('ec2', 'nested_groups')\n        else:\n            self.nested_groups = False\n\n        # Replace dash or not in group names\n        if config.has_option('ec2', 'replace_dash_in_groups'):\n            self.replace_dash_in_groups = config.getboolean('ec2', 'replace_dash_in_groups')\n        else:\n            self.replace_dash_in_groups = True\n\n        # Configure which groups should be created.\n        group_by_options = [\n            'group_by_instance_id',\n            'group_by_region',\n            'group_by_availability_zone',\n            'group_by_ami_id',\n            'group_by_instance_type',\n            'group_by_key_pair',\n            'group_by_vpc_id',\n            'group_by_security_group',\n            'group_by_tag_keys',\n            'group_by_tag_none',\n            'group_by_route53_names',\n            'group_by_rds_engine',\n            'group_by_rds_parameter_group',\n            'group_by_elasticache_engine',\n            'group_by_elasticache_cluster',\n            'group_by_elasticache_parameter_group',\n            'group_by_elasticache_replication_group',\n        ]\n        for option in group_by_options:\n            if config.has_option('ec2', option):\n                setattr(self, option, config.getboolean('ec2', option))\n            else:\n                setattr(self, option, True)\n\n        # Do we need to just include hosts that match a pattern?\n        try:\n            pattern_include = config.get('ec2', 'pattern_include')\n            if pattern_include and len(pattern_include) > 0:\n                self.pattern_include = re.compile(pattern_include)\n            else:\n                self.pattern_include = None\n        except configparser.NoOptionError:\n            self.pattern_include = None\n\n        # Do we need to exclude hosts that match a pattern?\n        try:\n            pattern_exclude = config.get('ec2', 'pattern_exclude');\n            if pattern_exclude and len(pattern_exclude) > 0:\n                self.pattern_exclude = re.compile(pattern_exclude)\n            else:\n                self.pattern_exclude = None\n        except configparser.NoOptionError:\n            self.pattern_exclude = None\n\n        # Instance filters (see boto and EC2 API docs). Ignore invalid filters.\n        self.ec2_instance_filters = defaultdict(list)\n        if config.has_option('ec2', 'instance_filters'):\n\n            filters = [f for f in config.get('ec2', 'instance_filters').split(',') if f]\n\n            for instance_filter in filters:\n                instance_filter = instance_filter.strip()\n                if not instance_filter or '=' not in instance_filter:\n                    continue\n                filter_key, filter_value = [x.strip() for x in instance_filter.split('=', 1)]\n                if not filter_key:\n                    continue\n                self.ec2_instance_filters[filter_key].append(filter_value)\n\n    def parse_cli_args(self):\n        ''' Command line argument processing '''\n\n        parser = argparse.ArgumentParser(description='Produce an Ansible Inventory file based on EC2')\n        parser.add_argument('--list', action='store_true', default=True,\n                           help='List instances (default: True)')\n        parser.add_argument('--host', action='store',\n                           help='Get all the variables about a specific instance')\n        parser.add_argument('--refresh-cache', action='store_true', default=False,\n                           help='Force refresh of cache by making API requests to EC2 (default: False - use cache files)')\n        parser.add_argument('--profile', '--boto-profile', action='store', dest='boto_profile',\n                           help='Use boto profile for connections to EC2')\n        self.args = parser.parse_args()\n\n\n    def do_api_calls_update_cache(self):\n        ''' Do API calls to each region, and save data in cache files '''\n\n        if self.route53_enabled:\n            self.get_route53_records()\n\n        for region in self.regions:\n            self.get_instances_by_region(region)\n            if self.rds_enabled:\n                self.get_rds_instances_by_region(region)\n            if self.elasticache_enabled:\n                self.get_elasticache_clusters_by_region(region)\n                self.get_elasticache_replication_groups_by_region(region)\n\n        self.write_to_cache(self.inventory, self.cache_path_cache)\n        self.write_to_cache(self.index, self.cache_path_index)\n\n    def connect(self, region):\n        ''' create connection to api server'''\n        if self.eucalyptus:\n            conn = boto.connect_euca(host=self.eucalyptus_host)\n            conn.APIVersion = '2010-08-31'\n        else:\n            conn = self.connect_to_aws(ec2, region)\n        return conn\n\n    def boto_fix_security_token_in_profile(self, connect_args):\n        ''' monkey patch for boto issue boto/boto#2100 '''\n        profile = 'profile ' + self.boto_profile\n        if boto.config.has_option(profile, 'aws_security_token'):\n            connect_args['security_token'] = boto.config.get(profile, 'aws_security_token')\n        return connect_args\n\n    def connect_to_aws(self, module, region):\n        connect_args = {}\n\n        # only pass the profile name if it's set (as it is not supported by older boto versions)\n        if self.boto_profile:\n            connect_args['profile_name'] = self.boto_profile\n            self.boto_fix_security_token_in_profile(connect_args)\n\n        conn = module.connect_to_region(region, **connect_args)\n        # connect_to_region will fail \"silently\" by returning None if the region name is wrong or not supported\n        if conn is None:\n            self.fail_with_error(\"region name: %s likely not supported, or AWS is down.  connection to region failed.\" % region)\n        return conn\n\n    def get_instances_by_region(self, region):\n        ''' Makes an AWS EC2 API call to the list of instances in a particular\n        region '''\n\n        try:\n            conn = self.connect(region)\n            reservations = []\n            if self.ec2_instance_filters:\n                for filter_key, filter_values in self.ec2_instance_filters.items():\n                    reservations.extend(conn.get_all_instances(filters = { filter_key : filter_values }))\n            else:\n                reservations = conn.get_all_instances()\n\n            for reservation in reservations:\n                for instance in reservation.instances:\n                    self.add_instance(instance, region)\n\n        except boto.exception.BotoServerError as e:\n            if e.error_code == 'AuthFailure':\n                error = self.get_auth_error_message()\n            else:\n                backend = 'Eucalyptus' if self.eucalyptus else 'AWS'\n                error = \"Error connecting to %s backend.\\n%s\" % (backend, e.message)\n            self.fail_with_error(error, 'getting EC2 instances')\n\n    def get_rds_instances_by_region(self, region):\n        ''' Makes an AWS API call to the list of RDS instances in a particular\n        region '''\n\n        try:\n            conn = self.connect_to_aws(rds, region)\n            if conn:\n                marker = None\n                while True:\n                    instances = conn.get_all_dbinstances(marker=marker)\n                    marker = instances.marker\n                    for instance in instances:\n                        self.add_rds_instance(instance, region)\n                    if not marker:\n                        break\n        except boto.exception.BotoServerError as e:\n            error = e.reason\n\n            if e.error_code == 'AuthFailure':\n                error = self.get_auth_error_message()\n            if not e.reason == \"Forbidden\":\n                error = \"Looks like AWS RDS is down:\\n%s\" % e.message\n            self.fail_with_error(error, 'getting RDS instances')\n\n    def get_elasticache_clusters_by_region(self, region):\n        ''' Makes an AWS API call to the list of ElastiCache clusters (with\n        nodes' info) in a particular region.'''\n\n        # ElastiCache boto module doesn't provide a get_all_intances method,\n        # that's why we need to call describe directly (it would be called by\n        # the shorthand method anyway...)\n        try:\n            conn = self.connect_to_aws(elasticache, region)\n            if conn:\n                # show_cache_node_info = True\n                # because we also want nodes' information\n                response = conn.describe_cache_clusters(None, None, None, True)\n\n        except boto.exception.BotoServerError as e:\n            error = e.reason\n\n            if e.error_code == 'AuthFailure':\n                error = self.get_auth_error_message()\n            if not e.reason == \"Forbidden\":\n                error = \"Looks like AWS ElastiCache is down:\\n%s\" % e.message\n            self.fail_with_error(error, 'getting ElastiCache clusters')\n\n        try:\n            # Boto also doesn't provide wrapper classes to CacheClusters or\n            # CacheNodes. Because of that wo can't make use of the get_list\n            # method in the AWSQueryConnection. Let's do the work manually\n            clusters = response['DescribeCacheClustersResponse']['DescribeCacheClustersResult']['CacheClusters']\n\n        except KeyError as e:\n            error = \"ElastiCache query to AWS failed (unexpected format).\"\n            self.fail_with_error(error, 'getting ElastiCache clusters')\n\n        for cluster in clusters:\n            self.add_elasticache_cluster(cluster, region)\n\n    def get_elasticache_replication_groups_by_region(self, region):\n        ''' Makes an AWS API call to the list of ElastiCache replication groups\n        in a particular region.'''\n\n        # ElastiCache boto module doesn't provide a get_all_intances method,\n        # that's why we need to call describe directly (it would be called by\n        # the shorthand method anyway...)\n        try:\n            conn = self.connect_to_aws(elasticache, region)\n            if conn:\n                response = conn.describe_replication_groups()\n\n        except boto.exception.BotoServerError as e:\n            error = e.reason\n\n            if e.error_code == 'AuthFailure':\n                error = self.get_auth_error_message()\n            if not e.reason == \"Forbidden\":\n                error = \"Looks like AWS ElastiCache [Replication Groups] is down:\\n%s\" % e.message\n            self.fail_with_error(error, 'getting ElastiCache clusters')\n\n        try:\n            # Boto also doesn't provide wrapper classes to ReplicationGroups\n            # Because of that wo can't make use of the get_list method in the\n            # AWSQueryConnection. Let's do the work manually\n            replication_groups = response['DescribeReplicationGroupsResponse']['DescribeReplicationGroupsResult']['ReplicationGroups']\n\n        except KeyError as e:\n            error = \"ElastiCache [Replication Groups] query to AWS failed (unexpected format).\"\n            self.fail_with_error(error, 'getting ElastiCache clusters')\n\n        for replication_group in replication_groups:\n            self.add_elasticache_replication_group(replication_group, region)\n\n    def get_auth_error_message(self):\n        ''' create an informative error message if there is an issue authenticating'''\n        errors = [\"Authentication error retrieving ec2 inventory.\"]\n        if None in [os.environ.get('AWS_ACCESS_KEY_ID'), os.environ.get('AWS_SECRET_ACCESS_KEY')]:\n            errors.append(' - No AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY environment vars found')\n        else:\n            errors.append(' - AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment vars found but may not be correct')\n\n        boto_paths = ['/etc/boto.cfg', '~/.boto', '~/.aws/credentials']\n        boto_config_found = list(p for p in boto_paths if os.path.isfile(os.path.expanduser(p)))\n        if len(boto_config_found) > 0:\n            errors.append(\" - Boto configs found at '%s', but the credentials contained may not be correct\" % ', '.join(boto_config_found))\n        else:\n            errors.append(\" - No Boto config found at any expected location '%s'\" % ', '.join(boto_paths))\n\n        return '\\n'.join(errors)\n\n    def fail_with_error(self, err_msg, err_operation=None):\n        '''log an error to std err for ansible-playbook to consume and exit'''\n        if err_operation:\n            err_msg = 'ERROR: \"{err_msg}\", while: {err_operation}'.format(\n                err_msg=err_msg, err_operation=err_operation)\n        sys.stderr.write(err_msg)\n        sys.exit(1)\n\n    def get_instance(self, region, instance_id):\n        conn = self.connect(region)\n\n        reservations = conn.get_all_instances([instance_id])\n        for reservation in reservations:\n            for instance in reservation.instances:\n                return instance\n\n    def add_instance(self, instance, region):\n        ''' Adds an instance to the inventory and index, as long as it is\n        addressable '''\n\n        # Only return instances with desired instance states\n        if instance.state not in self.ec2_instance_states:\n            return\n\n        # Select the best destination address\n        if self.destination_format and self.destination_format_tags:\n            dest = self.destination_format.format(*[ getattr(instance, 'tags').get(tag, '') for tag in self.destination_format_tags ])\n        elif instance.subnet_id:\n            dest = getattr(instance, self.vpc_destination_variable, None)\n            if dest is None:\n                dest = getattr(instance, 'tags').get(self.vpc_destination_variable, None)\n        else:\n            dest = getattr(instance, self.destination_variable, None)\n            if dest is None:\n                dest = getattr(instance, 'tags').get(self.destination_variable, None)\n\n        if not dest:\n            # Skip instances we cannot address (e.g. private VPC subnet)\n            return\n\n        # Set the inventory name\n        hostname = None\n        if self.hostname_variable:\n            if self.hostname_variable.startswith('tag_'):\n                hostname = instance.tags.get(self.hostname_variable[4:], None)\n            else:\n                hostname = getattr(instance, self.hostname_variable)\n\n        # If we can't get a nice hostname, use the destination address\n        if not hostname:\n            hostname = dest\n\n        hostname = self.to_safe(hostname).lower()\n\n        # if we only want to include hosts that match a pattern, skip those that don't\n        if self.pattern_include and not self.pattern_include.match(hostname):\n            return\n\n        # if we need to exclude hosts that match a pattern, skip those\n        if self.pattern_exclude and self.pattern_exclude.match(hostname):\n            return\n\n        # Add to index\n        self.index[hostname] = [region, instance.id]\n\n        # Inventory: Group by instance ID (always a group of 1)\n        if self.group_by_instance_id:\n            self.inventory[instance.id] = [hostname]\n            if self.nested_groups:\n                self.push_group(self.inventory, 'instances', instance.id)\n\n        # Inventory: Group by region\n        if self.group_by_region:\n            self.push(self.inventory, region, hostname)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'regions', region)\n\n        # Inventory: Group by availability zone\n        if self.group_by_availability_zone:\n            self.push(self.inventory, instance.placement, hostname)\n            if self.nested_groups:\n                if self.group_by_region:\n                    self.push_group(self.inventory, region, instance.placement)\n                self.push_group(self.inventory, 'zones', instance.placement)\n\n        # Inventory: Group by Amazon Machine Image (AMI) ID\n        if self.group_by_ami_id:\n            ami_id = self.to_safe(instance.image_id)\n            self.push(self.inventory, ami_id, hostname)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'images', ami_id)\n\n        # Inventory: Group by instance type\n        if self.group_by_instance_type:\n            type_name = self.to_safe('type_' + instance.instance_type)\n            self.push(self.inventory, type_name, hostname)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'types', type_name)\n\n        # Inventory: Group by key pair\n        if self.group_by_key_pair and instance.key_name:\n            key_name = self.to_safe('key_' + instance.key_name)\n            self.push(self.inventory, key_name, hostname)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'keys', key_name)\n\n        # Inventory: Group by VPC\n        if self.group_by_vpc_id and instance.vpc_id:\n            vpc_id_name = self.to_safe('vpc_id_' + instance.vpc_id)\n            self.push(self.inventory, vpc_id_name, hostname)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'vpcs', vpc_id_name)\n\n        # Inventory: Group by security group\n        if self.group_by_security_group:\n            try:\n                for group in instance.groups:\n                    key = self.to_safe(\"security_group_\" + group.name)\n                    self.push(self.inventory, key, hostname)\n                    if self.nested_groups:\n                        self.push_group(self.inventory, 'security_groups', key)\n            except AttributeError:\n                self.fail_with_error('\\n'.join(['Package boto seems a bit older.',\n                                            'Please upgrade boto >= 2.3.0.']))\n\n        # Inventory: Group by tag keys\n        if self.group_by_tag_keys:\n            for k, v in instance.tags.items():\n                if self.expand_csv_tags and v and ',' in v:\n                    values = map(lambda x: x.strip(), v.split(','))\n                else:\n                    values = [v]\n\n                for v in values:\n                    if v:\n                        key = self.to_safe(\"tag_\" + k + \"=\" + v)\n                    else:\n                        key = self.to_safe(\"tag_\" + k)\n                    self.push(self.inventory, key, hostname)\n                    if self.nested_groups:\n                        self.push_group(self.inventory, 'tags', self.to_safe(\"tag_\" + k))\n                        if v:\n                            self.push_group(self.inventory, self.to_safe(\"tag_\" + k), key)\n\n        # Inventory: Group by Route53 domain names if enabled\n        if self.route53_enabled and self.group_by_route53_names:\n            route53_names = self.get_instance_route53_names(instance)\n            for name in route53_names:\n                self.push(self.inventory, name, hostname)\n                if self.nested_groups:\n                    self.push_group(self.inventory, 'route53', name)\n\n        # Global Tag: instances without tags\n        if self.group_by_tag_none and len(instance.tags) == 0:\n            self.push(self.inventory, 'tag_none', hostname)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'tags', 'tag_none')\n\n        # Global Tag: tag all EC2 instances\n        self.push(self.inventory, 'ec2', hostname)\n\n        self.inventory[\"_meta\"][\"hostvars\"][hostname] = self.get_host_info_dict_from_instance(instance)\n        self.inventory[\"_meta\"][\"hostvars\"][hostname]['ansible_host'] = dest\n\n\n    def add_rds_instance(self, instance, region):\n        ''' Adds an RDS instance to the inventory and index, as long as it is\n        addressable '''\n\n        # Only want available instances unless all_rds_instances is True\n        if not self.all_rds_instances and instance.status != 'available':\n            return\n\n        # Select the best destination address\n        dest = instance.endpoint[0]\n\n        if not dest:\n            # Skip instances we cannot address (e.g. private VPC subnet)\n            return\n\n        # Set the inventory name\n        hostname = None\n        if self.hostname_variable:\n            if self.hostname_variable.startswith('tag_'):\n                hostname = instance.tags.get(self.hostname_variable[4:], None)\n            else:\n                hostname = getattr(instance, self.hostname_variable)\n\n        # If we can't get a nice hostname, use the destination address\n        if not hostname:\n            hostname = dest\n\n        hostname = self.to_safe(hostname).lower()\n\n        # Add to index\n        self.index[hostname] = [region, instance.id]\n\n        # Inventory: Group by instance ID (always a group of 1)\n        if self.group_by_instance_id:\n            self.inventory[instance.id] = [hostname]\n            if self.nested_groups:\n                self.push_group(self.inventory, 'instances', instance.id)\n\n        # Inventory: Group by region\n        if self.group_by_region:\n            self.push(self.inventory, region, hostname)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'regions', region)\n\n        # Inventory: Group by availability zone\n        if self.group_by_availability_zone:\n            self.push(self.inventory, instance.availability_zone, hostname)\n            if self.nested_groups:\n                if self.group_by_region:\n                    self.push_group(self.inventory, region, instance.availability_zone)\n                self.push_group(self.inventory, 'zones', instance.availability_zone)\n\n        # Inventory: Group by instance type\n        if self.group_by_instance_type:\n            type_name = self.to_safe('type_' + instance.instance_class)\n            self.push(self.inventory, type_name, hostname)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'types', type_name)\n\n        # Inventory: Group by VPC\n        if self.group_by_vpc_id and instance.subnet_group and instance.subnet_group.vpc_id:\n            vpc_id_name = self.to_safe('vpc_id_' + instance.subnet_group.vpc_id)\n            self.push(self.inventory, vpc_id_name, hostname)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'vpcs', vpc_id_name)\n\n        # Inventory: Group by security group\n        if self.group_by_security_group:\n            try:\n                if instance.security_group:\n                    key = self.to_safe(\"security_group_\" + instance.security_group.name)\n                    self.push(self.inventory, key, hostname)\n                    if self.nested_groups:\n                        self.push_group(self.inventory, 'security_groups', key)\n\n            except AttributeError:\n                self.fail_with_error('\\n'.join(['Package boto seems a bit older.',\n                                            'Please upgrade boto >= 2.3.0.']))\n\n\n        # Inventory: Group by engine\n        if self.group_by_rds_engine:\n            self.push(self.inventory, self.to_safe(\"rds_\" + instance.engine), hostname)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'rds_engines', self.to_safe(\"rds_\" + instance.engine))\n\n        # Inventory: Group by parameter group\n        if self.group_by_rds_parameter_group:\n            self.push(self.inventory, self.to_safe(\"rds_parameter_group_\" + instance.parameter_group.name), hostname)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'rds_parameter_groups', self.to_safe(\"rds_parameter_group_\" + instance.parameter_group.name))\n\n        # Global Tag: all RDS instances\n        self.push(self.inventory, 'rds', hostname)\n\n        self.inventory[\"_meta\"][\"hostvars\"][hostname] = self.get_host_info_dict_from_instance(instance)\n        self.inventory[\"_meta\"][\"hostvars\"][hostname]['ansible_host'] = dest\n\n    def add_elasticache_cluster(self, cluster, region):\n        ''' Adds an ElastiCache cluster to the inventory and index, as long as\n        it's nodes are addressable '''\n\n        # Only want available clusters unless all_elasticache_clusters is True\n        if not self.all_elasticache_clusters and cluster['CacheClusterStatus'] != 'available':\n            return\n\n        # Select the best destination address\n        if 'ConfigurationEndpoint' in cluster and cluster['ConfigurationEndpoint']:\n            # Memcached cluster\n            dest = cluster['ConfigurationEndpoint']['Address']\n            is_redis = False\n        else:\n            # Redis sigle node cluster\n            # Because all Redis clusters are single nodes, we'll merge the\n            # info from the cluster with info about the node\n            dest = cluster['CacheNodes'][0]['Endpoint']['Address']\n            is_redis = True\n\n        if not dest:\n            # Skip clusters we cannot address (e.g. private VPC subnet)\n            return\n\n        # Add to index\n        self.index[dest] = [region, cluster['CacheClusterId']]\n\n        # Inventory: Group by instance ID (always a group of 1)\n        if self.group_by_instance_id:\n            self.inventory[cluster['CacheClusterId']] = [dest]\n            if self.nested_groups:\n                self.push_group(self.inventory, 'instances', cluster['CacheClusterId'])\n\n        # Inventory: Group by region\n        if self.group_by_region and not is_redis:\n            self.push(self.inventory, region, dest)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'regions', region)\n\n        # Inventory: Group by availability zone\n        if self.group_by_availability_zone and not is_redis:\n            self.push(self.inventory, cluster['PreferredAvailabilityZone'], dest)\n            if self.nested_groups:\n                if self.group_by_region:\n                    self.push_group(self.inventory, region, cluster['PreferredAvailabilityZone'])\n                self.push_group(self.inventory, 'zones', cluster['PreferredAvailabilityZone'])\n\n        # Inventory: Group by node type\n        if self.group_by_instance_type and not is_redis:\n            type_name = self.to_safe('type_' + cluster['CacheNodeType'])\n            self.push(self.inventory, type_name, dest)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'types', type_name)\n\n        # Inventory: Group by VPC (information not available in the current\n        # AWS API version for ElastiCache)\n\n        # Inventory: Group by security group\n        if self.group_by_security_group and not is_redis:\n\n            # Check for the existence of the 'SecurityGroups' key and also if\n            # this key has some value. When the cluster is not placed in a SG\n            # the query can return None here and cause an error.\n            if 'SecurityGroups' in cluster and cluster['SecurityGroups'] is not None:\n                for security_group in cluster['SecurityGroups']:\n                    key = self.to_safe(\"security_group_\" + security_group['SecurityGroupId'])\n                    self.push(self.inventory, key, dest)\n                    if self.nested_groups:\n                        self.push_group(self.inventory, 'security_groups', key)\n\n        # Inventory: Group by engine\n        if self.group_by_elasticache_engine and not is_redis:\n            self.push(self.inventory, self.to_safe(\"elasticache_\" + cluster['Engine']), dest)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'elasticache_engines', self.to_safe(cluster['Engine']))\n\n        # Inventory: Group by parameter group\n        if self.group_by_elasticache_parameter_group:\n            self.push(self.inventory, self.to_safe(\"elasticache_parameter_group_\" + cluster['CacheParameterGroup']['CacheParameterGroupName']), dest)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'elasticache_parameter_groups', self.to_safe(cluster['CacheParameterGroup']['CacheParameterGroupName']))\n\n        # Inventory: Group by replication group\n        if self.group_by_elasticache_replication_group and 'ReplicationGroupId' in cluster and cluster['ReplicationGroupId']:\n            self.push(self.inventory, self.to_safe(\"elasticache_replication_group_\" + cluster['ReplicationGroupId']), dest)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'elasticache_replication_groups', self.to_safe(cluster['ReplicationGroupId']))\n\n        # Global Tag: all ElastiCache clusters\n        self.push(self.inventory, 'elasticache_clusters', cluster['CacheClusterId'])\n\n        host_info = self.get_host_info_dict_from_describe_dict(cluster)\n\n        self.inventory[\"_meta\"][\"hostvars\"][dest] = host_info\n\n        # Add the nodes\n        for node in cluster['CacheNodes']:\n            self.add_elasticache_node(node, cluster, region)\n\n    def add_elasticache_node(self, node, cluster, region):\n        ''' Adds an ElastiCache node to the inventory and index, as long as\n        it is addressable '''\n\n        # Only want available nodes unless all_elasticache_nodes is True\n        if not self.all_elasticache_nodes and node['CacheNodeStatus'] != 'available':\n            return\n\n        # Select the best destination address\n        dest = node['Endpoint']['Address']\n\n        if not dest:\n            # Skip nodes we cannot address (e.g. private VPC subnet)\n            return\n\n        node_id = self.to_safe(cluster['CacheClusterId'] + '_' + node['CacheNodeId'])\n\n        # Add to index\n        self.index[dest] = [region, node_id]\n\n        # Inventory: Group by node ID (always a group of 1)\n        if self.group_by_instance_id:\n            self.inventory[node_id] = [dest]\n            if self.nested_groups:\n                self.push_group(self.inventory, 'instances', node_id)\n\n        # Inventory: Group by region\n        if self.group_by_region:\n            self.push(self.inventory, region, dest)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'regions', region)\n\n        # Inventory: Group by availability zone\n        if self.group_by_availability_zone:\n            self.push(self.inventory, cluster['PreferredAvailabilityZone'], dest)\n            if self.nested_groups:\n                if self.group_by_region:\n                    self.push_group(self.inventory, region, cluster['PreferredAvailabilityZone'])\n                self.push_group(self.inventory, 'zones', cluster['PreferredAvailabilityZone'])\n\n        # Inventory: Group by node type\n        if self.group_by_instance_type:\n            type_name = self.to_safe('type_' + cluster['CacheNodeType'])\n            self.push(self.inventory, type_name, dest)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'types', type_name)\n\n        # Inventory: Group by VPC (information not available in the current\n        # AWS API version for ElastiCache)\n\n        # Inventory: Group by security group\n        if self.group_by_security_group:\n\n            # Check for the existence of the 'SecurityGroups' key and also if\n            # this key has some value. When the cluster is not placed in a SG\n            # the query can return None here and cause an error.\n            if 'SecurityGroups' in cluster and cluster['SecurityGroups'] is not None:\n                for security_group in cluster['SecurityGroups']:\n                    key = self.to_safe(\"security_group_\" + security_group['SecurityGroupId'])\n                    self.push(self.inventory, key, dest)\n                    if self.nested_groups:\n                        self.push_group(self.inventory, 'security_groups', key)\n\n        # Inventory: Group by engine\n        if self.group_by_elasticache_engine:\n            self.push(self.inventory, self.to_safe(\"elasticache_\" + cluster['Engine']), dest)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'elasticache_engines', self.to_safe(\"elasticache_\" + cluster['Engine']))\n\n        # Inventory: Group by parameter group (done at cluster level)\n\n        # Inventory: Group by replication group (done at cluster level)\n\n        # Inventory: Group by ElastiCache Cluster\n        if self.group_by_elasticache_cluster:\n            self.push(self.inventory, self.to_safe(\"elasticache_cluster_\" + cluster['CacheClusterId']), dest)\n\n        # Global Tag: all ElastiCache nodes\n        self.push(self.inventory, 'elasticache_nodes', dest)\n\n        host_info = self.get_host_info_dict_from_describe_dict(node)\n\n        if dest in self.inventory[\"_meta\"][\"hostvars\"]:\n            self.inventory[\"_meta\"][\"hostvars\"][dest].update(host_info)\n        else:\n            self.inventory[\"_meta\"][\"hostvars\"][dest] = host_info\n\n    def add_elasticache_replication_group(self, replication_group, region):\n        ''' Adds an ElastiCache replication group to the inventory and index '''\n\n        # Only want available clusters unless all_elasticache_replication_groups is True\n        if not self.all_elasticache_replication_groups and replication_group['Status'] != 'available':\n            return\n\n        # Select the best destination address (PrimaryEndpoint)\n        dest = replication_group['NodeGroups'][0]['PrimaryEndpoint']['Address']\n\n        if not dest:\n            # Skip clusters we cannot address (e.g. private VPC subnet)\n            return\n\n        # Add to index\n        self.index[dest] = [region, replication_group['ReplicationGroupId']]\n\n        # Inventory: Group by ID (always a group of 1)\n        if self.group_by_instance_id:\n            self.inventory[replication_group['ReplicationGroupId']] = [dest]\n            if self.nested_groups:\n                self.push_group(self.inventory, 'instances', replication_group['ReplicationGroupId'])\n\n        # Inventory: Group by region\n        if self.group_by_region:\n            self.push(self.inventory, region, dest)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'regions', region)\n\n        # Inventory: Group by availability zone (doesn't apply to replication groups)\n\n        # Inventory: Group by node type (doesn't apply to replication groups)\n\n        # Inventory: Group by VPC (information not available in the current\n        # AWS API version for replication groups\n\n        # Inventory: Group by security group (doesn't apply to replication groups)\n        # Check this value in cluster level\n\n        # Inventory: Group by engine (replication groups are always Redis)\n        if self.group_by_elasticache_engine:\n            self.push(self.inventory, 'elasticache_redis', dest)\n            if self.nested_groups:\n                self.push_group(self.inventory, 'elasticache_engines', 'redis')\n\n        # Global Tag: all ElastiCache clusters\n        self.push(self.inventory, 'elasticache_replication_groups', replication_group['ReplicationGroupId'])\n\n        host_info = self.get_host_info_dict_from_describe_dict(replication_group)\n\n        self.inventory[\"_meta\"][\"hostvars\"][dest] = host_info\n\n    def get_route53_records(self):\n        ''' Get and store the map of resource records to domain names that\n        point to them. '''\n\n        r53_conn = route53.Route53Connection()\n        all_zones = r53_conn.get_zones()\n\n        route53_zones = [ zone for zone in all_zones if zone.name[:-1]\n                          not in self.route53_excluded_zones ]\n\n        self.route53_records = {}\n\n        for zone in route53_zones:\n            rrsets = r53_conn.get_all_rrsets(zone.id)\n\n            for record_set in rrsets:\n                record_name = record_set.name\n\n                if record_name.endswith('.'):\n                    record_name = record_name[:-1]\n\n                for resource in record_set.resource_records:\n                    self.route53_records.setdefault(resource, set())\n                    self.route53_records[resource].add(record_name)\n\n\n    def get_instance_route53_names(self, instance):\n        ''' Check if an instance is referenced in the records we have from\n        Route53. If it is, return the list of domain names pointing to said\n        instance. If nothing points to it, return an empty list. '''\n\n        instance_attributes = [ 'public_dns_name', 'private_dns_name',\n                                'ip_address', 'private_ip_address' ]\n\n        name_list = set()\n\n        for attrib in instance_attributes:\n            try:\n                value = getattr(instance, attrib)\n            except AttributeError:\n                continue\n\n            if value in self.route53_records:\n                name_list.update(self.route53_records[value])\n\n        return list(name_list)\n\n    def get_host_info_dict_from_instance(self, instance):\n        instance_vars = {}\n        for key in vars(instance):\n            value = getattr(instance, key)\n            key = self.to_safe('ec2_' + key)\n\n            # Handle complex types\n            # state/previous_state changed to properties in boto in https://github.com/boto/boto/commit/a23c379837f698212252720d2af8dec0325c9518\n            if key == 'ec2__state':\n                instance_vars['ec2_state'] = instance.state or ''\n                instance_vars['ec2_state_code'] = instance.state_code\n            elif key == 'ec2__previous_state':\n                instance_vars['ec2_previous_state'] = instance.previous_state or ''\n                instance_vars['ec2_previous_state_code'] = instance.previous_state_code\n            elif type(value) in [int, bool]:\n                instance_vars[key] = value\n            elif isinstance(value, six.string_types):\n                instance_vars[key] = value.strip()\n            elif type(value) == type(None):\n                instance_vars[key] = ''\n            elif key == 'ec2_region':\n                instance_vars[key] = value.name\n            elif key == 'ec2__placement':\n                instance_vars['ec2_placement'] = value.zone\n            elif key == 'ec2_tags':\n                for k, v in value.items():\n                    if self.expand_csv_tags and ',' in v:\n                        v = map(lambda x: x.strip(), v.split(','))\n                    key = self.to_safe('ec2_tag_' + k)\n                    instance_vars[key] = v\n            elif key == 'ec2_groups':\n                group_ids = []\n                group_names = []\n                for group in value:\n                    group_ids.append(group.id)\n                    group_names.append(group.name)\n                instance_vars[\"ec2_security_group_ids\"] = ','.join([str(i) for i in group_ids])\n                instance_vars[\"ec2_security_group_names\"] = ','.join([str(i) for i in group_names])\n            else:\n                pass\n                # TODO Product codes if someone finds them useful\n                #print key\n                #print type(value)\n                #print value\n\n        return instance_vars\n\n    def get_host_info_dict_from_describe_dict(self, describe_dict):\n        ''' Parses the dictionary returned by the API call into a flat list\n            of parameters. This method should be used only when 'describe' is\n            used directly because Boto doesn't provide specific classes. '''\n\n        # I really don't agree with prefixing everything with 'ec2'\n        # because EC2, RDS and ElastiCache are different services.\n        # I'm just following the pattern used until now to not break any\n        # compatibility.\n\n        host_info = {}\n        for key in describe_dict:\n            value = describe_dict[key]\n            key = self.to_safe('ec2_' + self.uncammelize(key))\n\n            # Handle complex types\n\n            # Target: Memcached Cache Clusters\n            if key == 'ec2_configuration_endpoint' and value:\n                host_info['ec2_configuration_endpoint_address'] = value['Address']\n                host_info['ec2_configuration_endpoint_port'] = value['Port']\n\n            # Target: Cache Nodes and Redis Cache Clusters (single node)\n            if key == 'ec2_endpoint' and value:\n                host_info['ec2_endpoint_address'] = value['Address']\n                host_info['ec2_endpoint_port'] = value['Port']\n\n            # Target: Redis Replication Groups\n            if key == 'ec2_node_groups' and value:\n                host_info['ec2_endpoint_address'] = value[0]['PrimaryEndpoint']['Address']\n                host_info['ec2_endpoint_port'] = value[0]['PrimaryEndpoint']['Port']\n                replica_count = 0\n                for node in value[0]['NodeGroupMembers']:\n                    if node['CurrentRole'] == 'primary':\n                        host_info['ec2_primary_cluster_address'] = node['ReadEndpoint']['Address']\n                        host_info['ec2_primary_cluster_port'] = node['ReadEndpoint']['Port']\n                        host_info['ec2_primary_cluster_id'] = node['CacheClusterId']\n                    elif node['CurrentRole'] == 'replica':\n                        host_info['ec2_replica_cluster_address_'+ str(replica_count)] = node['ReadEndpoint']['Address']\n                        host_info['ec2_replica_cluster_port_'+ str(replica_count)] = node['ReadEndpoint']['Port']\n                        host_info['ec2_replica_cluster_id_'+ str(replica_count)] = node['CacheClusterId']\n                        replica_count += 1\n\n            # Target: Redis Replication Groups\n            if key == 'ec2_member_clusters' and value:\n                host_info['ec2_member_clusters'] = ','.join([str(i) for i in value])\n\n            # Target: All Cache Clusters\n            elif key == 'ec2_cache_parameter_group':\n                host_info[\"ec2_cache_node_ids_to_reboot\"] = ','.join([str(i) for i in value['CacheNodeIdsToReboot']])\n                host_info['ec2_cache_parameter_group_name'] = value['CacheParameterGroupName']\n                host_info['ec2_cache_parameter_apply_status'] = value['ParameterApplyStatus']\n\n            # Target: Almost everything\n            elif key == 'ec2_security_groups':\n\n                # Skip if SecurityGroups is None\n                # (it is possible to have the key defined but no value in it).\n                if value is not None:\n                    sg_ids = []\n                    for sg in value:\n                        sg_ids.append(sg['SecurityGroupId'])\n                    host_info[\"ec2_security_group_ids\"] = ','.join([str(i) for i in sg_ids])\n\n            # Target: Everything\n            # Preserve booleans and integers\n            elif type(value) in [int, bool]:\n                host_info[key] = value\n\n            # Target: Everything\n            # Sanitize string values\n            elif isinstance(value, six.string_types):\n                host_info[key] = value.strip()\n\n            # Target: Everything\n            # Replace None by an empty string\n            elif type(value) == type(None):\n                host_info[key] = ''\n\n            else:\n                # Remove non-processed complex types\n                pass\n\n        return host_info\n\n    def get_host_info(self):\n        ''' Get variables about a specific host '''\n\n        if len(self.index) == 0:\n            # Need to load index from cache\n            self.load_index_from_cache()\n\n        if not self.args.host in self.index:\n            # try updating the cache\n            self.do_api_calls_update_cache()\n            if not self.args.host in self.index:\n                # host might not exist anymore\n                return self.json_format_dict({}, True)\n\n        (region, instance_id) = self.index[self.args.host]\n\n        instance = self.get_instance(region, instance_id)\n        return self.json_format_dict(self.get_host_info_dict_from_instance(instance), True)\n\n    def push(self, my_dict, key, element):\n        ''' Push an element onto an array that may not have been defined in\n        the dict '''\n        group_info = my_dict.setdefault(key, [])\n        if isinstance(group_info, dict):\n            host_list = group_info.setdefault('hosts', [])\n            host_list.append(element)\n        else:\n            group_info.append(element)\n\n    def push_group(self, my_dict, key, element):\n        ''' Push a group as a child of another group. '''\n        parent_group = my_dict.setdefault(key, {})\n        if not isinstance(parent_group, dict):\n            parent_group = my_dict[key] = {'hosts': parent_group}\n        child_groups = parent_group.setdefault('children', [])\n        if element not in child_groups:\n            child_groups.append(element)\n\n    def get_inventory_from_cache(self):\n        ''' Reads the inventory from the cache file and returns it as a JSON\n        object '''\n\n        cache = open(self.cache_path_cache, 'r')\n        json_inventory = cache.read()\n        return json_inventory\n\n\n    def load_index_from_cache(self):\n        ''' Reads the index from the cache file sets self.index '''\n\n        cache = open(self.cache_path_index, 'r')\n        json_index = cache.read()\n        self.index = json.loads(json_index)\n\n\n    def write_to_cache(self, data, filename):\n        ''' Writes data in JSON format to a file '''\n\n        json_data = self.json_format_dict(data, True)\n        cache = open(filename, 'w')\n        cache.write(json_data)\n        cache.close()\n\n    def uncammelize(self, key):\n        temp = re.sub('(.)([A-Z][a-z]+)', r'\\1_\\2', key)\n        return re.sub('([a-z0-9])([A-Z])', r'\\1_\\2', temp).lower()\n\n    def to_safe(self, word):\n        ''' Converts 'bad' characters in a string to underscores so they can be used as Ansible groups '''\n        regex = \"[^A-Za-z0-9\\_\"\n        if not self.replace_dash_in_groups:\n            regex += \"\\-\"\n        return re.sub(regex + \"]\", \"_\", word)\n\n    def json_format_dict(self, data, pretty=False):\n        ''' Converts a dict to a JSON object and dumps it as a formatted\n        string '''\n\n        if pretty:\n            return json.dumps(data, sort_keys=True, indent=2)\n        else:\n            return json.dumps(data)\n\n\n# Run the script\nEc2Inventory()\n\n"
  },
  {
    "path": "tools/aws_lab_setup/inventory/group_vars/all.yml",
    "content": "ssh_port: 22\n\nusers:\n  - name: Brue Wayne\n    username: bwayne\n    email: bwayne@we.com\n\nadmin_password: LearnAnsible{{ lookup('pipe', 'date +%m%y')}}\nadmin_password_hash: \"{{ admin_password | password_hash(salt='sRvXWmR5BBwqRlih') }}\"\n"
  },
  {
    "path": "tools/aws_lab_setup/provision_lab.yml",
    "content": "- name: Perform Checks to make sure this Playbook will complete successfully\n  hosts: localhost\n  connection: local\n  become: no\n  gather_facts: no\n  tasks:\n    - name: make sure we are running correct Ansible Version\n      fail:\n        msg: \"Ansible version must be at least => 2.3.2\"\n      when: (ansible_version.major == 1) or\n            (ansible_version.major == 2 and ansible_version.minor < 3) or\n            (ansible_version.major == 2 and ansible_version.minor == 3 and ansible_version.revision < 2)\n\n    - name: save username of AWS user\n      set_fact:\n         lightbulb_user: '{{ ec2_key_name }}'\n\n- name: Create lab instances in AWS\n  hosts: localhost\n  connection: local\n  become: no\n  gather_facts: no\n  vars:\n    teardown: false\n  roles:\n    - manage_ec2_instances\n\n- name: Configure common options on managed nodes and control nodes\n  hosts: \"managed_nodes:control_nodes\"\n  become: yes\n  roles:\n    - user_accounts\n    - common\n\n- name: Configure control node\n  hosts: control_nodes\n  become: yes\n  roles:\n    - control_node\n\n- name: Email inventory to students\n  hosts: localhost\n  connection: local\n  become: no\n  gather_facts: no\n  roles:\n    - email\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/common/defaults/main.yml",
    "content": "common_node_config_options:\n  - dest: /etc/ssh/sshd_config\n    regexp: '^#?Port'\n    line: Port {{ ssh_port | default('22') }}\n    validate: sshd -t -f %s\n\n  - dest: /etc/ssh/sshd_config\n    regexp: '^#?PasswordAuthentication'\n    line: 'PasswordAuthentication yes'\n    validate: sshd -t -f %s\n\n  - dest: /etc/ssh/sshd_config\n    regexp: '^#?UseDNS'\n    line: 'UseDNS no'\n    validate: sshd -t -f %s\n\n  - dest: /etc/sudoers\n    regexp: '^{{ username }}'\n    line: '{{ username }} ALL=(ALL) NOPASSWD: ALL'\n    validate: visudo -cf %s\n\n  - dest: /etc/sudoers\n    regexp: '^Defaults.*requiretty'\n    line: 'Defaults    !requiretty'\n    validate: visudo -cf %s\n\nfirewall_rules:\n  - service: http\n  - service: https\n  - service: ssh\n  - port: 5432\n    protocol: udp\n  - port: 5672\n    protocol: udp\n  - port: 5432\n    protocol: tcp\n  - port: 5672\n    protocol: tcp\n\nfirewalld_service: firewalld\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/common/handlers/main.yml",
    "content": "- name: restart sshd\n  service:\n    name: \"{{ common_ssh_service_name }}\"\n    state: restarted\n\n- name: Reboot\n  shell: sleep 2 && reboot\n  async: 30\n  poll: 0\n\n- name: Wait for instance\n  delegate_to: localhost\n  become: no\n  wait_for:\n    port: \"{{ ssh_port | default('22') }}\"\n    host: \"{{ ansible_host }}\"\n    search_regex: OpenSSH\n    timeout: 500\n    delay: 40\n\n- name: restart firewalld\n  service:\n    name: \"{{ firewalld_service }}\"\n    state: restarted\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/common/tasks/RedHat.yml",
    "content": "- name: RHEL | Set version specific variables\n  include_vars: \"{{ ansible_os_family }}-{{ ansible_distribution_major_version }}.yml\"\n  tags:\n    - always\n\n- name: RHEL | Install {{ firewall_name }} and libselinux-python\n  yum:\n    name:\n      - libselinux-python\n      - \"{{ firewall_name }}\"\n    state: present\n  tags:\n    - common\n\n- name: RHEL | Start firewalld\n  service:\n    name: \"{{ firewalld_service }}\"\n    state: started\n\n- name: RHEL | Enable specific firewall services\n  firewalld:\n    immediate: yes\n    permanent: yes\n    port: \"{{ item.port ~ '/' ~ item.protocol if (item.port is defined and item.protocol is defined) else omit }}\"\n    service: \"{{ item.service | default(omit) }}\"\n    state: enabled\n  with_items: \"{{ firewall_rules }}\"\n  notify: restart firewalld\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/common/tasks/Ubuntu.yml",
    "content": "- name: UBUNTU | Set version specific variables\n  include_vars: \"{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml\"\n  tags:\n    - always\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/common/tasks/main.yml",
    "content": "- name: Include Red Hat tasks\n  include: \"{{ ansible_os_family }}.yml\"\n  static: no\n  when: ansible_os_family == 'RedHat'\n\n- name: Include Ubuntu tasks\n  include: \"{{ ansible_distribution }}.yml\"\n  static: no\n  when: ansible_distribution == 'Ubuntu'\n\n- name: Configure sshd and sudoers\n  lineinfile:\n    dest: \"{{ item.dest }}\"\n    regexp: \"{{ item.regexp }}\"\n    line: \"{{ item.line }}\"\n    state: \"{{ item.state | default('present') }}\"\n    validate: \"{{ item.validate | default(omit) }}\"\n    backup: no\n  with_items: \"{{ common_node_config_options }}\"\n  notify: restart sshd\n  tags:\n    - ssh\n    - sudo\n    - common\n\n- meta: flush_handlers\n  tags:\n    - common\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/common/vars/RedHat-6.yml",
    "content": "firewall_name: iptables\ncommon_ssh_service_name: sshd\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/common/vars/RedHat-7.yml",
    "content": "firewall_name: firewalld\ncommon_ssh_service_name: sshd\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/common/vars/Ubuntu-14.yml",
    "content": "common_ssh_service_name: ssh\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/common/vars/Ubuntu-16.yml",
    "content": "common_ssh_service_name: ssh\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/control_node/defaults/main.yml",
    "content": "control_node_inventory_path: ~{{ username }}/lightbulb/lessons/lab_inventory/{{ username }}-instances.txt\ncontrol_node_install_ansible: yes\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/control_node/tasks/main.yml",
    "content": "- name: Install EPEL\n  yum:\n    name: \"https://dl.fedoraproject.org/pub/epel/epel-release-latest-{{ ansible_distribution_major_version }}.noarch.rpm\"\n    state: present\n  tags:\n    - control_node\n    - control\n\n- name: Install base packages\n  yum:\n    name:\n      - vim\n      - git\n      - wget\n      - nano\n\n    state: latest\n    enablerepo: epel-testing\n  tags:\n    - control_node\n    - control\n\n- name: Install Ansible\n  yum:\n    name:\n      - sshpass\n      - ansible\n    state: latest\n    enablerepo: epel-testing\n  tags:\n    - control_node\n    - control\n  when: control_node_install_ansible\n\n\n- name: Clone lightbulb\n  git:\n    accept_hostkey: yes\n    clone: yes\n    dest: ~{{ username }}/lightbulb\n    repo: https://github.com/ansible/lightbulb.git\n    force: yes\n  become_user: \"{{ username }}\"\n  tags:\n    - control_node\n    - control\n\n- name: Remove things that students don't need\n  file:\n    state: absent\n    path: ~{{ username }}/lightbulb/{{ item }}\n  with_items:\n    - aws_lab_setup\n    - resources\n    - Vagrantfile\n    - README.md\n  tags:\n    - control_node\n    - control\n\n- name: Install ansible.cfg and vimrc in home directory\n  template:\n    src: \"{{ item }}\"\n    dest: ~{{ username }}/.{{ (item | splitext)[0] }}\n    owner: \"{{ username }}\"\n    group: \"{{ username }}\"\n  with_items:\n    - ansible.cfg.j2\n    - vimrc.j2\n  tags:\n    - control_node\n    - control\n    - vim\n\n- name: Create lab inventory directory\n  file:\n    state: directory\n    path: /home/{{ username }}/lightbulb/lessons/lab_inventory\n  tags:\n    - control_node\n    - control\n\n- name: Put student inventory in proper spot\n  copy:\n    src: ./{{ username }}-instances.txt\n    dest: \"{{ control_node_inventory_path }}\"\n    owner: \"{{ username }}\"\n    group: \"{{ username }}\"\n  when: username in inventory_hostname\n  tags:\n    - control_node\n    - control\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/control_node/templates/ansible.cfg.j2",
    "content": "[defaults]\nconnection = smart\ntimeout = 60\ninventory = {{ control_node_inventory_path }}\nhost_key_checking = False\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/control_node/templates/vimrc.j2",
    "content": "set autoindent\nset tabstop=2\nset shiftwidth=2\nautocmd FileType yaml setlocal ai ts=2 sw=2 et\nautocmd FileType yml setlocal ai ts=2 sw=2 et\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/email/defaults/main.yml",
    "content": "# sendgrid_user:\n# sendgrid_pass:\n# sendgrid_api_key:\nemail: yes\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/email/tasks/main.yml",
    "content": "- name: Send email to students with inventory attached\n  delegate_to: localhost\n  sendgrid:\n    username: \"{{ sendgrid_user | default(omit) }}\"\n    password: \"{{ sendgrid_pass | default(omit) }}\"\n    api_key: \"{{ sendgrid_api_key | default(omit) }}\"\n    subject: \"[Ansible] Important Training Details\"\n    body: |\n          Attached is the Ansible inventory to be used for training.<br>\n          Please check your ability to connect to each of the hosts via ssh.<br>\n          <br>\n          The username is '{{ item.username }}' and the password is '{{ admin_password }}'.<br>\n          If you have any issues connecting, please reply to this email to let me know.<br>\n\n    to_addresses: \"{{ item.email }}\"\n    html_body: yes\n    from_address: \"{{ instructor_email }}\"\n    attachments:\n      - \"{{ item.username }}-instances.txt\"\n  with_items: \"{{ users }}\"\n  when: email\n  tags:\n    - email\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/manage_ec2_instances/defaults/main.yml",
    "content": "ec2_name_prefix: TRAINING\nec2_region: us-east-1\nec2_exact_count: 1\nec2_wait: yes\nec2_subnet: \"172.16.0.0/16\"\nec2_subnet2: \"172.17.0.0/16\"\n\nec2_lab_node_types:\n  - name: ansible\n    type: rhel7-tower\n\n  - name: node1\n    type: rhel7\n\n  - name: node2\n    type: rhel7\n\n  - name: node3\n    type: rhel7\n\n#  - name: haproxy\n#    type: rhel7\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/manage_ec2_instances/tasks/create.yml",
    "content": "- name: Wait for instances to finish initializing\n  pause:\n    seconds: 90\n  when: instances is changed\n  tags:\n    - always\n    - provision\n\n- name: Add hosts to control_nodes group\n  add_host:\n    name: \"{{ item.invocation.module_args.instance_tags.Name }}\"\n    ansible_host: \"{{ item.tagged_instances[0].public_ip }}\"\n    ansible_user: \"{{ ec2_login_names[item.item.1.type] }}\"\n    ansible_port: \"{{ ssh_port | default('22') }}\"\n    groups: lab_hosts,control_nodes\n  with_items: \"{{ instances.results }}\"\n  when: \"'ansible' in item.invocation.module_args.instance_tags.Name\"\n  changed_when: no\n  tags:\n    - always\n    - provision\n\n- name: Add hosts to groups\n  add_host:\n    name: \"{{ item.invocation.module_args.instance_tags.Name }}\"\n    ansible_host: \"{{ item.tagged_instances[0].public_ip }}\"\n    ansible_user: \"{{ ec2_login_names[item.item.1.type] }}\"\n    ansible_port: \"{{ ssh_port | default('22') }}\"\n    groups: lab_hosts,managed_nodes\n  with_items: \"{{ instances.results }}\"\n  when: \"'ansible' not in item.invocation.module_args.instance_tags.Name\"\n  changed_when: no\n  tags:\n    - always\n    - provision\n\n- name: Wait for Ansible connection to all hosts\n  wait_for_connection:\n    delay: 0\n    timeout: 400\n  when: instances is changed\n  delegate_to: \"{{ item }}\"\n  loop: \"{{ groups.lab_hosts }}\"\n\n- name: Set local username to create on instances\n  set_fact:\n    username: \"{{ item | regex_replace('.*-(\\\\w*)-\\\\w*$','\\\\1') }}\"\n  with_items: \"{{ groups.lab_hosts }}\"\n  delegate_to: \"{{ item }}\"\n  delegate_facts: yes\n  tags:\n    - always\n    - provision\n\n- name: Generate student inventories\n  template:\n    src: instances.txt.j2\n    dest: ./{{ item.username }}-instances.txt\n  with_items: \"{{ users }}\"\n  tags:\n    - inventory\n    - users\n    - user_accounts\n\n- name: Generate instructor inventory\n  template:\n    src: instructor_inventory.j2\n    dest: ./instructor_inventory.txt\n  tags:\n    - inventory\n    - users\n    - user_accounts\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/manage_ec2_instances/tasks/main.yml",
    "content": "- include_tasks: teardown.yml\n  when: teardown\n\n- include_tasks: provision.yml\n  when: not teardown\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/manage_ec2_instances/tasks/provision.yml",
    "content": "- block:\n  - name: Set EC2 security group type\n    set_fact:\n      ec2_security_group: insecure_all\n\n  - name: Create EC2 security group\n    ec2_group:\n      name: \"{{ ec2_security_group }}\"\n      description: all ports open\n      region: \"{{ ec2_region }}\"\n      vpc_id: \"{{ ec2_vpc_id }}\"\n      rules:\n        - proto: all\n          cidr_ip: 0.0.0.0/0\n      rules_egress:\n        - proto: all\n          cidr_ip: 0.0.0.0/0\n    when: ec2_exact_count >= 1\n    tags:\n      - always\n      - provision\n  when: ec2_vpc_id is defined\n\n- block: \n  - name: Create AWS VPC {{ ec2_name_prefix }}-vpc\n    ec2_vpc_net:\n      name: \"{{ ec2_name_prefix }}-vpc\"\n      cidr_block: \"{{ec2_subnet}}\"\n      region: \"{{ ec2_region }}\"\n      tags:\n        Username: \"{{ lightbulb_user }}\"\n        Info: \"Username that provisioned this-> {{ lightbulb_user }}\"\n        Lightbulb: \"This was provisioned through the lightbulb provisioner\"\n    register: create_vpc\n    until: create_vpc is not failed\n    retries: 5\n    tags:\n      - provision\n\n  - name: Create EC2 security group for VPC named {{ ec2_name_prefix }}-vpc\n    ec2_group:\n      name: \"{{ ec2_name_prefix }}-insecure_all\"\n      description: all ports open\n      region: \"{{ ec2_region }}\"\n      vpc_id: \"{{create_vpc.vpc.id}}\"\n      tags:\n        Username: \"{{ lightbulb_user }}\"\n        Info: \"Username that provisioned this-> {{ lightbulb_user }}\"\n        Lightbulb: \"This was provisioned through the lightbulb provisioner\"\n      rules:\n        - proto: 47\n          to_port: -1\n          from_port: -1\n          cidr_ip: 0.0.0.0/0\n        - proto: tcp \n          to_port: 443 \n          from_port: 443 \n          cidr_ip: 0.0.0.0/0\n        - proto: icmp\n          to_port: -1\n          from_port: -1\n          cidr_ip: 0.0.0.0/0\n        - proto: tcp\n          to_port: 80\n          from_port: 80\n          cidr_ip: 0.0.0.0/0\n        - proto: tcp\n          to_port: 22\n          from_port: 22\n          cidr_ip: 0.0.0.0/0\n      rules_egress:\n        - proto: all\n          cidr_ip: 0.0.0.0/0\n    when: ec2_exact_count >= 1\n    tags:\n      - provision\n\n  - name: Create subnet for {{ ec2_name_prefix }}-vpc\n    ec2_vpc_subnet:\n      region: \"{{ ec2_region }}\"\n      az: \"{{ ec2_az }}\"\n      vpc_id: \"{{ create_vpc.vpc.id }}\"\n      cidr: \"{{ ec2_subnet }}\"\n      wait_timeout: 600\n      resource_tags:\n        Name: \"{{ ec2_name_prefix }}-subnet\"\n        Username: \"{{ lightbulb_user }}\"\n        Info: \"Username that provisioned this-> {{ lightbulb_user }}\"\n        Lightbulb: \"This was provisioned through the lightbulb provisioner\"\n    register: create_subnet\n    until: create_subnet is not failed\n    retries: 15\n    tags:\n      - provision\n\n  - name: VPC internet gateway is present for {{ create_vpc.vpc.id }}\n    ec2_vpc_igw:\n      region: \"{{ ec2_region }}\"\n      vpc_id: \"{{ create_vpc.vpc.id }}\"\n      tags:\n        Username: \"{{ lightbulb_user }}\"\n        Info: \"Username that provisioned this-> {{ lightbulb_user }}\"\n        lightbulb: \"This was provisioned through the lightbulb provisioner\"\n    register: igw\n    tags:\n      - provision\n\n  - name: VPC public subnet route table is present for {{ create_vpc.vpc.id }}\n    ec2_vpc_route_table:\n      region: \"{{ ec2_region }}\"\n      vpc_id: \"{{ create_vpc.vpc.id }}\"\n      subnets:\n        - \"{{ create_subnet.subnet.id }}\"\n      routes:\n        - dest: 0.0.0.0/0\n          gateway_id: \"{{ igw.gateway_id }}\"\n      tags:\n        Username: \"{{ lightbulb_user }}\"\n        Info: \"Username that provisioned this-> {{ lightbulb_user }}\"\n        Lightbulb: \"This was provisioned through the lightbulb provisioner\"\n    register: routetable\n    until: routetable is not failed\n    retries: 5\n    tags:\n      - provision\n\n  - name: Set variables for instance creation dynamically since VPC was not supplied by user\n    set_fact:\n      ec2_vpc_id: \"{{ create_vpc.vpc.id }}\"\n      ec2_security_group: \"{{ ec2_name_prefix }}-insecure_all\"\n      ec2_vpc_subnet_id: \"{{ create_subnet.subnet.id }}\"\n    tags:\n      - provision\n  when: ec2_vpc_id is undefined\n\n- name: Create EC2 instances\n  ec2:\n    assign_public_ip: yes\n    key_name: \"{{ ec2_key_name }}\"\n    group: \"{{ ec2_security_group }}\"\n    instance_type: \"{{ ec2_instance_types[item.1.type].size }}\"\n    image: \"{{ ec2_instance_types[item.1.type].ami_id }}\"\n    region: \"{{ ec2_region }}\"\n    exact_count: \"{{ ec2_exact_count }}\"\n    count_tag:\n      Name: \"{{ ec2_name_prefix }}-{{ item.0.username }}-{{ item.1.name }}\"\n    instance_tags:\n      Name: \"{{ ec2_name_prefix }}-{{ item.0.username }}-{{ item.1.name }}\"\n      Workshop: \"{{ec2_name_prefix}}\"\n      Username: \"{{ lightbulb_user }}\"\n      Info: \"Username that provisioned this-> {{ lightbulb_user }}\"\n      Linklight: \"This was provisioned through the lightbulb provisioner\"\n    wait: \"{{ ec2_wait }}\"\n    vpc_subnet_id: \"{{ ec2_vpc_subnet_id | default(omit) }}\"\n    volumes:\n      - device_name: /dev/sda1\n        volume_type: gp2\n        volume_size: \"{{ ec2_instance_types[item.1.type].disk_space }}\"\n        delete_on_termination: true\n  with_nested:\n    - \"{{ users }}\"\n    - \"{{ ec2_lab_node_types }}\"\n  register: instances\n  tags:\n    - always\n    - provision\n\n- name: Include tasks only needed when creating instances\n  include_tasks: create.yml\n  when: ec2_exact_count >= 1\n  tags:\n    - provision\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/manage_ec2_instances/tasks/teardown.yml",
    "content": "- name: grab facts for workshop\n  ec2_instance_facts:\n    region: \"{{ ec2_region }}\"\n    filters:\n      instance-state-name: running\n      \"tag:Workshop\": \"{{ec2_name_prefix}}\"\n  register: all_workshop_nodes\n\n- name: Destroy EC2 instances\n  ec2:\n    region: \"{{ ec2_region }}\"\n    state: absent\n    instance_ids: \"{{ all_workshop_nodes.instances | map(attribute='instance_id') | list }}\"\n  register: result\n  when: all_workshop_nodes.instances\n\n- name: Get the VPC ID for {{ ec2_name_prefix }}\n  ec2_vpc_net_facts:\n    filters:\n      \"tag:Name\": \"{{ ec2_name_prefix }}-vpc\"\n    region: \"{{ ec2_region }}\"\n  register: vpc_net_facts\n\n- name: set variables for instance creation dynamically since VPC was not supplied by user\n  set_fact:\n    ec2_vpc_id: \"{{vpc_net_facts.vpcs[0].id}}\"\n    ec2_security_group: \"{{ ec2_name_prefix }}-insecure_all\"\n  when: vpc_net_facts.vpcs|length > 0 and ec2_security_group is undefined\n\n- name: Deleted EC2 security group for VPC vpc-{{ ec2_name_prefix }}\n  ec2_group:\n    name: \"{{ec2_security_group}}\"\n    region: \"{{ ec2_region }}\"\n    vpc_id: \"{{ec2_vpc_id}}\"\n    state: absent\n  register: delete_sg\n  until: delete_sg is not failed\n  retries: 50\n  when: vpc_net_facts.vpcs|length > 0\n\n- name: Delete subnet for {{ ec2_name_prefix }}-vpc\n  ec2_vpc_subnet:\n    region: \"{{ ec2_region }}\"\n    az: \"{{ec2_az}}\"\n    vpc_id: \"{{ec2_vpc_id}}\"\n    cidr: \"{{ec2_subnet}}\"\n    state: absent\n  when: vpc_net_facts.vpcs|length > 0\n\n- name: Ensure vpc internet gateway is deleted for vpc-{{ ec2_name_prefix }}\n  ec2_vpc_igw:\n    region: \"{{ ec2_region }}\"\n    vpc_id: \"{{ec2_vpc_id}}\"\n    state: absent\n  when: vpc_net_facts.vpcs|length > 0\n\n- name: Grab route information for {{ ec2_name_prefix }} on {{ ec2_region }}\n  ec2_vpc_route_table_facts:\n    region: \"{{ ec2_region }}\"\n    filters:\n      vpc_id: \"{{ec2_vpc_id}}\"\n  register: route_table_facts\n  when: vpc_net_facts.vpcs|length > 0\n\n- name: VPC public subnet route table is deleted\n  ec2_vpc_route_table:\n    region: \"{{ ec2_region }}\"\n    vpc_id: \"{{ec2_vpc_id}}\"\n    route_table_id: \"{{item.id}}\"\n    lookup: id\n    state: absent\n  with_items: \"{{route_table_facts.route_tables}}\"\n  when: vpc_net_facts.vpcs|length > 0 and item.associations == []\n\n- name: Delete AWS VPC {{ ec2_name_prefix }}\n  ec2_vpc_net:\n    name: \"{{ ec2_name_prefix }}-vpc\"\n    cidr_block: \"{{ ec2_subnet }}\"\n    region: \"{{ ec2_region }}\"\n    state: absent\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/manage_ec2_instances/templates/instances.txt.j2",
    "content": "[all:vars]\nansible_user={{ item.username }}\nansible_ssh_pass={{ admin_password }}\n{% if ssh_port is defined %}\nansible_port={{ ssh_port }}\n{% endif %}\n\n[web]\n{% for grouper, list in instances.results|groupby('item') if grouper[0] == item %}\n{% for vm in list %}\n{% if 'ansible' not in vm.invocation.module_args.instance_tags.Name and 'haproxy' not in vm.invocation.module_args.instance_tags.Name%}\n{{ vm.invocation.module_args.instance_tags.Name | regex_replace('.*-(node)(\\\\d)', '\\\\1-\\\\2') }} ansible_host={{ vm.tagged_instances[0].public_ip }}\n{% endif %}\n{% endfor %}\n{% endfor %}\n\n[control]\n{% for grouper, list in instances.results|groupby('item') if grouper[0] == item %}\n{% for vm in list %}\n{% if 'ansible' in vm.invocation.module_args.instance_tags.Name %}\n{{ vm.invocation.module_args.instance_tags.Name | regex_replace('.*-([\\\\w]*)', '\\\\1') }} ansible_host={{ vm.tagged_instances[0].public_ip }}\n{% endif %}\n{% endfor %}\n{% endfor %}\n\n{#\n[haproxy]\n{% for grouper, list in instances.results|groupby('item') if grouper[0] == item %}\n{% for vm in list %}\n{% if 'haproxy' in vm.invocation.module_args.instance_tags.Name %}\n{{ vm.invocation.module_args.instance_tags.Name | regex_replace('.*-([\\\\w]*)', '\\\\1') }} ansible_host={{ vm.tagged_instances[0].public_ip }}\n{% endif %}\n{% endfor %}\n{% endfor %}\n#}\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/manage_ec2_instances/vars/main.yml",
    "content": "# --- Lookup tables --- #\n# Region specific AMIs, subnets, and VPCs\nec2_regions:\n  us-east-1:\n    vpc: vpc-06c9a463\n    subnet_id: subnet-a2169ed5\n    key_name: \"{{ ec2_key_name | default('training') }}\"\n    amis:\n      centos7: ami-61bbf104\n      centos7-tower: ami-877b9e91\n      rhel7: ami-85241def\n      rhel7-tower: ami-85241def\n      ubuntu14: ami-feed08e8\n      ubuntu16: ami-9dcfdb8a\n\n  us-west-1:\n    vpc: vpc-ce1f9aab\n    subnet_id: subnet-25b9157c\n    key_name: \"{{ ec2_key_name | default('training') }}\"\n    amis:\n      centos7: ami-af4333cf\n      centos7-tower: ami-c04a00a0\n      rhel7: ami-f88fc498\n      rhel7-tower: ami-f88fc498\n      ubuntu14: ami-6ebcec0e\n      ubuntu16: ami-b05203d0\n\n  eu-west-1:\n    vpc: vpc-48141c2a\n    subnet_id: subnet-70f9f012\n    key_name: \"{{ ec2_key_name | default('training') }}\"\n    amis:\n      centos7: ami-af4333cf\n      centos7-tower: ami-c04a00a0\n      rhel7: ami-2b9d6c52\n      rhel7-tower: ami-2b9d6c52\n      ubuntu14: ami-6ebcec0e\n      ubuntu16: ami-b05203d0\n  \n  ap-southeast-1:\n    vpc: vpc-xxxxxxxx\n    subnet_id: subnet-xxxxxxxx\n    key_name: \"{{ ec2_key_name | default('training') }}\"\n    amis:\n      centos7: ami-7d2eab1e\n      centos7-tower: ami-c7ed91a4\n      rhel7: ami-dccc04bf\n      rhel7-tower: ami-dccc04bf\n      ubuntu14: ami-adb51dce\n      ubuntu16: ami-87b917e4\n\n  ap-southeast-2:\n    vpc: vpc-xxxxxxxxa\n    subnet_id: subnet-xxxxxxxx\n    key_name: \"{{ ec2_key_name | default('training') }}\"\n    amis:\n      centos7: ami-34171d57\n      centos7-tower: ami-01b45763\n      rhel7: ami-286e4f4b\n      rhel7-tower: ami-286e4f4b\n      ubuntu14: ami-940e0bf7\n      ubuntu16: ami-e6b58e85\n\n  ap-south-1:\n    vpc: vpc-xxxxxxxx\n    subnet_id: subnet-xxxxxxxx\n    key_name: \"{{ ec2_key_name | default('training') }}\"\n    amis:\n      centos7: ami-3c0e7353\n      centos7-tower: ami-5b145434\n      rhel7: ami-cdbdd7a2\n      rhel7-tower: ami-cdbdd7a2\n      ubuntu14: ami-533e4e3c\n      ubuntu16: ami-dd3442b2\n\n  ap-northeast-1:\n    vpc: vpc-xxxxxxxx\n    subnet_id: subnet-xxxxxxxx\n    key_name: \"{{ ec2_key_name | default('training') }}\"\n    amis:\n      centos7: ami-29d1e34e\n      centos7-tower: ami-0d05d46b\n      rhel7: ami-a05854ce\n      rhel7-tower: ami-a05854ce\n      ubuntu14: ami-3995e55e\n      ubuntu16: ami-18afc47f\n      \n  cn-north-1:\n    vpc: vpc-xxxxxxxx\n    subnet_id: subnet-xxxxxxxx\n    key_name: \"{{ ec2_key_name | default('training') }}\"\n    amis:\n      centos7: ami-3d805750\n      centos7-tower: ami-3d805750\n      rhel7: ami-52d1183f\n      rhel7-tower: ami-52d1183f\n      ubuntu14: ami-0220b23b\n      ubuntu16: ami-a6fc21cb\n\n# Instance types used for lab configurations\nec2_instance_types:\n  centos7:\n    ami_id: \"{{ ec2_regions[ec2_region].amis.centos7 }}\"\n    size: t2.micro\n    os_type: linux\n    disk_space: 10\n\n  centos7-tower:\n    ami_id: \"{{ ec2_regions[ec2_region].amis['centos7-tower'] }}\"\n    size: t2.medium\n    os_type: linux\n    disk_space: 20\n\n  # Look for owner 309956199498 to find official Red Hat AMIs\n  rhel7:\n    ami_id: \"{{ ec2_regions[ec2_region].amis.rhel7 }}\"\n    size: t2.micro\n    os_type: linux\n    disk_space: 10\n\n  rhel7-tower:\n    ami_id: \"{{ ec2_regions[ec2_region].amis['rhel7-tower'] }}\"\n    size: t2.medium\n    os_type: linux\n    disk_space: 20\n\n  # Look at https://cloud-images.ubuntu.com/locator/ec2/ for Ubuntu AMIs\n  ubuntu14:\n    ami_id: \"{{ ec2_regions[ec2_region].amis.ubuntu14 }}\"\n    size: t2.micro\n    os_type: linux\n    disk_space: 10\n\n  ubuntu16:\n    ami_id: \"{{ ec2_regions[ec2_region].amis.ubuntu16 }}\"\n    size: t2.micro\n    os_type: linux\n    disk_space: 10\n\n# Login names used for SSH connections. These are baked in to the AMIs.\nec2_login_names:\n  rhel7-tower: ec2-user\n  rhel7: ec2-user\n  centos7-tower: centos\n  centos7: centos\n  ubuntu14: ubuntu\n  ubuntu16: ubuntu\n\n\n# Backwards compatability\ntypes: \"{{ ec2_lab_node_types }}\"\naws_key_name: \"{{ ec2_key_name }}\"\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/user_accounts/defaults/main.yml",
    "content": "admin_password: LearnAnsible{{ lookup('pipe', 'date +%m%y')}}\nadmin_password_hash: \"{{ admin_password | password_hash(salt='sRvXWmR5BBwqRlih') }}\"\n"
  },
  {
    "path": "tools/aws_lab_setup/roles/user_accounts/tasks/main.yml",
    "content": "- name: Create User Group\n  group:\n    name: \"{{ username }}\"\n    state: present\n  tags:\n    - user_accounts\n    - users\n\n- name: Create User Account\n  user:\n    createhome: yes\n    group: \"{{ username }}\"\n    name: \"{{ username }}\"\n    shell: /bin/bash\n    state: present\n    password: \"{{ admin_password_hash }}\"\n  tags:\n    - user_accounts\n    - users\n"
  },
  {
    "path": "tools/aws_lab_setup/sample-users.yml",
    "content": "users:\n  - name: Luke Cage\n    username: lcage\n    email: lcage@marvel.com\n"
  },
  {
    "path": "tools/aws_lab_setup/teardown_lab.yml",
    "content": "- name: Destroy lab instances in AWS\n  hosts: localhost\n  connection: local\n  become: no\n  gather_facts: yes\n\n  vars:\n    ec2_exact_count: 0\n    teardown: true\n    ec2_wait: no\n\n  roles:\n    - manage_ec2_instances\n\n  post_tasks:\n    - name: Remove inventory files\n      file:\n        name: \"{{ playbook_dir }}/*.txt\"\n        state: absent\n"
  },
  {
    "path": "tools/inventory_import/README.md",
    "content": "# Static Inventory Importer\n\nThis playbook helps students import their local, static inventory into Ansible Tower.\n\n## Usage\n\nThis playbook users the tower-cli tool and it's associated Ansible modules to create an inventory, groups, and then hosts with a special type of loop: `with_inventory_hostnames`\n\n### Pre-requisites\n\nIn order for this play to work, you must have already completed the following:\n\n* The Ansible Tower install exercise\n* Logged in using the password you set for the admin user\n* Applied your license key\n\n### Setup\n\nAssuming a standard setup of the Lightbulb environment -- meaning you've followed the guides here or an instructors directions -- the only item you need to adjust in `inventory_import.yml` is the value for `tower_host_password` on line 5. This is the same value that you set as your admin accounts password.\n\n### Running the playbook\n\nOnce you've modified the `tower_host_password` value, you can run the playbook normally as your student user:\n\n```yaml\n[student1@~]$ansible-playbook lightbulb/tools/inventory_import/inventory_import.yml\n```\n\nOnce that's done, you can check under the Inventory tab in Ansible Tower and you should now have 2 inventory groups:\n![created inventory](./images/created_inventory.png)\n\n## Next steps\n\nTower-cli does not currently support creating hosts under a group, so they'll need to be moved individually to the appropriate group.\n\nSelect the 'Ansible Lightbulb Lab' inventory, and begin moving the hosts to their respective groups by clicking the 'copy or move icon':\n![move hosts](./images/move_host.png)\n\nChoose the 'web' group for nodes 1-3, and the 'control' group for the control node. The default behavior is to copy the hosts to groups, but you can choose move if desired.\n\nOnce complete, you should be able to look at each group and see their associated hosts. Here's the web group as an example:\n![modified group](./images/modified_group.png)\n"
  },
  {
    "path": "tools/inventory_import/inventory_import.yml",
    "content": "---\n- name: Create an inventory, group, and hosts within Tower for the 'web' group nodes[1:3]\n  hosts: localhost\n  vars:\n    tower_host_password: #Enter your password you setup for 'admin_password' when installing Tower\n    pip_packages:\n      - python-pip\n      - python-devel\n    tower_host_ip: 127.0.0.1 #Since Tower serves on localhost by default, this is sufficient for Lightbulb\n    tower_host_user: admin #Admin is the default user for Lighbulb projects\n    organization: \"Default\" #Default org setup by Tower Install\n    inventory_name: \"Ansible Lightbulb Lab\" #Follows inventory_name in docs for Lightbulb\n    group_name:\n      - \"web\"\n      - \"control\"\n\n  tasks:\n  - name: Install pip and ansible-tower-cli\n    block:\n      - name: Install pip packages\n        yum:\n          name: \"{{ item }}\"\n          state: present\n        with_items: \"{{ pip_packages }}\"\n      - name: Install tower-cli\n        pip:\n          name: ansible-tower-cli\n    become: yes\n\n  - name: Create the Ansible Lightbulb inventory\n    tower_inventory:\n      name: \"{{ inventory_name }}\"\n      description: \"Generic Inventory\"\n      organization: \"{{ organization }}\"\n      tower_host: \"{{ tower_host_ip }}\"\n      tower_username: \"{{ tower_host_user }}\"\n      tower_password: \"{{ tower_host_password }}\"\n      state: present\n\n  - name: Add tower group\n    tower_group:\n      name: \"{{ item }}\"\n      description: \"Generic group\"\n      inventory: \"{{ inventory_name }}\"\n      tower_host: \"{{ tower_host_ip }}\"\n      tower_username: \"{{ tower_host_user }}\"\n      tower_password: \"{{ tower_host_password }}\"\n      state: present\n    with_items: \"{{ group_name }}\"\n\n  - name: Add tower hosts\n    tower_host:\n      name: \"{{item}}\"\n      variables: \"ansible_host: {{ hostvars[item]['ansible_host'] }}\"\n      description: \"{{ item }} from your static inventory file\"\n      inventory: \"{{ inventory_name }}\"\n      tower_host: \"{{ tower_host_ip }}\"\n      tower_username: \"{{ tower_host_user }}\"\n      tower_password: \"{{ tower_host_password }}\"\n      state: present\n    with_inventory_hostnames:\n    - web\n    - control\n"
  },
  {
    "path": "tools/lightbulb-from-tower/README.md",
    "content": "# Controlling lightbulb from within Tower\n\nWhile this guide expects that the lightbulb environment is provisioned from the command line, it is actually possible to run the entire environment from within Tower. To do so, the following steps are required.\n\n## Prepare Ansible environment on a Tower\n\nLightbulb requires multiple libraries to be present. While `boto` and `boto3` are shipped as part of Tower, `passlib` is necessary. So it needs to be installed in the [Tower Ansible environment](http://docs.ansible.com/ansible-tower/latest/html/upgrade-migration-guide/virtualenv.html).\n\n```bash\n$ ssh tower.example.com\n$ sudo -i\n# . /var/lib/awx/venv/ansible/bin/activate\n(ansible) # pip install passlib\n```\n\n## Add credentials and project to Ansible Tower\n\n1. Create [AWS credentials](http://docs.ansible.com/ansible-tower/latest/html/userguide/credentials.html#amazon-web-services): In Tower, go to \"SETTINGS\", \"CREDENTIALS\", click on \"+ADD\", enter a name, pick the \"CREDENTIAL TYPE\" \"Amazon Web Services\" and fill in the necessary details.\n1. Create [machines credentials](http://docs.ansible.com/ansible-tower/latest/html/userguide/credentials.html#machine) containing your AWS SSH key.\n1. Add a [new project](http://docs.ansible.com/ansible-tower/latest/html/userguide/projects.html#add-a-new-project), the Lightbulb Github repository, to Tower: Create a new Tower project, and add [lightbulb]() as source repository.\n\n![Amazon web Services credentials](aws-credentials.png)\n\n## Create Provision Job Template in Ansible Tower\n\n1. Create a new [job template](http://docs.ansible.com/ansible-tower/latest/html/userguide/job_templates.html) with the Git repo as project.\n1. Use an [inventory](http://docs.ansible.com/ansible-tower/latest/html/userguide/inventories.html) only containing localhost.\n1. As credentials, use the Amazon Web Services and also the corresponding machines credentials created above.\n1. The playbook needs to be `tools/aws_lab_setup/provision_lab.yml`.\n1. In the \"EXTRA VARIABLES\" field, provide the variables necessary for the AWS provider, like list of users, `ec2_name_prefix`, and so on.\n\n![Provision Job Template](Job-Template.png)\n\n## Create Teardown Job Template in Ansible Tower\n\n1. Copy the just created job template.\n1. Change the playbook to `tools/aws_lab_setup/teardown_lab.yml`.\n\nThat's already it. The proper function is ensured via the variables.\n"
  },
  {
    "path": "workshops/README.md",
    "content": "# Ansible Workshops\n\nThe workshops are a collection of assignments for learning how to automate with Ansible. The workshops are introduced by the decks and are assigned to the students during the presentations.\n\nThe workshops are set up as exercises to be done by the participants, and are most suitable for smaller audiences.\n\n## Available Workshops\n\nCurrently there are two workshops available:\n\n* [Ansible Engine](ansible_engine/README.md)\n* [Ansible Tower](ansible_tower/README.md)\n\n## Additional information\n\nAdditional information about Ansible, how to get started and where to proceed after this workshop, have a look at the following sources:\n\n* [Ansible Getting Started](http://docs.ansible.com/ansible/latest/intro_getting_started.html)\n"
  },
  {
    "path": "workshops/ansible_engine/README.md",
    "content": "# Ansible Workshops\n\nThis workshop is part of lightbulb, and is a collection of assignments for learning how to automate with Ansible. The workshops are introduced by the decks and are assigned to the students during the presentations.\n\nThe focus of this workshop is on the set up and usage of Ansible Engine.\n\n## Ansible Engine Exercises\n\n* [Ansible installation](ansible_install)\n* [Ad-Hoc commands](adhoc_commands)\n* [Simple playbook](simple_playbook)\n* [Basic playbook](basic_playbook)\n* [Roles](roles)\n\n## Additional information\n\nAdditional information about Ansible, how to get started and where to proceed after this workshop, have a look at the following sources:\n\n* [Ansible Getting Started](http://docs.ansible.com/ansible/latest/intro_getting_started.html)\n"
  },
  {
    "path": "workshops/ansible_engine/adhoc_commands/README.md",
    "content": "# Workshop: Ad-Hoc Commands\n\n## Topics Covered\n\n* Ansible Modules\n* Facts\n* Inventory and Groups\n* `ansible` command-line options: `-i -m -a -b --limit`\n\n## What You Will Learn\n\n* How to test your Ansible configuration and connectivity\n* How to get and display Ansible facts\n* How to install a package\n\n## The Assignment\n\nPerform the following operations using ad-hoc commands:\n\n1. Test that Ansible is setup correctly to communicate with all hosts in your inventory using the `ping` module.\n1. Fetch and display to STDOUT Ansible facts using the `setup` module.\n1. Setup and enable the EPEL package repository on the hosts in the \"web\" group using the yum module.\n  * CentOS systems use the latest `epel-release` package\n  * RHEL systems should use [https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm](https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm)\n\n### Extra Credit\n\n1. Fetch and display only the \"virtual\" subset of facts for each host.\n1. Fetch and display the value of fully qualified domain name (FQDN) of each host from their Ansible facts.\n1. Display the uptime of all hosts using the `command` module.\n1. Ping all hosts **except** the 'control' host using the `--limit` option\n\n## Reference\n\n* `ansible --help`\n* [ping module](http://docs.ansible.com/ansible/ping_module.html)\n* [setup module](http://docs.ansible.com/ansible/setup_module.html)\n* [yum module](http://docs.ansible.com/ansible/yum_module.html)\n* [command module](http://docs.ansible.com/ansible/command_module.html)\n* [Introduction To Ad-Hoc Commands](http://docs.ansible.com/ansible/intro_adhoc.html)\n\n---\n\n[Click Here to return to the Ansible Lightbulb - Ansible Engine Workshop](../README.md)\n"
  },
  {
    "path": "workshops/ansible_engine/ansible_install/README.md",
    "content": "# Workshop: Installing Ansible\n\n## Topics Covered\n\n* Installing Ansible using pip or yum\n* Configuration file basics\n\n## What You Will Learn\n\n* How easy it is to install and configure Ansible for yourself.\n\n## The Assignment\n\n1. Use pip to install the `ansible` package and its dependencies to you control machine.\n1. Display the Ansible version and man page to STDOUT.\n1. In `~/.ansible.cfg` file (create the file if it doesn't exist already) do the following:\n  * Create a new directory `~/.ansible/retry-files` and set `retry_files_save_path` to it.\n  * Set the Ansible system `forks` to 10\n\n## Reference\n\n* [Ansible Installation](http://docs.ansible.com/ansible/intro_installation.html)\n* [Ansible Configuration File](http://docs.ansible.com/ansible/intro_configuration.html)\n\n---\n\n[Click Here to return to the Ansible Lightbulb - Ansible Engine Workshop](../README.md)\n"
  },
  {
    "path": "workshops/ansible_engine/basic_playbook/README.md",
    "content": "# Workshop: Basic Playbook\n\n## Topics Covered\n\n* Using `ansible-playbook`\n* YAML syntax basics\n* Basic Ansible playbook structure\n* Tasks and modules\n* Handlers\n* Variables\n* Loops\n\n## What You Will Learn\n\n* How to use `ansible-playbook`\n* The basics of YAML syntax and Ansible playbook structure\n* How to deploy and configure an application onto a group of hosts\n\n## Before You Begin\n\nIf you're not familiar with the structure and authoring YAML files take a moment to read thru the Ansible [YAML Syntax](http://docs.ansible.com/ansible/YAMLSyntax.html) guide.\n\n### NOTE\n\nYou will need to assure each host in \"web\" group has setup the EPEL repository to find and install the nginx package with yum.\n\n## The Assignment\n\nCreate an Ansible playbook that will assure nginx is present, configured and running on all hosts in the \"web\" group:\n\n1. Has variables for `nginx_test_message` and `nginx_webserver_port`.\n1. Assures that the following yum packages are present on the each web host:\n  * nginx\n  * python-pip\n  * python-devel\n  * gcc\n1. Assure that the uwsgi pip package is present on each host.\n1. Generate a host-specific home page with the value of `nginx_test_message` for each host using the provided `index.html.j2` template.\n1. Generate a configuration with the value of `nginx_webserver_port` for each host using the provided `nginx.conf.j2` template.\n1. Assure that nginx is running on each host.\n1. The playbook should restart nginx if the homepage or configuration file is altered.\n\nWhile developing the playbook use the `--syntax-check` to check your work and debug problems. Run your playbook in verbose mode using the `-v` switch to get more information on what Ansible is doing. Try `-vv` and `-vvv` for added verbosity. Also consider running `--check` to do a dry-run as you are developing.\n\n### Extra Credit\n\n1. Add a smoke test to your playbook using the `uri` module that test nginx is serving the sample home page.\n1. Create a separate playbook that stops and removes nginx along with its configuration file and home page.\n\n## Resources\n\n* [YAML Syntax](http://docs.ansible.com/ansible/YAMLSyntax.html)\n* [Intro to Ansible Playbooks](http://docs.ansible.com/ansible/playbooks_intro.html)\n  * [Handlers](http://docs.ansible.com/ansible/playbooks_intro.html#handlers-running-operations-on-change)\n  * [Variables](http://docs.ansible.com/ansible/playbooks_variables.html)\n  * [Loops](http://docs.ansible.com/ansible/playbooks_loops.html)\n* [yum module](http://docs.ansible.com/ansible/yum_module.html)\n* [pip module](http://docs.ansible.com/ansible/pip_module.html)\n* [template module](http://docs.ansible.com/ansible/template_module.html)\n* [service module](http://docs.ansible.com/ansible/service_module.html)\n* [uri module](http://docs.ansible.com/ansible/uri_module.html)\n* [file module](http://docs.ansible.com/ansible/file_module.html)\n\n---\n\n[Click Here to return to the Ansible Lightbulb - Ansible Engine Workshop](../README.md)\n"
  },
  {
    "path": "workshops/ansible_engine/basic_playbook/resources/index.html.j2",
    "content": "<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>Ansible: Automation for Everyone</title>\n  <link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>\n  <style>\nbody {\n    font-family: 'Open Sans', sans-serif;\n    text-align: center;\n}\n.container {\n    position: absolute;\n    top: 50%;\n    left: 50%;\n    -moz-transform: translateX(-50%) translateY(-50%);\n    -webkit-transform: translateX(-50%) translateY(-50%);\n    transform: translateX(-50%) translateY(-50%);\n    font-size: 200%;\n}\nfooter {\n    width: 100%;\n    bottom: 0;\n    position: fixed;\n    font-size: 75%;\n}\nimg {\n    margin: 0 auto;\n}\n  </style>\n</head>\n<body>\n<div class=\"container\">\n    <img src=\"https://www.ansible.com/hubfs/2016_Images/Assets/Ansible-Tower-Logotype-Small-RGB-FullBlack.png\"/>\n    <p>{{ nginx_test_message }}</p>\n</div>\n<footer>{{ inventory_hostname }}<br />Ansible by Red Hat</footer>\n</body>\n</html>\n"
  },
  {
    "path": "workshops/ansible_engine/basic_playbook/resources/nginx.conf.j2",
    "content": "# Based on nginx version: nginx/1.10.1\n# For more information on configuration, see:\n#   * Official English Documentation: http://nginx.org/en/docs/\n#   * Official Russian Documentation: http://nginx.org/ru/docs/\n\nuser nginx;\nworker_processes auto;\nerror_log /var/log/nginx/error.log;\npid /run/nginx.pid;\n\n# Load dynamic modules. See /usr/share/nginx/README.dynamic.\ninclude /usr/share/nginx/modules/*.conf;\n\nevents {\n    worker_connections 1024;\n}\n\nhttp {\n    log_format  main  '$remote_addr - $remote_user [$time_local] \"$request\" '\n                      '$status $body_bytes_sent \"$http_referer\" '\n                      '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\n    access_log  /var/log/nginx/access.log  main;\n\n    sendfile            on;\n    tcp_nopush          on;\n    tcp_nodelay         on;\n    keepalive_timeout   115;\n    types_hash_max_size 2048;\n\n    include             /etc/nginx/mime.types;\n    default_type        application/octet-stream;\n\n    # Load modular configuration files from the /etc/nginx/conf.d directory.\n    # See http://nginx.org/en/docs/ngx_core_module.html#include\n    # for more information.\n    include /etc/nginx/conf.d/*.conf;\n\n    server {\n        listen       {{ nginx_webserver_port }} default_server;\n        listen       [::]:{{ nginx_webserver_port }} default_server;\n        server_name  _;\n        root         /usr/share/nginx/html;\n\n        # Load configuration files for the default server block.\n        include /etc/nginx/default.d/*.conf;\n\n        location / {\n        }\n\n        error_page 404 /404.html;\n            location = /40x.html {\n        }\n\n        error_page 500 502 503 504 /50x.html;\n            location = /50x.html {\n        }\n    }\n\n# Settings for a TLS enabled server.\n#\n#    server {\n#        listen       443 ssl http2 default_server;\n#        listen       [::]:443 ssl http2 default_server;\n#        server_name  _;\n#        root         /usr/share/nginx/html;\n#\n#        ssl_certificate \"/etc/pki/nginx/server.crt\";\n#        ssl_certificate_key \"/etc/pki/nginx/private/server.key\";\n#        ssl_session_cache shared:SSL:1m;\n#        ssl_session_timeout  10m;\n#        ssl_ciphers HIGH:!aNULL:!MD5;\n#        ssl_prefer_server_ciphers on;\n#\n#        # Load configuration files for the default server block.\n#        include /etc/nginx/default.d/*.conf;\n#\n#        location / {\n#        }\n#\n#        error_page 404 /404.html;\n#            location = /40x.html {\n#        }\n#\n#        error_page 500 502 503 504 /50x.html;\n#            location = /50x.html {\n#        }\n#    }\n\n}\n\n"
  },
  {
    "path": "workshops/ansible_engine/roles/README.md",
    "content": "# Workshop: Roles\n\n## Topics Covered\n\n* Roles\n\n## What You Will Learn\n\n* How Ansible roles are developed and structured\n* How to use a role in a playbook\n\n## The Assignment\n\nYour assignment is simple: refactor the Ansible playbook you've been developing into a role called \"nginx-simple\".\n\nThis assignment should result in a drop in replacement that is portable and more modular. It does not add any new tasks or functionality.\n\n1. Initialize your role with `ansible-galaxy init` in a new subdirectory `roles/`.\n1. Refactor your existing basic playbook and associated resources into your role.\n1. Create a new playbook that uses the role still targeting the \"web\" group.\n1. Remove any empty files and directories from your role.\n\n### Extra Credit\n\n1. Refactor and merge the Nginx remove and uninstall playbook from the Basic Playbook Extra Credit assignments into your \"nginx-simple\" role. Create a separate playbook to execute that function of the role.\n\n## Resources\n\n* [Ansible Roles](http://docs.ansible.com/ansible/playbooks_roles.html#roles)\n* [Create Roles (with ansible-galaxy)](http://docs.ansible.com/ansible/galaxy.html#create-roles)\n* [inculde_role](http://docs.ansible.com/ansible/include_role_module.html)\n\n---\n\n[Click Here to return to the Ansible Lightbulb - Ansible Engine Workshop](../README.md)\n"
  },
  {
    "path": "workshops/ansible_engine/simple_playbook/README.md",
    "content": "# Workshop: Simple Playbook\n\n## Topics Covered\n\n* Using `ansible-playbook`\n* YAML syntax basics\n* Basic Ansible playbook structure\n* Tasks and modules\n\n## What You Will Learn\n\n* How to use `ansible-playbook`\n* The basics of YAML syntax and Ansible playbook structure\n\n## Before You Begin\n\nIf you're not familiar with the structure and authoring YAML files take a moment to read thru the Ansible [YAML Syntax](http://docs.ansible.com/ansible/YAMLSyntax.html) guide.\n\n### NOTE\n\nYou will need to assure each host in \"web\" group has setup the EPEL repository to find and install the nginx package with yum.\n\n## The Assignment\n\nCreate an Ansible playbook that targets members of the \"web\" group has the following state:\n\n1. The nginx package is present using yum\n1. Has the homepage that is provided in `resources/`.\n1. Nginx is started on each host.\n\nWhile developing the playbook use the `--syntax-check` to check your work and debug problems. Run your playbook in verbose mode using the `-v` switch to get more information on what Ansible is doing. Try `-vv` and `-vvv` for added verbosity. Also consider running `--check` to do a dry-run as you are developing.\n\n## Resources\n\n* [YAML Syntax](http://docs.ansible.com/ansible/YAMLSyntax.html)\n* [Intro to Ansible Playbooks](http://docs.ansible.com/ansible/playbooks_intro.html)\n* [yum module](http://docs.ansible.com/ansible/yum_module.html)\n* [file module](http://docs.ansible.com/ansible/file_module.html)\n* [service module](http://docs.ansible.com/ansible/service_module.html)\n\n---\n\n[Click Here to return to the Ansible Lightbulb - Ansible Engine Workshop](../README.md)\n"
  },
  {
    "path": "workshops/ansible_engine/simple_playbook/resources/index.html",
    "content": "<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>Ansible: Automation for Everyone</title>\n  <link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>\n  <style>\nbody {\n    font-family: 'Open Sans', sans-serif;\n    text-align: center;\n}\n.container {\n    position: absolute;\n    top: 50%;\n    left: 50%;\n    -moz-transform: translateX(-50%) translateY(-50%);\n    -webkit-transform: translateX(-50%) translateY(-50%);\n    transform: translateX(-50%) translateY(-50%);\n    font-size: 200%;\n}\nfooter {\n    width: 100%;\n    bottom: 0;\n    position: fixed;\n    font-size: 75%;\n}\nimg {\n    margin: 0 auto;\n}\n  </style>\n</head>\n<body>\n<div class=\"container\">\n    <img src=\"https://www.ansible.com/hubfs/2016_Images/Assets/Ansible-Tower-Logotype-Small-RGB-FullBlack.png\"/>\n</div>\n<footer>Ansible by Red Hat</footer>\n</body>\n</html>\n"
  },
  {
    "path": "workshops/ansible_tower/README.md",
    "content": "# Ansible Workshops\n\nThis workshop is part of lightbulb, and is a collection of assignments for learning how to automate with Ansible. The workshops are introduced by the decks and are assigned to the students during the presentations.\n\nThe focus of this workshop is on the set up and usage of Ansible Tower.\n\n## Ansible Tower Exercises\n\n* [Tower installation](tower_install)\n* [Tower basic setup](tower_basic_setup)\n\n## Additional information\n\n* [Ansible Tower User Guide](http://docs.ansible.com/ansible-tower/latest/html/userguide/index.html)\n* [Ansible Tower Administration Guide](http://docs.ansible.com/ansible-tower/latest/html/administration/index.html)\n"
  },
  {
    "path": "workshops/ansible_tower/tower_basic_setup/README.md",
    "content": "# Workshop: Ansible Tower Basic Setup\n\n## Topics Covered\n\n* Credentials\n* Inventory\n* Users\n* Roles & Permissions\n* Projects\n* Job Templates\n* Running a Job (Playbook)\n\n## What You Will Learn\n\n* Setting up a new instance of Tower with all the parts needed to run an existing Ansible playbook.\n\n## Requirements\n\n* A running instance of Ansible Tower with sufficient permissions to create credentials, inventory sources, projects etc.\n\n## Before You Begin\n\nBefore doing this assignment you will need to perform a task you typical won't have to do when setting up Ansible Tower: manually enter your inventory. Commonly, Ansible Tower will be setup with one or more dynamic inventory sources such as AWS EC2 or vSphere or internal CMDB as a source of truth. Given the size and static nature of the Lightbulb lab environment, taking the time to setup and configure dynamic inventory is unnecessary.\n\nTo make this process a bit easier, we now include a playbook to automatically import your inventory into tower to reduce the configuration needed. See the [Inventory Import README](../../tools/inventory_import/README.md) for additional details.\n\nIf you choose to run this playbook and its next steps, you can skip items 1 & 3 in the below assignment.\n\n## The Assignment\n\nNOTE: Create all new entities under the \"Default\" organization Ansible Tower create during setup unless noted otherwise.\n\n1. Enter your lab inventory's groups and hosts into Tower. (See \"Before You Begin\" above.)\n1. Create a machine credential called \"Ansible Lab Machine\" with the username and password for the hosts in your lab.\n1. Create an inventory source called \"Ansible Lightbulb Lab\" and create the groups and hosts in your static inventory file.\n1. Create a project called \"Ansible Lightbulb Examples Project\" with a SCM type of Git and URL of [https://github.com/ansible/lightbulb](https://github.com/ansible/lightbulb). You should also enable \"Update on Launch\".\n1. Create a Job Template called \"Nginx Role Example\" with the machine credential, inventory and project you created in the previous steps. Select \"examples/nginx-role/site.yml\" as the playbook.\n1. Execute the job template. Check that it runs without errors and the web servers are serving the home page.\n1. Add an extra variables of `nginx_test_message` with a string like \"Hello World\" then run the \"Nginx Role Example\" job template again. Again, check that it executes without errors and the web servers are serving the home page with the new test message.\n\n### Extra Credit\n\nSetup a self-service user simulation with the playbook\n\n* Create a \"Normal\" user with the name \"someuser\"\n* Assign the new user \"Execute\" permissions on the \"Nginx Role Example\" job template\n* Create a user survey on the job template that enables users to update the test message on the home page\n\n---\n\n[Click Here to return to the Ansible Lightbulb - Ansible Tower Workshop](../README.md)\n"
  },
  {
    "path": "workshops/ansible_tower/tower_install/README.md",
    "content": "# Workshop: Installing Tower\n\n## Topics Covered\n\n* Installing Ansible Tower\n* Importing a License File\n\n## What You Will Learn\n\n* The installation process for a standalone instance of Ansible Tower using the bundled installer.\n\n## The Assignment\n\nInstall Ansible Tower on your controller machine using the [latest installer](http://releases.ansible.com/ansible-tower/setup/ansible-tower-setup-latest.tar.gz) and upload your license file.\n\n1. Follow the [quick installation instructions](http://docs.ansible.com/ansible-tower/latest/html/quickinstall/index.html) to configure and run the single integrated installation of Ansible Tower on the control machine.\n1. Sign into the Ansible Tower UI and upload the license file to enable the instance when prompted. Request a trial license if one hasn't been provided already. (Either trial license type will suffice for these lab assignments.)\n\n## Extra Credit\n\n* After importing the license file, access the ping API endpoint using a browser.\n\n## Reference\n\n* [Ansible Tower Software Releases Archive](http://releases.ansible.com/ansible-tower/)\n* [Ansible Tower Quick Installation Guide](http://docs.ansible.com/ansible-tower/latest/html/quickinstall/index.html)\n* [Import a License](http://docs.ansible.com/ansible-tower/latest/html/userguide/import_license.html)\n* [Ansible Tower Ping API Endpoint](http://docs.ansible.com/ansible-tower/3.0.3/html/towerapi/ping.html)\n\n---\n\n[Click Here to return to the Ansible Lightbulb - Ansible Tower Workshop](../README.md)\n"
  }
]