7 months, 1 week

What is Ansible?





Ansible has become a widely adopted tool for application deployment and infrastructure automation projects since its first release in early 2012. It is primarily intended for IT professionals, who use it for application deployment, updates on workstations and servers, cloud provisioning, configuration management, intra-service orchestration, and nearly anything a systems administrator does on a weekly or daily basis. Ansible doesn't depend on agent software and has no additional security infrastructure, so it's easy to deploy. Its vast ecosystem of pre-build modules allows you to describe the desired state of your infrastructure or application deployment using the YAML Ain't Markup Language (YAML) language. Being agentless, there is no need for a central controller that you send your instructions to for these to be carried out.
In Ansible, there are two categories of computers: the control node and managed nodes. The control node is a computer that runs Ansible. There must be at least one control node, although a backup control node may also exist. A managed node is any device being managed by the control node.
Ansible works by connecting to nodes (clients, servers) on a network, and then sending a small program called an Ansible module to that node. Ansible executes these modules over SSH and removes them when finished. The only requirement for this interaction is that your Ansible control node has login access to the managed nodes. SSH keys are the most common way to provide access, but other forms of authentication are also supported.

An Ansible playbook is a blueprint of automation tasks—which are complex IT actions executed with no need for human involvement. Ansible playbooks are written in human-readable YAML format and executed on a set, group, or classification of hosts, which together make up an Ansible inventory.They are also idempotent, meaning that a playbook can be run on a system at any time without having a negative effect upon it. 

Ansible is currently only supported on Unix or Unix-like systems such as most Linux distributions and Mac OS X.If you use Windows, you can either install Ansible in a Linux virtual machine (VM) or use the Windows Subsystem for Linux (WSL). You can find more information on how to set up Ansible under Windows by following this link to the official documentation: 
https://docs.ansible.com/ansible/latest/user_guide/windows_faq.html#can-ansible-run-on-windows.

 Ansible's primary purpose is to configure three main types of tasks:

Configuration management: This is used to fetch and push configurations on various devices that we call as inventory in Ansible. Based upon the type of inventory, Ansible is capable of pushing specific or full configurations in bulk.

Application deployment: In server scenarios, we often need to bulk deploy some specific applications or patches. Ansible takes care of them as well as bulk uploading patches or applications on the server, installing them, and even configuring the applications of a particular task. Ansible can also take care of customizing settings based upon the devices in the inventory.

Task automation: This is a feature of Ansible that performs a certain written task on a single device or a group of devices. The tasks can be written and Ansible can be configured to run those tasks once, or on a periodic basis.

Some of the key components that make up the Ansible framework are as follows:

Inventory: This is a configuration file where you define the host information that needs to be accessed. The default file created at the time of installation is /etc/ansible/hosts.

Playbook: Playbooks are simply a set of instructions that we create for Ansible to configure, deploy, and manage the nodes declared in the inventory.

Plays: Plays are defined tasks that are performed on a given set of nodes. A playbook consists of one or more plays. Tasks: Tasks are specific configured actions executed in a playbook.

Variables: These are custom defined and can store values based upon execution of tasks.

Roles: Roles define the hierarchy of how the playbooks can be executed. For example, say as a primary role of a web server can have sub tasks/roles to install certain application based upon server types.

You can install Ansible by running python -m pip install --user ansible, or if you use Linux, by using the package manager of your distribution. You may find more information on how to set up Ansible in your specific environment by following this link to the official documentation:

https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installingand-upgrading-ansible-with-pip.

import json
import sys
from collections import namedtuple
from ansible.parsing.dataloader import DataLoader
from ansible.vars.manager import VariableManager
from ansible.inventory.manager import InventoryManager
from ansible.playbook.play import Play
from ansible.executor.playbook_executor import PlaybookExecutor
def ansible_part():
  playbook_path = "checktemplate.yml"
  inventory_path = "hosts"
  Options = namedtuple('Options', ['connection', 'module_path', 'forks','become', 'become_method', 'become_user', 'check', 'diff', 'listhosts',
      'listtasks', 'listtags', 'syntax'])
  loader = DataLoader()
  options = Options(connection='local', module_path='', forks=100,become=None, become_method=None, become_user=None, check=False,
     diff=False, listhosts=False, listtasks=False,listtags=False, syntax=False)
  passwords = dict(vault_pass='secret')
  inventory = InventoryManager(loader=loader, sources=['inventory'])
  variable_manager = VariableManager(loader=loader, inventory=inventory)
  executor = PlaybookExecutor( playbooks=[playbook_path], inventory=inventory,
      variable_manager=variable_manager, loader=loader, options=options, passwords=passwords)
  results = executor.run()
  print results
def main():
   ansible_part()
sys.exit(main())

 

Let us see another example to display the hostname from the inventory by creating a playbook inside the Python script:


import json
from collections import namedtuple
from ansible.parsing.dataloader import DataLoader
from ansible.vars.manager import VariableManager
from ansible.inventory.manager import InventoryManager
from ansible.playbook.play import Play
from ansible.executor.task_queue_manager import TaskQueueManager
from ansible.plugins.callback import CallbackBase
Options = namedtuple('Options', ['connection', 'module_path', 'forks','become', 'become_method', 'become_user', 'check', 'diff'])

loader = DataLoader()
options = Options(connection='local', module_path='', forks=100,become=None, become_method=None, become_user=None, check=False, diff=False)
passwords = dict(vault_pass='secret')

inventory = InventoryManager(loader=loader, sources=['inventory'])
variable_manager = VariableManager(loader=loader, inventory=inventory)

play_source = dict( name = "mypythoncheck", hosts = 'testrouters', gather_facts = 'no',
 tasks = [ dict(action=dict(module='debug',args=dict(msg='{{inventory_hostname}}')))
 ]
 )
play = Play().load(play_source, variable_manager=variable_manager,loader=loader)

task = None
try:
   task = TaskQueueManager(
   inventory=inventory,
   variable_manager=variable_manager,
   loader=loader,
  options=options,
  passwords=passwords,
 stdout_callback='default'
 )
  result = task.run(play)
finally:
  if task is not None:
    task.cleanup()

Setting up the module structure
Functionality in Ansible is provided and can be extended in two different ways: by writing either a module or a plugin. Ansible defines a module as "reusable, standalone scripts that can be used by the Ansible API, the ansible command, or the ansible-playbook command. Modules provide a defined interface. Each module accepts arguments and returns information…"
(https://docs.ansible.com/ansible/latest/dev_guide/developing_locally.html#modules-and-plugins-what-is-thedifference).
Plugins are pieces of code that augment Ansible’s core functionality. Ansible uses a plugin architecture to enable a rich, flexible and expandable feature set. "

( https://docs.ansible.com/ansible/latest/plugins/plugins.html#plugins-lookup )

Follow these steps to create your first Ansible module:
1. In your console, create a new directory that will contain the Ansible modules:
mkdir custom_ansible_modules
cd custom_ansible_modules
2. We have to make Ansible aware of this new directory by adding it to the Ansible path. This path is controlled by the ANSIBLE_LIBRARY environment 
variable:
export ANSIBLE_LIBRARY=$ANSIBLE_LIBRARY:`pwd`
3. Verify that the path has been altered successfully by printing the ANSIBLE_LIBRARY path.  
echo $ANSIBLE_LIBRARY
4. With our directory added to the path, we can create our first module by creating a first_module.py file:
touch first_module.py
5. With the structure created and Ansible made aware of our new module directory, you can open the previously created first_module.py file in your code editor to get started writing your first module.First, we need to import the required libraries from Ansible:
from ansible.module_utils.basic import AnsibleModule
6. Next, we want to define our module function in which we are going to specify the module itself:
def run_module():
7. Then, we'll define our module arguments as an empty dictionary:
args = {}
8. Next, we can define our result dictionary. The result is the data that is passed back from your module both to the user and to any subsequent modules that want to consume the information. 
result = {
 "changed": False,
 "message": "Hello from my ansible module"
}
9. Finally, we can define our Ansible module itself:
module = AnsibleModule(argument_spec=args, supports_check_mode=False)
10. Since we only want to pass the static information specified in our result dictionary back, we can exit the module and return the content of the results as JavaScript Object Notation (JSON):
module.exit_json(**result)
11. Finally, we need to specify that our previously defined run_module() method should be called when the file itself is being run: 
if __name__ == "__main__":
 run_module()
12. With our first module written and stored in the previously created directory, we can go back to the terminal and run the newly created module:
ansible localhost -m first_module.py

Documenting your module
One great feature of Ansible is its built-in documentation.
1. If you have not already done so, set up the directory structure needed for your Ansible modules.
mkdir custom_ansible_modules
cd custom_ansible_modules
export ANSIBLE_LIBRARY=$ANSIBLE_LIBRARY:`pwd`
touch documented_module.py
2. With your directory structure set up, open the documented_module.py file in your code editor.
from ansible.module_utils.basic import AnsibleModule
3. We first specify the basic documentation information in a variable called DOCUMENTATION:
DOCUMENTATION = r'''
---
module: documented_module
short_description: A small documented module.
version_added: "1.0.0"
description: This is a longer description of our documented module.
author:
 - Name (GitHub handle)
'''
4. With our basic information defined, we can also specify an example of how our basic module can be used in an Ansible playbook. 
EXAMPLES = r'''
- name: Testing my documented task documented_module
'''
5. We can also specify the information that is being returned by our module by specifying a RETURN variable:
RETURN = r'''
message:
 description: A small welcome message returned 
by our module
 type: str
 returned: always
 sample: 'Hello from my first ansible module'
'''
6. With our documentation defined, we can go ahead and actually specify the module and its functionality. 
def run_module():
   args = {}
   result = {
 "changed": False,
 "messsage": 'Hello from my first ansible module'
 }
   module = AnsibleModule(
   argument_spec=args,
 supports_check_mode=False
 )
   module.exit_json(**result)
def main():
   run_module()
if __name__ == '__main__':
   main()
7. With our module defined and documented, we can have a look at the docs by running the ansible-doc command:
ansible-doc documented_module

Using Ansible's built-in functionality to do web requests
mkdir custom_ansible_modules
cd custom_ansible_modules
export ANSIBLE_LIBRARY=$ANSIBLE_LIBRARY:`pwd`
touch web_module.py
2. With your directory structure set up, open the web_module.py file in your code editor, and in it, start by importing the required modules from Ansible.
from ansible.module_utils.basic import AnsibleModule
form ansible.module_utils.urls import fetch_url
import json
5. With the documentation finished, we can start specifying the module itself inside our run_module() method:
def run_module():
6. We start by defining two parameters inside of the args dictionary:
 args = {
 "access_token": {
 "required": True,
 "type": str
 },
 "base_url": {
 "required": False,
 "default": "https://api.meraki.com/api/v1",
 "type": str
 }
 }
7. Next, we can create our module:
 module = AnsibleModule(
 argument_spec=args,
 supports_check_mode=False
 )
8. For our web request, we will need headers that contain the authentication information (the access token passed into the module as a parameter). 
 headers = {
 "X-Cisco-Meraki-API-Key":module.params['access_token']
 }
9. We can piece together our request Uniform Resource Locator (URL) and send the actual request:
 url = "{}/organizations".format(module.params['base_url'])
 resp, info = fetch_url(module, url, method="get", headers=headers)
10. We then need to prepare our resulting output:
 result = {
 "changed": False,
 "status": info['status'],
 }
11. In case of a successful request, we want to return all the organizations. 
if info['status'] == 200:
   result["networks"] = json.loads(resp.read())
   module.exit_json(**result)
else:
   result["msg"] = "Unable to call API."
   module.fail_json(**result)
if __name__ == '__main__':
 run_module()


Responses(0)