DEV Community

Juell
Juell

Posted on • Updated on

Ansible: Enders Game

Alt Text

Configuration Management

The purpose of configuration management is to maintain systems in a desired state. It aims to eliminate configuration drift as much as possible.
Configuration drift occurs when ad-hoc configuration changes and updates result in a mismatched development, test, and deployment environments. This can result in issues at deployment, security vulnerabilities, and risks when developing applications and services that need to meet strict regulatory compliance standards.
Configuration management makes it possible to scale infrastructure without worrying about increasing staff or workload. It ensures consistency across all infrastructure, improves automation and mitigates human error.

Ansible

Ansible is a very powerful and popular configuration management tool. Here are some quick facts that will interest you about Ansible:

  • The name “ansible” originally refers to a category of fictional technology capable of faster-than-light communication. It can send and receive messages to and from a corresponding device over any distance or obstacle whatsoever with no delay.
  • Its name is used in sci-fi movies (e.g Enders Game)and novels (Ursula).
  • In the Enders Game, the story depends on the ansible to centrally control a huge, complex fleet of distant ships. This was what inspired Ansible automation software founders to choose the term as the name for their product.
  • Developed by Michael DeHaan, the creator of Cobbler and Func.

Ansible is an agentless push-based configuration tool that
functions by connecting via SSH to its managed nodes. It doesn’t need
a special agent on the node and operates by pushing modules to the
clients. The modules are then executed locally on the node, and the
output is pushed back to the controller or host.

Drift is the process of making changes on a host that makes it differ from the synced version.

Nodes for Demo:

  • Spin-up 3 Ubuntu Linux VMs on portal.azure.com
  • Make user=agent, password=password@101.
  • Copy out the IP addresses of all 3 VMs

Ssh into any one that would serve as our control node, and proceed as follows.

Ansible Installation :

Step 1: Perform an update to the packages

   sudo apt update
Enter fullscreen mode Exit fullscreen mode

Step 2: Install the software-properties-common package

 sudo apt install software-properties-common
Enter fullscreen mode Exit fullscreen mode

Step 3: Add ansible personal package archive

sudo apt-add-repository ppa:ansible/ansible
Enter fullscreen mode Exit fullscreen mode

Step 4: Install ansible

 sudo apt install ansible
Enter fullscreen mode Exit fullscreen mode

To test your installation, do:

ansible --version
Enter fullscreen mode Exit fullscreen mode

Ansible Ad-hoc Commands:

This is one of the simplest ways Ansible can be used to issue some commands on a server or a bunch of servers. Ad-hoc commands are not version controlled but represent a fast way to interact with the desired servers.

ansible -m ping localhost 
Enter fullscreen mode Exit fullscreen mode

-m ==> module
Ping ==> module name
Localhost ==> target node

ansible -m copy -a 'src=./file1.txt dest=~/file.txt' localhost 
Enter fullscreen mode Exit fullscreen mode

To see if some changes would be implemented, do a dry run. Add ‘--check’ to the command. To see the changes that would be implemented, add ‘--diff’. ‘ diff flag alone implements the changes after pointing them out to you.

Idempotency:

This is an IaC property that ensures a defined infrastructure
deployment sets the target environment to the desired state, every
time it is run, regardless of its starting state. The IaC provider
will decide what needs to be stood-up, torn down or reconfigure to
match your described target state.

Ansible Playbook

The ansible playbook is a more readable, versionable way of executing tasks using ansible. In comparison with ad-hoc commands, playbooks are used in complex scenarios, and they offer increased flexibility and capability. Playbooks use YAML format, so there is not much syntax needed, but its indentation rules are strict. Ansible playbooks tend to be more of a configuration language than a programming language.

Like its name, a playbook is a collection of plays. A play is a set of
orchestration instructions.

    ---
      #ansible -m copy -a “src=./file1.txt dest=~/file1.txt” localhost
    - name: Play name
    hosts: localhost
      tasks:
      - name: Copy a file
        copy:
          src: ./file1.txt
          dest: ~/file1.txt
Enter fullscreen mode Exit fullscreen mode
  • An ansible playbook starts with the 3 dashes.
  • "#" indicates a comment line.
  • The - name indicates a non-executed name that serves a descriptive purpose.
  • Hosts specifies the target node device, or groups.
  • A task is made up of an action and its args.
  • Under tasks you can specify your modules and its parameter.

If the action you desire requires root/sudo, use the ‘Become’ keyword
for privilege escalation.

Ansible Inventory

Ansible uses the Inventory to identify its managed hosts. The default host file is at:

/etc/ansible/hosts 
Enter fullscreen mode Exit fullscreen mode

Here you define your nodes, node alias, node groups and variables. Create a group with the ip addresses of the VMs you created.

[master]
host1 ansible_host=192.168.100.2 ansible_user=agent ansible_password=password@101
[agents]
node1 ansible_host=192.168.100.3 ansible_user=agent ansible_password=password@101
node2 ansible_host=192.168.100.4 ansible_user=agent ansible_password=password@101
Enter fullscreen mode Exit fullscreen mode

The ‘ansible-inventory’ command is used for working with the managed nodes.
To see a full list of all our nodes type:

ansible-inventory --list
Enter fullscreen mode Exit fullscreen mode

For a more graphical layout use:

ansible-inventory --graph
Enter fullscreen mode Exit fullscreen mode
  • @localhost refers to the current/control node
  • @all refers to all the nodes
  • @group_name targets all nodes under a group.

To simplify our host file, define all variables in the [all:vars] group:

[all:vars]
ansible_user=agent
ansible_password=password@101

[master]
host1 ansible_host=192.168.100.2 

[agents]
node1 ansible_host=192.168.100.3 
node2 ansible_host=192.168.100.4 
Enter fullscreen mode Exit fullscreen mode

NOTE: Since Ansible is going to be making an SSH connection with a password to these machines, we need to install “sshpass”.

sudo apt install sshpass -y
Enter fullscreen mode Exit fullscreen mode

Let us try to test our hosts with the ping command:

ansible -m ping all
Enter fullscreen mode Exit fullscreen mode

Gives us an error. We need to modify some ansible configuration.

Ansible CFG

The Ansible configuration file is exactly what you expect it to be. Its located at:

/etc/ansible/ansible.cfg
Enter fullscreen mode Exit fullscreen mode

We’ll need to open it and modify a setting. Open the file and uncomment: host_key_checking = false
Or enter it.
Now you can run your ping command:

ansible -m ping all
Enter fullscreen mode Exit fullscreen mode

Writing a Playbook

Create a file:

touch playbook.yml
Enter fullscreen mode Exit fullscreen mode

Open it and add the following to it:

    ---
    - name: play one - install vim from custom inventory
      hosts: agents
      become: true
      tasks:
      - name: apt 
        apt:
          name: vim
          state: latest

      - name: zsh install
        apt:
          name: zsh
          state: latest

      - name: vagrant install
        apt:
          name: vagrant
          state: latest

      - name: nodejs install
        apt:
          name: nodejs
          state: latest

    - name: play two server config
      become: true
      hosts: master
      tasks:
        - name: ensure Nginx is at latest
          apt:
            name: nginx
            state: latest
        - name: start server
          service:
            name: nginx
            state: started
Enter fullscreen mode Exit fullscreen mode

Save it.

Executing a Playbook

To execute the play we just wrote, we use the ‘ansible-playbook’ command. Enter:

ansible-playbook playbook.yml -v
Enter fullscreen mode Exit fullscreen mode

The -v is for verbosity. The more the v’s the more details that would be output.
We can also execute a playbook from a custom inventory file.
Create a custom inventory file.

touch inventory
Enter fullscreen mode Exit fullscreen mode

Add nodes to it.

Ping test it with the following:

ansible -I inventory -m ping all
Enter fullscreen mode Exit fullscreen mode

Run a playbook off it with:

ansible-playbook -I inventory playbook.yml
Enter fullscreen mode Exit fullscreen mode


Shut-down our VMs

Now, as a final demo, let's shut down our VMs to avoid extra charges. Since we know ansible can do all sorts of things, let's use it for this demo.
Can you write the YAML script for this? Try it out first. It's a simple one.

    ---
    - name: shut down all my agent VMs
      hosts: all
      become: true
      tasks:
        - name: run shutdown command
          command: shutdown
Enter fullscreen mode Exit fullscreen mode

Save and execute this:

ansible-playbook shutdownVMs.yml -vv
Enter fullscreen mode Exit fullscreen mode

Ansible on Azure Cloudshell

Azure Cloudshell has all we need to execute both Ansible Ad-hoc command and playbooks. We shall next create a VM with its underlying resources from the Azure Cloudshell.

Visit HTTP://portal.azure.com and open the Cloudshell. Choose Bash and enter the following to open its embedded VS Code editor:

code .
Enter fullscreen mode Exit fullscreen mode

inside the editor, create a azvmplaybook.yml and paste the following inside:


- name: Create Azure VM
  hosts: localhost
  connection: local
  tasks:
  - name: Create resource group
    azure_rm_resourcegroup:
      name: myResourceGroup
      location: eastus


  - name: Create virtual network
    azure_rm_virtualnetwork:
      resource_group: myResourceGroup
      name: myVnet
      address_prefixes: "10.0.0.0/16"


  - name: Add subnet
    azure_rm_subnet:
      resource_group: myResourceGroup
      name: mySubnet
      address_prefix: "10.0.1.0/24"
      virtual_network: myVnet


  - name: Create public IP address
    azure_rm_publicipaddress:
      resource_group: myResourceGroup
      allocation_method: Static
      name: myPublicIP
    register: output_ip_address

  - name: Dump public IP for VM which will be created
    debug:
      msg: "The public IP is {{ output_ip_address.state.ip_address }}."


  - name: Create Network Security Group that allows SSH
    azure_rm_securitygroup:
      resource_group: myResourceGroup
      name: myNetworkSecurityGroup
      rules:
        - name: SSH
          protocol: Tcp
          destination_port_range: 22
          access: Allow
          priority: 1001
          direction: Inbound


  - name: Create virtual network interface card
    azure_rm_networkinterface:
      resource_group: myResourceGroup
      name: myNIC
      virtual_network: myVnet
      subnet: mySubnet
      public_ip_name: myPublicIP
      security_group: myNetworkSecurityGroup


  - name: Create VM
    azure_rm_virtualmachine:
      resource_group: myResourceGroup
      name: myVM
      vm_size: Standard_DS1_v2
      admin_username: agent
      admin_password: password@101
      network_interfaces: myNIC
      image:
        offer: UbuntuServer
        publisher: Canonical
        sku: '18.04-LTS'
        version: latest

Enter fullscreen mode Exit fullscreen mode

To set the ball rolling, execute this on the Cloudshell:

ansible-playbook azvmplaybook.yml
Enter fullscreen mode Exit fullscreen mode

Wait for it...
When it's done, go to your Resource Group to check it out. You can even SSH into your created VM. When done playing with it, we need to clean our tracks. Conveniently, simply deleting our resource group will take all the created resources with it. So let's do just that. Create a rmrgplaybook.yml:

---
- hosts: localhost
  tasks:
    - name: Deleting resource group
      azure_rm_resourcegroup:
        name: myResourceGroup
        state: absent
        force_delete_nonempty: yes

Enter fullscreen mode Exit fullscreen mode

Here we set the force_delete_nonempty flag to yes to enable it to delete all underlying resources.
Now to put it in play:

ansible-playbook rmrgplaybook.yml
Enter fullscreen mode Exit fullscreen mode

Thanks for your time!

Top comments (0)